pandasの文字列を区切り文字や正規表現で複数の列に分割

pandasで文字列要素をもつ列を複数の列に分割する方法を説明します。 以下の文字列メソッドを使います。

str.split(): 区切り文字で分割 str.extract(): 正規表現で分割

文字列メソッドはpandas.Seriesのメソッド。 pandas.Seriesまたはpandas.DataFrameの列(= pandas.Series)に対して適用します。 正規表現による文字列の置換や抽出は以下の記事を参照。

str.split(): 区切り文字で分割

区切り文字(デリミタ: delimiter)で分割するには、文字列メソッドstr.split()を使います。

pandas.Series.str.split — pandas 0.21.1 documentation

pandas.Seriesの場合 以下のpandas.Seriesを例とします。

import pandas as pd

s_org = pd.Series(['aaa@xxx.com', 'bbb@yyy.com', 'ccc@zzz.com', 'ddd'], index=['A', 'B', 'C', 'D'])
print(s_org)
print(type(s_org))
# A    aaa@xxx.com
# B    bbb@yyy.com
# C    ccc@zzz.com
# D            ddd
# dtype: object
# <class 'pandas.core.series.Series'>

第一引数に区切り文字を指定します。分割した文字列のリストが要素のpandas.Seriesが返る。

s = s_org.str.split('@')
print(s)
print(type(s))
# A    [aaa, xxx.com]
# B    [bbb, yyy.com]
# C    [ccc, zzz.com]
# D             [ddd]
# dtype: object
# <class 'pandas.core.series.Series'>

複数の列に分割してpandas.DataFrameとして取得するには、引数expand=Trueを指定します。デフォルトはexpand=False。 分割数が少ない行の足りない分の要素はNoneとなります。

df = s_org.str.split('@', expand=True)
print(df)
print(type(df))
#      0        1
# A  aaa  xxx.com
# B  bbb  yyy.com
# C  ccc  zzz.com
# D  ddd     None
# <class 'pandas.core.frame.DataFrame'>

取得したpandas.DataFrameの列名はcolumnsで指定できます。

df.columns = ['local', 'domain']
print(df)
#   local   domain
# A   aaa  xxx.com
# B   bbb  yyy.com
# C   ccc  zzz.com
# D   ddd     None

pandas.DataFrameの場合

pandas.DataFrameの特定の列を複数の列に分割して更新する場合、ちょっと面倒。もっといいやり方があるかもしれない。 先に作成したpandas.DataFrameを例とします。

print(df)
#   local   domain
# A   aaa  xxx.com
# B   bbb  yyy.com
# C   ccc  zzz.com
# D   ddd     None

特定の列にstr.split()を使うと、分割されたpandas.DataFrameが得られる。

print(df['domain'].str.split('.', expand=True))
#       0     1
# A   xxx   com
# B   yyy   com
# C   zzz   com
# D  None  None

これをpd.concat()を使って元のpandas.DataFrameと連結(結合)し、元の列をdrop()メソッドで削除します。

df2 = pd.concat([df, df['domain'].str.split('.', expand=True)], axis=1).drop('domain', axis=1)
print(df2)
#   local     0     1
# A   aaa   xxx   com
# B   bbb   yyy   com
# C   ccc   zzz   com
# D   ddd  None  None

残りの列が少ないのであれば、pd.concat()で連結(結合)するときに必要な列だけ選択してもいい。

df3 = pd.concat([df['local'], df['domain'].str.split('.', expand=True)], axis=1)
print(df3)
#   local     0     1
# A   aaa   xxx   com
# B   bbb   yyy   com
# C   ccc   zzz   com
# D   ddd  None  None

特定の列名を変更するにはrename()メソッドを使います。

df3.rename(columns={0: 'second_LD', 1: 'TLD'}, inplace=True)
print(df3)
#   local second_LD   TLD
# A   aaa       xxx   com
# B   bbb       yyy   com
# C   ccc       zzz   com
# D   ddd      None  None

str.extract(): 正規表現で分割

正規表現のパターンで分割するには文字列メソッドstr.extract()を使います。

