pandasの文字列から正規表現で抽出して新たな列を生成
文字列を要素とするpandas.DataFrame
の列、pandas.Series
から正規表現で特定の文字列を抽出して新たな列を生成する方法を説明する。
以下の文字列メソッドを使う。
str.extract()
: 最初のマッチ部分のみ抽出str.extractall()
: すべてのマッチ部分を抽出
pandas.DataFrame
の列から抽出したい場合はdf['列名']
のように列(= pandas.Series
)を指定してstr.extract()
やstr.extractall()
を呼べばOK。
文字列の位置・長さを指定して抽出する場合はスライスを使うと簡単。以下の記事を参照。
str.extract(): 最初のマッチ部分のみ抽出
以下のpandas.Series
を例とする。
import pandas as pd
s_org = pd.Series(['aaa@xxx.com', 'bbb@yyy.net', 'ccc@zzz.co.jp'], index=['A', 'B', 'C'])
print(s_org)
# A aaa@xxx.com
# B bbb@yyy.net
# C ccc@zzz.co.jp
# dtype: object
正規表現の最初のマッチ部分のみ抽出するにはstr.extract()
メソッドを使う。
str.extract()
の第一引数に正規表現パターンを指定すると()
で囲まれたグループ部分にマッチする文字列が抽出される。
引数expand
をTrue
とするとpandas.DataFrame
、False
とするとpandas.Series
として新たなオブジェクトが生成される。
df_single = s_org.str.extract('(.+)@', expand=True)
print(df_single)
print(type(df_single))
# 0
# A aaa
# B bbb
# C ccc
# <class 'pandas.core.frame.DataFrame'>
s = s_org.str.extract('(.+)@', expand=False)
print(s)
print(type(s))
# A aaa
# B bbb
# C ccc
# dtype: object
# <class 'pandas.core.series.Series'>
バージョン0.22.0
ではexpand=False
がデフォルトだが、将来的にはexpand=True
がデフォルトになるとのこと。バージョンによって結果が変わってしまうのでexpand
は明示的に指定しておいたほうが無難。
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>.+)@', expand=True)
print(df_name)
print(type(df_name))
# local
# A aaa
# B bbb
# C ccc
# <class 'pandas.core.frame.DataFrame'>
()
で囲まれたグループが複数あると、各グループで抽出された部分がそれぞれ列となるpandas.DataFrame
が返る。この場合は引数expand
がTrue
でもFalse
でもpandas.DataFrame
。
デフォルトでは0始まりの連番が列名となり、名前付きグループ(?P<name>...)
を使うとそれが列名となる。
print(s_org.str.extract('(.+)@(.+)'))
# 0 1
# A aaa xxx.com
# B bbb yyy.net
# C ccc zzz.co.jp
print(s_org.str.extract('(?P<local>.+)@(?P<domain>.+)'))
# local domain
# A aaa xxx.com
# B bbb yyy.net
# C ccc zzz.co.jp
なお、この例の場合はstr.split()
メソッドで区切り文字を@
として分割することも可能。文字列の分割については以下の記事を参照。
マッチする部分がない場合はNaN
となる。
print(s_org.str.extract('(a+)', expand=True))
# 0
# A aaa
# B NaN
# C NaN
str.extractall(): すべてのマッチ部分を抽出
以下のpandas.Series
を例とする。
s_org2 = pd.Series(['aaa@xxx.com, iii@xxx.com', 'bbb@yyy.net, jjj@yyy.net', 'ccc@zzz.co.jp'],
index=['A', 'B', 'C'])
print(s_org2)
# A aaa@xxx.com, iii@xxx.com
# B bbb@yyy.net, jjj@yyy.net
# C ccc@zzz.co.jp
# dtype: object
str.extract()
は最初のマッチ部分のみを返すので、以下のような結果となる。
print(s_org2.str.extract('([a-z]+)@([a-z.]+)', expand=True))
# 0 1
# A aaa xxx.com
# B bbb yyy.net
# C ccc zzz.co.jp
すべてのマッチ部分を抽出するにはstr.extractall()
メソッドを使う。
str.extractall()
の結果は以下の通り。str.extractall()
には引数expand
は無く、常にindex
がマルチインデックスのpandas.DataFrame
を返す。
df_all = s_org2.str.extractall('([a-z]+)@([a-z.]+)')
print(df_all)
# 0 1
# match
# A 0 aaa xxx.com
# 1 iii xxx.com
# B 0 bbb yyy.net
# 1 jjj yyy.net
# C 0 ccc zzz.co.jp
print(df_all.index)
# MultiIndex(levels=[['A', 'B', 'C'], [0, 1]],
# labels=[[0, 0, 1, 1, 2], [0, 1, 0, 1, 0]],
# names=[None, 'match'])
マルチインデックスのpandas.DataFrame
の要素の指定・選択については以下の記事を参照。
マッチする部分が一つしかなくてもindex
はマルチインデックスとなるので注意。str.extract()
の例で使用したSeries
を使っている。
print(s_org.str.extractall('([a-z]+)@([a-z.]+)'))
# 0 1
# match
# A 0 aaa xxx.com
# B 0 bbb yyy.net
# C 0 ccc zzz.co.jp