pandasで複数条件のAND, OR, NOTから行を抽出(選択)

Modified: | Tags: Python, pandas

pandasで複数の条件のAND, OR, NOTからpandas.DataFrameの行を抽出する方法を説明する。

注意点は二つ。

  1. &|~を使う(andornotだとエラー)
  2. 比較演算子を使うときは条件ごとに括弧で囲む(括弧がないとエラー)

なお、ここではブーリアンインデックス(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に対して、真偽値型boolTrue, 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)に比較演算子を適用すると、boolpandas.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

文字列メソッドなどでもboolpandas.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で行を抽出するには、複数のboolpandas.Seriesに対してAND, OR, NOTを適用すればよい。

2つのboolpandas.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

冒頭に書いたように、注意点は以下の二つ。

  1. &|~を使う(andornotだとエラー)
  2. 比較演算子を使うときは条件ごとに括弧で囲む(括弧がないとエラー)

例えば、andornotを使うと以下のようなエラーが発生する。

ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

andornotではなく&, |, ~を使う理由や括弧が必要な理由の詳細は以下の記事を参照。

isinメソッドによる複数条件の抽出

pandas.Seriesisin()メソッドは、引数に指定したリストのいずれかの要素に一致する要素に対して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

関連カテゴリー

関連記事