pandasで特定の文字列を含む行を抽出(完全一致、部分一致)
pandas.DataFrame
から特定の文字列を含む要素を持つ行を抽出する方法(完全一致・部分一致)について説明する。
ここではブーリアンインデックス(Boolean indexing)を用いた方法を説明するが、query()
メソッドを使うことも可能。
データ(要素)ではなく、行名・列名が特定の文字列を含む行・列を抽出するにはfilter()
メソッドを使う。以下の記事を参照。
本記事のサンプルコードのpandasのバージョンは以下の通り。以下のpandas.DataFrame
を例として使う。
import pandas as pd
print(pd.__version__)
# 2.0.3
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
例はpandas.DataFrame
だが、pandas.Series
でも同様。
条件を満たす行を抽出する方法(ブーリアンインデックス)
pandas.DataFrame
に対して、真偽値型bool
(True
, False
)を要素とするリストやpandas.Series
を[]
で指定すると、True
の行が抽出される。ブーリアンインデックス(Boolean indexing)と呼ばれる。
mask = [True, False, True]
print(df[mask])
# name age state point
# 0 Alice 24 NY 64
# 2 Charlie 18 CA 70
したがって、所望の条件に対応するbool
のpandas.Series
を取得できれば、その行を抽出できる。
複数条件で抽出する場合は&
(AND)、|
(OR)を使う。~
(NOT)も使用可能。詳細は以下の記事を参照。
特定の文字列と完全一致: ==, isin()
==
を使うと、指定した文字列と完全一致する要素が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
また、pandas.Series
のisin()
メソッドは、引数に指定したリストのいずれかの要素に完全一致する要素に対してTrue
を返す。
指定した複数の文字列のいずれかと完全一致する要素を抽出するにはこちらを使う。
print(df['state'].isin(['NY', 'CA']))
# 0 True
# 1 True
# 2 True
# Name: state, dtype: bool
print(df[df['state'].isin(['NY', 'CA'])])
# name age state point
# 0 Alice 24 NY 64
# 1 Bob 42 CA 92
# 2 Charlie 18 CA 70
特定の文字列を含む(部分一致): str.contains()
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
後述のように、第一引数に指定した文字列はデフォルトで正規表現パターンとして処理されるので注意。
欠損値NaNの処理: 引数na
要素が欠損値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 mask with non-boolean array 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, flags
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(r'\?'))
# 0 False
# 1 False
# 2 True
# Name: name, dtype: bool
引数flags
でre.IGNORECASE
などの正規表現フラグを指定することも可能。また、str.contains()
はre.search()
に相当するが、後述のように、re.match()
に相当するstr.match()
もある。
特定の文字列で始まる(前方一致): str.startswith()
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.endswith()
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.match()
str.match()
を使うと、要素の先頭が正規表現のパターンに一致するとTrue
となるpandas.Series
を取得できる。
print(df['name'].str.match('.*i'))
# 0 True
# 1 False
# 2 True
# Name: name, dtype: bool
print(df[df['name'].str.match('.*i')])
# name age state point
# 0 Alice 24 NY 64
# 2 Charlie 18 CA 70
上述のように、str.match()
はre.match()
に相当し、文字列の先頭がパターンにマッチするかを判定する。先頭にマッチしないとFalse
となる。
先頭に限らずパターンにマッチする部分を含むかどうかを判定したい場合は、上述のようにre.search()
に相当するre.contains()
をデフォルト(regex=True
)で使用する。
print(df['name'].str.match('i.*e'))
# 0 False
# 1 False
# 2 False
# Name: name, dtype: bool
print(df['name'].str.contains('i.*e'))
# 0 True
# 1 False
# 2 True
# Name: name, dtype: bool
re.match()
やre.search()
についての詳細は以下の記事を参照。
str.match()
はstr.contains()
と同様に引数na
, case
, flags
を指定可能。