pandasで複数条件のAND, OR, NOTから行を抽出(選択)
pandasで複数の条件のAND, OR, NOTからpandas.DataFrame
の行を抽出する方法を説明する。
注意点は二つ。
&
、|
、~
を使う(and
、or
、not
だとエラー)- 比較演算子を使うときは条件ごとに括弧で囲む(括弧がないとエラー)
なお、ここではブーリアンインデックス(Boolean indexing)を用いた方法を説明するが、query()
メソッドを使うとより簡潔に書ける。
本記事のサンプルコードのpandasのバージョンは以下の通り。以下のpandas.DataFrame
を例として使う。
import pandas as pd
print(pd.__version__)
# 2.0.3
df = pd.read_csv('data/src/sample_pandas_normal.csv')
print(df)
# name age state point
# 0 Alice 24 NY 64
# 1 Bob 42 CA 92
# 2 Charlie 18 CA 70
# 3 Dave 68 TX 70
# 4 Ellen 24 CA 88
# 5 Frank 30 NY 57
例はpandas.DataFrame
だが、pandas.Series
の要素を複数条件で抽出する場合も同様。
条件を満たす行を抽出する方法(ブーリアンインデックス)
pandas.DataFrame
に対して、真偽値型bool
(True
, False
)を要素とするリストやpandas.Series
を[]
で指定すると、True
の行が抽出される。ブーリアンインデックス(Boolean indexing)と呼ばれる。
mask = [True, False, True, False, True, False]
print(df[mask])
# name age state point
# 0 Alice 24 NY 64
# 2 Charlie 18 CA 70
# 4 Ellen 24 CA 88
pandas.DataFrame
の列(= pandas.Series
)に比較演算子を適用すると、bool
のpandas.Series
を得られる。これを利用して、条件を満たす行を抽出できる。
print(df['age'] < 25)
# 0 True
# 1 False
# 2 True
# 3 False
# 4 True
# 5 False
# Name: age, dtype: bool
print(df[df['age'] < 25])
# name age state point
# 0 Alice 24 NY 64
# 2 Charlie 18 CA 70
# 4 Ellen 24 CA 88
文字列メソッドなどでもbool
のpandas.Series
を得られるので同様に利用できる。
print(df['name'].str.endswith('e'))
# 0 True
# 1 False
# 2 True
# 3 True
# 4 False
# 5 False
# 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
# 3 Dave 68 TX 70
複数条件のAND, OR, NOTで行を抽出(&
、|
、~
を使用)
複数条件のAND, OR, NOTで行を抽出するには、複数のbool
のpandas.Series
に対してAND, OR, NOTを適用すればよい。
2つのbool
のpandas.Series
のAND(かつ)をとるには&
を使う。
print((df['age'] < 25) & df['name'].str.endswith('e'))
# 0 True
# 1 False
# 2 True
# 3 False
# 4 False
# 5 False
# dtype: bool
print(df[(df['age'] < 25) & df['name'].str.endswith('e')])
# name age state point
# 0 Alice 24 NY 64
# 2 Charlie 18 CA 70
OR(または)には|
、NOT(否定)には~
を使う。
print(~df['name'].str.endswith('e'))
# 0 False
# 1 True
# 2 False
# 3 False
# 4 True
# 5 True
# Name: name, dtype: bool
print(df['point'] < 65)
# 0 True
# 1 False
# 2 False
# 3 False
# 4 False
# 5 True
# Name: point, dtype: bool
print(~df['name'].str.endswith('e') | (df['point'] < 65))
# 0 True
# 1 True
# 2 False
# 3 False
# 4 True
# 5 True
# dtype: bool
print(df[~df['name'].str.endswith('e') | (df['point'] < 65)])
# name age state point
# 0 Alice 24 NY 64
# 1 Bob 42 CA 92
# 4 Ellen 24 CA 88
# 5 Frank 30 NY 57
冒頭に書いたように、注意点は以下の二つ。
&
、|
、~
を使う(and
、or
、not
だとエラー)- 比較演算子を使うときは条件ごとに括弧で囲む(括弧がないとエラー)
例えば、and
、or
、not
を使うと以下のようなエラーが発生する。
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
and
、or
、not
ではなく&
, |
, ~
を使う理由や括弧が必要な理由の詳細は以下の記事を参照。
isinメソッドによる複数条件の抽出
pandas.Series
のisin()
メソッドは、引数に指定したリストのいずれかの要素に一致する要素に対してTrue
を返す。
これまでの例と同様に、isin()
を利用していずれかの要素に一致する行を抽出できる。
print(df['state'].isin(['NY', 'TX']))
# 0 True
# 1 False
# 2 False
# 3 True
# 4 False
# 5 True
# Name: state, dtype: bool
print(df[df['state'].isin(['NY', 'TX'])])
# name age state point
# 0 Alice 24 NY 64
# 3 Dave 68 TX 70
# 5 Frank 30 NY 57
これは==
と|
を使った以下の条件抽出と同等。
print(df[(df['state'] == 'NY') | (df['state'] == 'TX')])
# name age state point
# 0 Alice 24 NY 64
# 3 Dave 68 TX 70
# 5 Frank 30 NY 57
isin()
メソッドで抽出できるのは完全一致のみ。部分一致などで抽出したい場合は上述の文字列メソッドなどと&
や|
を使う。
print(df[df['name'].str.contains('li') | df['name'].str.endswith('k')])
# name age state point
# 0 Alice 24 NY 64
# 2 Charlie 18 CA 70
# 5 Frank 30 NY 57
3個以上の条件では演算子の優先順位に注意
Pythonにおける演算子の優先順位は、高い順に~
、&
、|
。
条件が3つ以上あると、順番によって結果が異なる場合がある。
df_multi_1 = df[(df['age'] < 35) | ~(df['state'] == 'NY') & (df['point'] < 75)]
print(df_multi_1)
# name age state point
# 0 Alice 24 NY 64
# 2 Charlie 18 CA 70
# 3 Dave 68 TX 70
# 4 Ellen 24 CA 88
# 5 Frank 30 NY 57
df_multi_2 = df[(df['age'] < 35) & (df['point'] < 75) | ~(df['state'] == 'NY')]
print(df_multi_2)
# name age state point
# 0 Alice 24 NY 64
# 1 Bob 42 CA 92
# 2 Charlie 18 CA 70
# 3 Dave 68 TX 70
# 4 Ellen 24 CA 88
# 5 Frank 30 NY 57
コードの読み手(自分も含む)が優先順位を覚えているとは限らないので、先に処理したい部分を明示的に括弧で囲ったほうが無難。
df_multi_3 = df[((df['age'] < 35) | ~(df['state'] == 'NY')) & (df['point'] < 75)]
print(df_multi_3)
# name age state point
# 0 Alice 24 NY 64
# 2 Charlie 18 CA 70
# 3 Dave 68 TX 70
# 5 Frank 30 NY 57