So filtern Sie Pandas-Datenrahmen mit 'in' und 'nicht in' wie in SQL

432

Wie kann ich die Entsprechungen von SQL INund erreichen NOT IN?

Ich habe eine Liste mit den erforderlichen Werten. Hier ist das Szenario:

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

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

Meine derzeitige Vorgehensweise ist wie folgt:

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

Aber das scheint ein schrecklicher Kludge zu sein. Kann jemand es verbessern?

LondonRob
quelle
1
Ich denke, Ihre Lösung ist die beste Lösung. Ihre können IN, NOT_IN mehrerer Spalten abdecken.
Bruce Jung
Möchten Sie an einer oder mehreren Spalten testen?
smci
1
Verwandte (Leistung / Pandas Interna): Pandas pd.Series.isin Leistung mit Set versus Array
jpp

Antworten:

817

Sie können verwenden pd.Series.isin.

Für "IN" verwenden Sie: something.isin(somewhere)

Oder für "NOT IN": ~something.isin(somewhere)

Als Beispiel:

>>> 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
quelle
1
Nur zu Ihrer Information, der @LondonRob hatte seinen als DataFrame und Ihr ist eine Serie. DataFrame's isinwurde in .13 hinzugefügt.
TomAugspurger
Irgendwelche Vorschläge, wie man das mit Pandas 0.12.0 macht? Es ist die aktuell veröffentlichte Version. (Vielleicht sollte ich einfach auf 0,13 warten?!)
LondonRob
Wenn Sie tatsächlich mit eindimensionalen Arrays arbeiten (wie in Ihrem Beispiel), verwenden Sie in Ihrer ersten Zeile eine Serie anstelle eines DataFrame, wie @DSM verwendet:df = pd.Series({'countries':['US','UK','Germany','China']})
TomAugspurger
2
@ TomAugspurger: Wie immer fehlt mir wahrscheinlich etwas. df, sowohl mein als auch sein, ist ein DataFrame. countriesist eine Liste. df[~df.countries.isin(countries)]erzeugt a DataFrame, nicht a Seriesund scheint sogar in 0.11.0.dev-14a04dd zu funktionieren.
DSM
6
Diese Antwort ist verwirrend, da Sie die countriesVariable immer wieder verwenden. Nun, das OP macht es, und das ist vererbt, aber dass etwas vorher schlecht gemacht wurde, rechtfertigt es nicht, es jetzt schlecht zu machen.
ifly6
63

Alternative Lösung mit der Methode .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
quelle
10
@LondonRob queryist nicht mehr experimentell.
Paul Rougieux
37

Wie implementiere ich 'in' und 'not in' für einen Pandas DataFrame?

Pandas bietet zwei Methoden an: Series.isinund DataFrame.isinfür Serien- bzw. DataFrames.


Filtern von DataFrame basierend auf EINER Spalte (gilt auch für Serien)

Das häufigste Szenario ist das Anwenden einer isinBedingung auf eine bestimmte Spalte, um Zeilen in einem DataFrame zu filtern.

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.isinakzeptiert verschiedene Typen als Eingaben. Im Folgenden finden Sie alle gültigen Methoden, um das zu erhalten, was Sie möchten:

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

Filtern Sie nach vielen Spalten

Manchmal möchten Sie eine In-Mitgliedschaftsprüfung mit einigen Suchbegriffen auf mehrere Spalten anwenden.

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

isinVerwenden Sie Folgendes, um die Bedingung auf beide Spalten "A" und "B" anzuwenden DataFrame.isin:

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

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

Um Zeilen beizubehaltenTrue , in denen sich mindestens eine Spalte befindet , können wir anyentlang der ersten Achse Folgendes verwenden:

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

Beachten Sie, dass Sie, wenn Sie jede Spalte durchsuchen möchten, den Schritt zur Spaltenauswahl einfach weglassen und ausführen müssen

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

In ähnlicher Weise zu Reihen zu behalten , wo alle Spalten sindTrue , die Verwendung allin der gleichen Weise wie zuvor.

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

   A  B  C
0  x  w  0

Bemerkenswerte Mentions: numpy.isin, query, Listenkomprehensionen (string data)

Zusätzlich zu den oben beschriebenen Methoden können Sie auch das numpy-Äquivalent verwenden : 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

Warum ist es eine Überlegung wert? NumPy-Funktionen sind aufgrund des geringeren Overheads normalerweise etwas schneller als ihre Pandas-Entsprechungen. Da dies eine elementweise Operation ist, die nicht von der Indexausrichtung abhängt, gibt es nur sehr wenige Situationen, in denen diese Methode kein geeigneter Ersatz für Pandas ist isin.

Pandas-Routinen sind normalerweise iterativ, wenn mit Zeichenfolgen gearbeitet wird, da Zeichenfolgenoperationen schwer zu vektorisieren sind. Es gibt viele Hinweise darauf, dass das Listenverständnis hier schneller sein wird. . Wir greifen injetzt auf einen Scheck zurück.

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

Die Angabe ist jedoch viel unhandlicher. Verwenden Sie sie daher nur, wenn Sie wissen, was Sie tun.

Schließlich gibt es DataFrame.querynoch etwas, das in dieser Antwort behandelt wurde . numexpr FTW!

cs95
quelle
Ich mag es, aber was ist, wenn ich eine Spalte in df3 vergleichen möchte, die sich in der Spalte df1 befindet? Wie würde das aussehen?
Arthur D. Howland
12

Ich habe normalerweise generisches Filtern über Zeilen wie diese durchgeführt:

criterion = lambda row: row['countries'] not in countries
not_in = df[df.apply(criterion, axis=1)]
Kos
quelle
10
Zu Ihrer Information, dies ist viel langsamer als die @DSM-Lösung, die vektorisiert ist
Jeff
@ Jeff Ich würde das erwarten, aber darauf greife ich zurück, wenn ich über etwas filtern muss, das in Pandas nicht verfügbar ist. (Ich wollte gerade sagen "wie .startwith oder Regex Matching, habe aber gerade etwas über Series.str herausgefunden, das all das hat!)
Kos
7

Ich wollte dfbc-Zeilen herausfiltern, die eine BUSINESS_ID hatten, die sich auch in der BUSINESS_ID von dfProfilesBusIds befand

dfbc = dfbc[~dfbc['BUSINESS_ID'].isin(dfProfilesBusIds['BUSINESS_ID'])]
Sam Henderson
quelle
5
Sie können das isin negieren (wie in der akzeptierten Antwort angegeben), anstatt es mit False
OneCricketeer zu vergleichen.
6

Zusammenstellung möglicher Lösungen aus den Antworten:

Für in: df[df['A'].isin([3, 6])]

Für 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]))]

Abhishek Gaur
quelle
3
Dies wiederholt meistens Informationen aus anderen Antworten. Die Verwendung logical_notist ein Bissenäquivalent des ~Bedieners.
CS95
3
df = pd.DataFrame({'countries':['US','UK','Germany','China']})
countries = ['UK','China']

implementieren in :

df[df.countries.isin(countries)]

nicht in wie in anderen Ländern umsetzen :

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