web-dev-qa-db-fra.com

Comment implémenter 'in' et 'not in' pour Pandas dataframe

Comment puis-je obtenir les équivalents de IN et NOT IN de SQL?

J'ai une liste avec les valeurs requises. Voici le scénario:

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

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

Ma façon actuelle de le faire est la suivante:

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

Mais cela ressemble à un horrible kludge. Quelqu'un peut-il l'améliorer?

312
LondonRob

Vous pouvez utiliser pd.Series.isin .

Pour "IN", utilisez: something.isin(somewhere)

Ou pour "NOT IN": ~something.isin(somewhere)

A titre d'exemple travaillé:

_>>> 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
_
611
DSM

Solution alternative utilisant la méthode . 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
40
MaxU

Comment implémenter 'in' et 'not in' pour un pandas DataFrame?

Pandas propose deux méthodes: Series.isin et DataFrame.isin pour Series et DataFrames, respectivement.


Filtrer le cadre de données en fonction d'une colonne (s'applique également à la série)

Le scénario le plus courant consiste à appliquer une condition isin sur une colonne spécifique pour filtrer les lignes d'un 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_ accepte différents types en tant qu'entrées. Voici tous les moyens valables d’obtenir ce que vous voulez:

_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
_

Filtrer sur de nombreuses colonnes

Parfois, vous voudrez appliquer une vérification de membre "in" avec certains termes de recherche sur plusieurs colonnes,

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

Pour appliquer la condition isin aux deux colonnes "A" et "B", utilisez _DataFrame.isin_:

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

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

À partir de cela, pour conserver les lignes dont au moins une colonne est True, on peut utiliser any le long du premier axe:

_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
_

Notez que si vous souhaitez effectuer une recherche dans chaque colonne, il vous suffit d’omettre l’étape de sélection

_df2.isin(c1).any(axis=1)
_

De même, pour conserver les lignes où TOUTES les colonnes sont True, utilisez all de la même manière qu'auparavant.

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

   A  B  C
0  x  w  0
_

Mentions notables: _numpy.isin_, query, compréhensions de liste (données de chaîne)

En plus des méthodes décrites ci-dessus, vous pouvez également utiliser l'équivalent 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
_

Pourquoi cela vaut-il la peine d'être envisagé? Les fonctions NumPy sont généralement un peu plus rapides que leurs équivalents pandas en raison de la réduction des frais généraux. Comme il s’agit d’une opération élémentaire ne dépendant pas de l’alignement d’index, il existe très peu de situations dans lesquelles cette méthode n’est pas un remplacement approprié pour les pandas 'isin.

Les routines de pandas sont généralement itératives lorsque vous travaillez avec des chaînes, car les opérations sur les chaînes sont difficiles à vectoriser. De nombreuses preuves suggèrent que la compréhension des listes sera plus rapide ici. . Nous avons recours à une vérification in maintenant.

_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
_

Il est toutefois beaucoup plus difficile à préciser, alors ne l'utilisez pas à moins de savoir ce que vous faites.

Enfin, il y a aussi _DataFrame.query_ qui a été couvert dans cette réponse . numexpr FTW!

15
cs95

Je fais habituellement du filtrage générique sur des lignes comme ceci:

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

Je voulais filtrer les lignes dfbc ayant un BUSINESS_ID qui figurait également dans le BUSINESS_ID de dfProfilesBusIds

dfbc = dfbc[~dfbc['BUSINESS_ID'].isin(dfProfilesBusIds['BUSINESS_ID'])]
6
Sam Henderson
df = pd.DataFrame({'countries':['US','UK','Germany','China']})
countries = ['UK','China']

implémenter dans:

df[df.countries.isin(countries)]

mettre en œuvre pas dans comme dans des pays de repos:

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

Rassembler les solutions possibles à partir des réponses:

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

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

0
Abhishek Gaur