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


433

SQL INと同等のものをどのようにして実現できますNOT 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'])]

しかし、これは恐ろしいクラッジのようです。誰かがそれを改善できますか?


1
私はあなたの解決策が最良の解決策だと思います。複数列のIN、NOT_INをカバーできます。
Bruce Jung、

単一列または複数列でテストしますか?
smci 2015

1
関連(パフォーマンス/パンダの内部):パンダpd.Series.isinのパフォーマンスとセットとアレイの比較
jpp

回答:


820

使用できます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

1
参考までに、@ LondonRobはDataFrameとして彼を持ち、あなたはシリーズです。DataFrame isinは.13で追加されました。
TomAugspurger 2013年

パンダ0.12.0でこれを行う方法について何か提案はありますか?現在リリースされているバージョンです。(たぶん、私は0.13だけ待つべきでしょうか?!)
LondonRob

あなたが実際にあなたにそれから(している例あなたのように)1次元配列を扱っている場合している最初の行を使用@DSMなどの代わりに、データフレームのシリーズは、使用:df = pd.Series({'countries':['US','UK','Germany','China']})
TomAugspurger

2
@TomAugspurger:いつものように、おそらく何かが足りない。 df私と彼の両方がDataFrameです。 countriesリストです。 ではなくをdf[~df.countries.isin(countries)]生成しDataFrameSeries0.11.0.dev-14a04ddでも動作するようです。
DSM

7
countries変数を再利用し続けるため、この答えは混乱を招きます。まあ、OPはそれをします、そしてそれは継承されます、しかし、何かが以前にひどく行われたということは、今それをひどくすることを正当化しません。
ifly6

63

.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

10
@LondonRob queryはもう実験的ではありません。
Paul Rougieux

38

パンダのDataFrameに「in」と「not in」を実装する方法は?

:パンダは、2つのメソッドを提供しています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

多くの列でフィルター

場合によっては、複数の列にいくつかの検索用語を含む「in」メンバーシップチェックを適用したい場合があります

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.isinquery、リストの内包表記(文字列データ)

上記のメソッドに加えて、同等の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関数は通常、対応するパンダよりも少し高速です。これはインデックスの配置に依存しない要素ごとの操作であるため、このメソッドがパンダの適切な代替ではない状況はほとんどありません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!


私はそれが好きですが、df1列にあるdf3の列を比較したい場合はどうなりますか?それはどのように見えますか?
アーサーD.ハウランド

12

私は通常、このような行に対して一般的なフィルタリングを行ってきました:

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

10
参考までに、これはベクトル化された@DSMソルンよりもはるかに遅い
Jeff

@ジェフ私はそれを期待しますが、パンダで直接利用できないものを直接フィルタリングする必要があるときに私はフォールバックします。(私は「.startwithやregexマッチングのように言っていましたが、それをすべて備えたSeries.strについて知りました!)
Kos

7

dfProfilesBusIdsのBUSINESS_IDにもあるBUSINESS_IDを持つdfbc行をフィルターで除外したかった

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

5
Falseと比較するのではなく、(受け入れられた回答で行われたように)isinを否定できます
OneCricketeer

6

回答から可能な解決策を照合する:

INの場合: df[df['A'].isin([3, 6])]

NOT INの場合:

  1. df[-df["A"].isin([3, 6])]

  2. df[~df["A"].isin([3, 6])]

  3. df[df["A"].isin([3, 6]) == False]

  4. df[np.logical_not(df["A"].isin([3, 6]))]


3
これは主に他の回答からの情報を繰り返します。を使用logical_notすることは、~オペレーターと一口で同等です。
cs95

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

実装

df[df.countries.isin(countries)]

残りの国のように実装しない

df[df.countries.isin([x for x in np.unique(df.countries) if x not in countries])]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.