pandas.Series.str.extract — pandas 0.21.1 documentation

以下のpandas.Seriesを例とします。

import pandas as pd

s_org = pd.Series(['aaa@xxx.com', 'bbb@yyy.com', 'ccc@zzz.com', 'ddd'], index=['A', 'B', 'C', 'D'])
print(s_org)
# A    aaa@xxx.com
# B    bbb@yyy.com
# C    ccc@zzz.com
# D            ddd
# dtype: object

第一引数に正規表現パターンを指定します。正規表現の() で囲まれたグループ部分にマッチする文字列ごとに分割される。 複数のグループが抽出される場合は引数expandによらずpandas.DataFrameを返す。 マッチしない場合はNaNとなります。

df = s_org.str.extract('(.+)@(.+)\.(.+)', expand=True)
print(df)
#      0    1    2
# A  aaa  xxx  com
# B  bbb  yyy  com
# C  ccc  zzz  com
# D  NaN  NaN  NaN

df = s_org.str.extract('(.+)@(.+)\.(.+)', expand=False)
print(df)
#      0    1    2
# A  aaa  xxx  com
# B  bbb  yyy  com
# C  ccc  zzz  com
# D  NaN  NaN  NaN

グループが一つの場合は引数expand=Trueだとpandas.DataFrame、expand=Falseだとpandas.Seriesを返す。

df_single = s_org.str.extract('(\w+)', expand=True)
print(df_single)
print(type(df_single))
#      0
# A  aaa
# B  bbb
# C  ccc
# D  ddd
# <class 'pandas.core.frame.DataFrame'>

s = s_org.str.extract('(\w+)', expand=False)
print(s)
print(type(s))
# A    aaa
# B    bbb
# C    ccc
# D    ddd
# dtype: object
# <class 'pandas.core.series.Series'>

現在のバージョン0.22.0ではexpand=Falseがデフォルトだが、将来的にはexpand=Trueがデフォルトになるとのこと。 FutureWarning: currently extract(expand=None) means expand=False (return Index/Series/DataFrame) but in a future version of pandas this will be changed to expand=True (return DataFrame)

正規表現パターンに名前付きグループ(?P...)を使うと名前がそのまま列名(カラム名)になる。

df_name = s_org.str.extract('(?P<local>.*)@(?P<second_LD>.*)\.(?P<TLD>.*)', expand=True)
print(df_name)
#   local second_LD  TLD
# A   aaa       xxx  com
# B   bbb       yyy  com
# C   ccc       zzz  com
# D   NaN       NaN  NaN

pandas.DataFrameの特定の列を複数の列に分割して更新する場合は、上述のstr.split()の例を参照のこと。pd.concat()を使って元のpandas.DataFrameと連結(結合)し、元の列をdrop()メソッドで削除すれば問題ありません。 なお、str.extract()では最初のマッチ部分のみ抽出される。すべてのマッチ部分を抽出するにはstr.extractall()メソッドを使います。以下の記事を参照。

シェア

関連カテゴリー

Python pandas 正規表現

pandasで特定の文字列を含む行を抽出(完全一致、部分一致) pandasで欠損値NaNを除外(削除)・置換(穴埋め)・抽出 pandasで要素、行、列に関数を適用するmap, applymap, apply pandasの行・列をランダムサンプリング(抽出)するsample pandasで中央値を取得するmedian pandas.DataFrameの構造とその作成方法 pandas.DataFrame, Seriesの重複した行を抽出・削除 pandas.DataFrame, Seriesを辞書に変換(to_dict) pandasでExcelファイル(xlsx, xls)の書き込み(to_excel) pandasの要素としてリストを格納し処理 pandasでJSON文字列・ファイルを読み込み(read_json) pandasでカテゴリ変数をダミー変数に変換(get_dummies) 『Python Data Science Handbook』(英語の無料オンライン版あり) pandasで欠損値NaNを前後の値から補間するinterpolate pandas.DataFrame, Seriesを連結するconcat

Last Updated: 6/26/2019, 10:34:03 PM