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