Cara memfilter dataframe Pandas menggunakan 'in' dan 'not in' seperti di SQL

434

Bagaimana saya bisa mencapai setara dengan SQL INdan NOT IN?

Saya memiliki daftar dengan nilai yang diperlukan. Inilah skenarionya:

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

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

Cara saya saat ini melakukan ini adalah sebagai berikut:

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'])]

Tapi ini seperti lumpur yang mengerikan. Adakah yang bisa memperbaikinya?

LondonRob
sumber
1
Saya pikir solusi Anda adalah solusi terbaik. Anda dapat mencakup IN, NOT_IN dari beberapa kolom.
Bruce Jung
Apakah Anda ingin menguji pada satu kolom atau beberapa kolom?
smci
1
Terkait (kinerja / internal panda): Pandas pd.Series.isin kinerja dengan set versus array
jpp

Jawaban:

821

Anda bisa menggunakannya pd.Series.isin.

Untuk penggunaan "IN": something.isin(somewhere)

Atau untuk "NOT IN": ~something.isin(somewhere)

Sebagai contoh yang berhasil:

>>> 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
DSM
sumber
1
Hanya sebuah FYI, @LondonRob memilikinya sebagai DataFrame dan milik Anda adalah Seri. DataFrame isinditambahkan pada .13.
TomAugspurger
Ada saran untuk bagaimana melakukan ini dengan panda 0.12.0? Ini adalah versi yang dirilis saat ini. (Mungkin saya harus menunggu 0.13 ?!)
LondonRob
Jika Anda benar-benar berurusan dengan array 1 dimensi (seperti pada contoh Anda) maka pada baris pertama Anda gunakan Seri bukan DataFrame, seperti @DSM digunakan:df = pd.Series({'countries':['US','UK','Germany','China']})
TomAugspurger
2
@ TomAugspurger: seperti biasa, saya mungkin melewatkan sesuatu. df, baik milikku maupun miliknya, adalah a DataFrame. countriesadalah daftar. df[~df.countries.isin(countries)]menghasilkan DataFrame, bukan a Series, dan tampaknya berfungsi kembali di 0.11.0.dev-14a04dd.
DSM
7
Jawaban ini membingungkan karena Anda terus menggunakan kembali countriesvariabel. Nah, OP yang melakukannya, dan itu diwariskan, tetapi sesuatu telah dilakukan dengan buruk sebelumnya tidak membenarkan melakukannya dengan buruk sekarang.
ifly6
63

Solusi alternatif yang menggunakan metode .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
MaxU
sumber
10
@LondonRob querytidak lagi eksperimental.
Paul Rougieux
38

Bagaimana cara menerapkan 'dalam' dan 'tidak dalam' untuk DataFrame panda?

Penawaran panda dua metode: Series.isindan DataFrame.isinuntuk Seri dan DataFrames, masing-masing.


Filter DataFrame Berdasarkan pada ONE Column (juga berlaku untuk Seri)

Skenario yang paling umum adalah menerapkan isinkondisi pada kolom tertentu untuk memfilter baris dalam 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.isinmenerima berbagai jenis sebagai input. Berikut ini adalah semua cara yang sah untuk mendapatkan yang Anda inginkan:

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

Filter pada BANYAK Kolom

Terkadang, Anda ingin menerapkan cek keanggotaan 'dalam' dengan beberapa istilah pencarian di beberapa kolom,

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']

Untuk menerapkan isinkondisi pada kedua kolom "A" dan "B", gunakan DataFrame.isin:

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

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

Dari ini, untuk mempertahankan baris di mana setidaknya satu kolom beradaTrue , kita dapat menggunakan anysepanjang sumbu pertama:

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

Perhatikan bahwa jika Anda ingin mencari setiap kolom, Anda cukup menghilangkan langkah pemilihan kolom dan lakukan

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

Demikian pula, untuk mempertahankan baris di mana SEMUA kolom beradaTrue , gunakan alldengan cara yang sama seperti sebelumnya.

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

   A  B  C
0  x  w  0

Terkemuka Mention: numpy.isin, query, daftar comprehensions (data string)

Selain metode yang dijelaskan di atas, Anda juga dapat menggunakan numpy setara: 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

Mengapa itu layak dipertimbangkan? Fungsi NumPy biasanya sedikit lebih cepat daripada panda yang setara karena overhead yang lebih rendah. Karena ini adalah operasi elementwise yang tidak bergantung pada penyelarasan indeks, ada beberapa situasi di mana metode ini bukan pengganti yang tepat untuk panda 'isin .

Rutinitas panda biasanya berulang saat bekerja dengan string, karena operasi string sulit untuk vectorise. Ada banyak bukti yang menunjukkan bahwa pemahaman daftar akan lebih cepat di sini. . Kami menggunakan incek sekarang.

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

Namun, jauh lebih sulit untuk ditentukan, jadi jangan menggunakannya kecuali Anda tahu apa yang Anda lakukan.

Terakhir, ada juga DataFrame.queryyang telah dibahas dalam jawaban ini . numexpr FTW!

cs95
sumber
Saya menyukainya, tetapi bagaimana jika saya ingin membandingkan kolom di df3 yang ada di kolom df1? Terlihat seperti apa?
Arthur D. Howland
12

Saya biasanya melakukan penyaringan generik pada baris seperti ini:

criterion = lambda row: row['countries'] not in countries
not_in = df[df.apply(criterion, axis=1)]
Kos
sumber
10
FYI, ini jauh lebih lambat daripada @DSM soln yang merupakan vektor
Jeff
@ Jeff aku berharap itu, tapi itulah yang saya jatuh kembali ketika saya perlu menyaring sesuatu yang tidak tersedia di panda secara langsung. (Saya akan mengatakan "seperti .startwith atau pencocokan regex, tetapi baru tahu tentang Series.str yang memiliki semua itu!)
Kos
7

Saya ingin memfilter baris dfbc yang memiliki BUSINESS_ID yang juga ada di BUSINESS_ID dari dfProfilesBusIds

dfbc = dfbc[~dfbc['BUSINESS_ID'].isin(dfProfilesBusIds['BUSINESS_ID'])]
Sam Henderson
sumber
5
Anda dapat meniadakan isin (seperti yang dilakukan dalam jawaban yang diterima) daripada membandingkan dengan False
OneCricketeer
6

Menyusun kemungkinan solusi dari jawaban:

Untuk IN: df[df['A'].isin([3, 6])]

Untuk TIDAK DI:

  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]))]

Abhishek Gaur
sumber
3
Ini sebagian besar mengulang informasi dari jawaban lain. Penggunaan logical_notsetara dengan ~operator.
cs95
3
df = pd.DataFrame({'countries':['US','UK','Germany','China']})
countries = ['UK','China']

terapkan dalam :

df[df.countries.isin(countries)]

mengimplementasikan tidak seperti di negara-negara lain:

df[df.countries.isin([x for x in np.unique(df.countries) if x not in countries])]
Ioannis Nasios
sumber