pandasで特定の文字列を含む行を抽出(完全一致、部分一致)
pandasで特定の文字列を含む要素を持つ行を抽出する方法について、以下の内容を説明する。
- 行を抽出(選択)する方法
- 完全一致
==
- 部分一致
str.contains()
: 特定の文字列を含む- 引数
na
: 欠損値NaN
の処理 - 引数
case
: 大文字小文字の処理 - 引数
regex
: 正規表現パターンの使用
- 引数
str.endswith()
: 特定の文字列で終わるstr.startswith()
: 特定の文字列で始まるstr.match()
: 正規表現のパターンに一致する
部分一致する行を抽出して抜き出すためには、pandasに準備されている文字列メソッド(str.xxx()
)を条件に応じて使う。
ここではブールインデックスを用いたレガシーな方法を説明するが、query()
メソッドを使うとより簡潔に書ける。以下の記事を参照。
今回は例として以下のデータを使用する。
import pandas as pd
df = pd.read_csv('data/src/sample_pandas_normal.csv').head(3)
print(df)
# name age state point
# 0 Alice 24 NY 64
# 1 Bob 42 CA 92
# 2 Charlie 18 CA 70
サンプルのcsvファイルはコチラ。
例はpandas.DataFrame
だが、pandas.Series
でも同様。
データ(要素)ではなく、行名や列名に対して文字列メソッドを適用し、条件を満たす行や列を抽出することもできる。以下の記事を参照。
行を抽出(選択)する方法
まず、pandas.DataFrame
から行を抽出(選択)して新しいpandas.DataFrame
を取得する方法を示す。
真偽値bool
のリスト(配列)またはpandas.Series
を使うと、True
の行だけが抽出(選択)できる。
mask = [True, False, True]
df_mask = df[mask]
print(df_mask)
# name age state point
# 0 Alice 24 NY 64
# 2 Charlie 18 CA 70
したがって、文字列要素を持つ列に対して条件に応じたbool
のリストを取得できればよい。
完全一致(==)
==
を使うと、要素が文字列に完全一致するとTrue
となるpandas.Series
を取得できる。
print(df['state'] == 'CA')
# 0 False
# 1 True
# 2 True
# Name: state, dtype: bool
print(df[df['state'] == 'CA'])
# name age state point
# 1 Bob 42 CA 92
# 2 Charlie 18 CA 70
str.contains(): 特定の文字列を含む
pandas.Series
の文字列メソッドstr.contains()
を使うと、要素が特定の文字列を含むとTrue
となるpandas.Series
を取得できる。
print(df['name'].str.contains('li'))
# 0 True
# 1 False
# 2 True
# Name: name, dtype: bool
print(df[df['name'].str.contains('li')])
# name age state point
# 0 Alice 24 NY 64
# 2 Charlie 18 CA 70
後述のように、第一引数に指定した文字列はデフォルトで正規表現パターンとして処理されるので要注意。
引数na: 欠損値NaNの処理
要素が欠損値NaN
である場合、デフォルトではTrue
でもFalse
でもなくNaN
を返す。このため、そのpandas.Series
を使って行を抽出するとエラーになる。
df_nan = df.copy()
df_nan.iloc[2, 0] = float('nan')
print(df_nan)
# name age state point
# 0 Alice 24 NY 64
# 1 Bob 42 CA 92
# 2 NaN 18 CA 70
print(df_nan['name'].str.contains('li'))
# 0 True
# 1 False
# 2 NaN
# Name: name, dtype: object
# print(df_nan[df_nan['name'].str.contains('li')])
# ValueError: cannot index with vector containing NA / NaN values
str.contains()
の引数na
でNaN
の結果を置き換える値を指定できる。
print(df_nan['name'].str.contains('li', na=False))
# 0 True
# 1 False
# 2 False
# Name: name, dtype: bool
print(df_nan['name'].str.contains('li', na=True))
# 0 True
# 1 False
# 2 True
# Name: name, dtype: bool
条件として使う場合、na=True
とすればNaN
の行も選択され、na=False
とすればNaN
の行は選択されない。
引数case: 大文字小文字の処理
デフォルトでは大文字と小文字は区別して処理される。引数case
をFalse
とすると大文字小文字が区別されない。
print(df['name'].str.contains('LI'))
# 0 False
# 1 False
# 2 False
# Name: name, dtype: bool
print(df['name'].str.contains('LI', case=False))
# 0 True
# 1 False
# 2 True
# Name: name, dtype: bool
引数regex: 正規表現パターンの使用
str.contains()
を使う際に要注意なのが、第一引数に指定した文字列はデフォルトで正規表現パターンとして処理されるということ。
print(df['name'].str.contains('i.*e'))
# 0 True
# 1 False
# 2 True
# Name: name, dtype: bool
引数regex
をFalse
とすると第一引数の文字列そのものが含まれているかを判定する。
print(df['name'].str.contains('i.*e', regex=False))
# 0 False
# 1 False
# 2 False
# Name: name, dtype: bool
例えば?
や.
, *
などの正規表現の特殊文字を含むかどうかを判定したい場合はregex=False
とする必要がある。もちろん、\?
のように特殊文字をエスケープした正規表現パターンを指定してもよい。
デフォルトだとエラーになってしまう場合があるので注意。
df_q = df.copy()
df_q.iloc[2, 0] += '?'
print(df_q)
# name age state point
# 0 Alice 24 NY 64
# 1 Bob 42 CA 92
# 2 Charlie? 18 CA 70
# print(df_q['name'].str.contains('?'))
# error: nothing to repeat at position 0
print(df_q['name'].str.contains('?', regex=False))
# 0 False
# 1 False
# 2 True
# Name: name, dtype: bool
print(df_q['name'].str.contains('\?'))
# 0 False
# 1 False
# 2 True
# Name: name, dtype: bool
str.contains()
はre.search()
に相当し、引数flags
で正規表現のフラグを指定することも可能。後述のように、re.match()
に相当するstr.match()
もある。
なお、この例のように末尾の?
などを判定するのであれば次に説明するstr.endswith()
のほうが簡単。
str.endswith(): 特定の文字列で終わる
pandas.Series
の文字列メソッドstr.endswith()
を使うと、要素が特定の文字列で終わるとTrue
となるpandas.Series
を取得できる。
print(df['name'].str.endswith('e'))
# 0 True
# 1 False
# 2 True
# Name: name, dtype: bool
print(df[df['name'].str.endswith('e')])
# name age state point
# 0 Alice 24 NY 64
# 2 Charlie 18 CA 70
str.endswith()
も引数na
を持つ。欠損値NaN
の行を選択したい場合はna=True
、選択したくない場合はna=False
とする。
引数case
はない。常に大文字小文字が区別される。
また、第一引数の文字列がそのまま判定に使われ、正規表現パターンとして処理されることはない。
str.startswith(): 特定の文字列で始まる
pandas.Series
の文字列メソッドstr.startswith()
を使うと、要素が特定の文字列で始まるとTrue
となるpandas.Series
を取得できる。
print(df['name'].str.startswith('B'))
# 0 False
# 1 True
# 2 False
# Name: name, dtype: bool
print(df[df['name'].str.startswith('B')])
# name age state point
# 1 Bob 42 CA 92
str.startswith()
も引数na
を持つ。欠損値NaN
の行を選択したい場合はna=True
、選択したくない場合はna=False
とする。
引数case
はない。常に大文字小文字が区別される。
また、第一引数の文字列がそのまま判定に使われ、正規表現パターンとして処理されることはない。
str.match(): 正規表現のパターンに一致する
pandas.Series
の文字列メソッドstr.match()
を使うと、要素が正規表現のパターンに一致するとTrue
となるpandas.Series
を取得できる。
print(df['name'].str.match('.*i.*e'))
# 0 True
# 1 False
# 2 True
# Name: name, dtype: bool
print(df[df['name'].str.match('.*i.*e')])
# name age state point
# 0 Alice 24 NY 64
# 2 Charlie 18 CA 70
上述のように、str.match()
はre.match()
に相当し、文字列の先頭がパターンにマッチするかを判定する。先頭にマッチしないとFalse
となる。
print(df['name'].str.match('.*i'))
# 0 True
# 1 False
# 2 True
# Name: name, dtype: bool
print(df['name'].str.match('i.*e'))
# 0 False
# 1 False
# 2 False
# Name: name, dtype: bool
先頭に限らずパターンにマッチする部分を含むかどうかを判定したい場合は、上述のようにre.search()
に相当するre.contains()
をデフォルト(regex=True
)で使用する。
re.match()
やre.search()
についての詳細は以下の記事を参照。
str.match()
はstr.contains()
と同様に引数na
, case
, flags
を指定可能。