SQLのように'in'と'not in'を使用してPandasのデータフレームをフィルタリングする方法

python pandas dataframe sql-function


SQLの INNOT IN に相当するものをどのように実現できますか?

必要な値のリストを持っています シナリオはこんな感じです

df = pd.DataFrame({'countries':['US','UK','Germany','China']})
countries = ['UK','China']

# pseudo-code:
df[df['countries'] not in countries]

現在の私のやり方は以下の通りです。

df = pd.DataFrame({'countries':['US','UK','Germany','China']})
countries = pd.DataFrame({'countries':['UK','China'], 'matched':True})

# IN
df.merge(countries,how='inner',on='countries')

# NOT IN
not_in = df.merge(countries,how='left',on='countries')
not_in = not_in[pd.isnull(not_in['matched'])]

しかし、これは恐ろしいほどの不良品のように思えます。誰か改善してくれませんか?




Answer 1 DSM


pd.Series.isin を使用できます。

「IN」の場合: something.isin(somewhere)

または「NOT IN」の場合: ~something.isin(somewhere)

実務例として。

>>> df
  countries
0        US
1        UK
2   Germany
3     China
>>> countries
['UK', 'China']
>>> df.countries.isin(countries)
0    False
1     True
2    False
3     True
Name: countries, dtype: bool
>>> df[df.countries.isin(countries)]
  countries
1        UK
3     China
>>> df[~df.countries.isin(countries)]
  countries
0        US
2   Germany



Answer 2 MaxU


.query()メソッドを使用する代替ソリューション:

In [5]: df.query("countries in @countries")
Out[5]:
  countries
1        UK
3     China

In [6]: df.query("countries not in @countries")
Out[6]:
  countries
0        US
2   Germany



Answer 3 cs95


パンダのDataFrameに'in'と'not in'を実装するには?

Pandasは2つのメソッドを提供します Series.isin DataFrame.isin に対して、それぞれSeries.isinとDataFrame.isinです。


1カラムに基づくフィルタDataFrame(シリーズにも適用されます

最も一般的なシナリオは、特定の列に isin 条件を適用して、DataFrameの行をフィルター処理することです。

df = pd.DataFrame({'countries': ['US', 'UK', 'Germany', np.nan, 'China']})
df
  countries
0        US
1        UK
2   Germany
3     China

c1 = ['UK', 'China']             # list
c2 = {'Germany'}                 # set
c3 = pd.Series(['China', 'US'])  # Series
c4 = np.array(['US', 'UK'])      # array

Series.isin は、さまざまなタイプを入力として受け入れます。以下は、必要なものを取得する有効な方法です。

df['countries'].isin(c1)

0    False
1     True
2    False
3    False
4     True
Name: countries, dtype: bool

# `in` operation
df[df['countries'].isin(c1)]

  countries
1        UK
4     China

# `not in` operation
df[~df['countries'].isin(c1)]

  countries
0        US
2   Germany
3       NaN

# Filter with `set` (tuples work too)
df[df['countries'].isin(c2)]

  countries
2   Germany

# Filter with another Series
df[df['countries'].isin(c3)]

  countries
0        US
4     China

# Filter with array
df[df['countries'].isin(c4)]

  countries
0        US
1        UK

多数のカラムにフィルタをかける

場合によっては、複数の列にまたがるいくつかの検索語句で「イン」メンバーシップチェックを適用したいこともあるでしょう。

df2 = pd.DataFrame({
    'A': ['x', 'y', 'z', 'q'], 'B': ['w', 'a', np.nan, 'x'], 'C': np.arange(4)})
df2

   A    B  C
0  x    w  0
1  y    a  1
2  z  NaN  2
3  q    x  3

c1 = ['x', 'w', 'p']

isin 条件を列 "A"と "B"の両方に適用するには、 DataFrame.isin を使用します。

df2[['A', 'B']].isin(c1)

      A      B
0   True   True
1  False  False
2  False  False
3  False   True

これから、少なくとも1つの列が True である行を保持するには、最初の軸に沿って any を使用できます。

df2[['A', 'B']].isin(c1).any(axis=1)

0     True
1    False
2    False
3     True
dtype: bool

df2[df2[['A', 'B']].isin(c1).any(axis=1)]

   A  B  C
0  x  w  0
3  q  x  3

すべてのカラムを検索したい場合は、カラム選択ステップを省略して

df2.isin(c1).any(axis=1)

同様に、すべての列が True である行を保持するには、以前と同じ方法で all を使用します。

df2[df2[['A', 'B']].isin(c1).all(axis=1)]

   A  B  C
0  x  w  0

主な numpy.isin :numpy.isin、 query 、リスト内包表記(文字列データ)

上記のメソッドに加えて、同等のnumpyを使用することもできます: numpy.isin

# `in` operation
df[np.isin(df['countries'], c1)]

  countries
1        UK
4     China

# `not in` operation
df[np.isin(df['countries'], c1, invert=True)]

  countries
0        US
2   Germany
3       NaN

なぜそれを検討する価値があるのですか?オーバーヘッドが少ないため、NumPy関数は通常、対応するパンダよりも少し高速です。これは、インデックスの配置に依存しない要素ごとの操作であるため、このメソッドがpandasの isin の適切な置き換えにならない状況はほとんどありません。

文字列操作はベクトル化するのが難しいため、文字列を操作する場合、通常Pandasルーチンは反復的です。ここでは、リスト内包表記の方が高速であることを示唆する多くの証拠があります。。私たちは、に頼る in なりましチェック。

c1_set = set(c1) # Using `in` with `sets` is a constant time operation... 
                 # This doesn't matter for pandas because the implementation differs.
# `in` operation
df[[x in c1_set for x in df['countries']]]

  countries
1        UK
4     China

# `not in` operation
df[[x not in c1_set for x in df['countries']]]

  countries
0        US
2   Germany
3       NaN

とはいえ、指定するのが面倒くさいので、わかっている人以外は使わないようにしましょう。

最後に、この回答でカバーされている DataFrame.query もあります。numexpr FTW!




Answer 4 Kos


普段はこんな感じで行に対して汎用的なフィルタリングをしていました。

criterion = lambda row: row['countries'] not in countries
not_in = df[df.apply(criterion, axis=1)]



Answer 5 Sam Henderson


dfProfilesBusIdsのBUSINESS_IDにもあるBUSINESS_IDを持つdfbc行をフィルタリングしたいと思いました。

dfbc = dfbc[~dfbc['BUSINESS_ID'].isin(dfProfilesBusIds['BUSINESS_ID'])]