pandasの文字列を区切り文字や正規表現で複数の列に分割
pandasで文字列要素をもつ列を複数の列に分割する方法を説明する。
以下の文字列メソッドを使う。
str.split()
: 区切り文字で分割str.extract()
: 正規表現で分割
文字列メソッドはpandas.Series
のメソッド。
pandas.Series
またはpandas.DataFrame
の列(= pandas.Series
)に対して適用する。
正規表現による文字列の置換や抽出は以下の記事を参照。
str.split(): 区切り文字で分割
区切り文字(デリミタ: delimiter)で分割するには、文字列メソッドstr.split()
を使う。
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の要素としてリストを格納し処理
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
を例とする。
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<name>...)
を使うと名前がそのまま列名(カラム名)になる。
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()
メソッドで削除すればOK。
なお、str.extract()
では最初のマッチ部分のみ抽出される。すべてのマッチ部分を抽出するにはstr.extractall()
メソッドを使う。以下の記事を参照。