Como filtrar o dataframe do Pandas usando 'in' e 'not in' como no SQL


434

Como posso obter os equivalentes de SQL INeNOT IN ?

Eu tenho uma lista com os valores necessários. Aqui está o cenário:

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

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

Minha maneira atual de fazer isso é a seguinte:

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

Mas isso parece um sentimento horrível. Alguém pode melhorar isso?


1
Eu acho que sua solução é a melhor solução. O seu pode cobrir IN, NOT_IN de várias colunas.
— Bruce Jung

Deseja testar em uma única coluna ou em várias colunas?
— smci 17/07/2015

1
Relacionados (de desempenho / pandas internos): desempenho pandas pd.Series.isin com o conjunto de contra matriz
— jpp

Respostas:


821

Você pode usar pd.Series.isin.

Para "IN", use: something.isin(somewhere)

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

Como um exemplo trabalhado:

>>> 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
Apenas um FYI, o @LondonRob tinha o dele como DataFrame e o seu é uma série. O DataFrame isinfoi adicionado em 0,13.
— TomAugspurger

Alguma sugestão de como fazer isso com os pandas 0.12.0? É a versão atual lançada. (Talvez eu deva esperar 0,13 ?!)
— LondonRob

Se você está lidando com matrizes unidimensionais (como no exemplo), na primeira linha, use uma série em vez de um DataFrame, como o @DSM usado: df = pd.Series({'countries':['US','UK','Germany','China']})
— TomAugspurger

2
@ TomAugspurger: como sempre, provavelmente estou perdendo alguma coisa. df, tanto o meu quanto o dele, é um DataFrame. countriesé uma lista. df[~df.countries.isin(countries)]produz a DataFrame, não a Seriese parece funcionar mesmo em 0.11.0.dev-14a04dd.
— DSM

7
Essa resposta é confusa porque você continua reutilizando a countriesvariável. Bem, o OP faz isso, e isso é herdado, mas que algo é feito mal antes, não justifica fazê-lo agora.
— ifly6

63

Solução alternativa que usa o método .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 querynão é mais experimental.
— Paul Rougieux 20/06/19

38

Como implementar 'in' e 'not in' para um DataFrame de pandas?

O Pandas oferece dois métodos: Series.isine DataFrame.isinpara Series e DataFrames, respectivamente.


Filtrar DataFrame com base em uma coluna (também se aplica a séries)

O cenário mais comum é aplicar uma isincondição em uma coluna específica para filtrar linhas em um 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.isinaceita vários tipos como entradas. A seguir, são apresentadas todas as formas válidas de obter o que você deseja:

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

Filtrar em MUITAS colunas

Às vezes, convém aplicar uma verificação de associação "com" com alguns termos de pesquisa em várias colunas,

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

Para aplicar a isincondição às duas colunas "A" e "B", use DataFrame.isin:

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

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

A partir disso, para manter as linhas onde há pelo menos uma colunaTrue , podemos usar anyao longo do primeiro eixo:

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

Observe que, se você deseja pesquisar todas as colunas, omite a etapa de seleção de colunas e faça

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

Da mesma forma, para manter as linhas onde estão TODAS as colunasTrue , use allda mesma maneira que antes.

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

   A  B  C
0  x  w  0

Notável Menções: numpy.isin,query , compreensões lista (dados de cadeia)

Além dos métodos descritos acima, você também pode usar o equivalente 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

Por que vale a pena considerar? As funções do NumPy geralmente são um pouco mais rápidas que seus equivalentes de pandas, devido à menor sobrecarga. Como essa é uma operação elementar que não depende do alinhamento do índice, há muito poucas situações em que esse método não é um substituto apropriado para os pandas.isin .

As rotinas do Pandas geralmente são iterativas ao trabalhar com strings, porque é difícil vetorizar operações de strings. Há muitas evidências para sugerir que a compreensão da lista será mais rápida aqui. . Recorremos a um incheque agora.

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

No entanto, é muito mais difícil especificar, portanto, não o use, a menos que você saiba o que está fazendo.

Por fim, há também o DataFrame.queryque foi abordado nesta resposta . numexpr FTW!


Eu gosto, mas e se eu quiser comparar uma coluna no DF3 que está na coluna DF1? Como seria isso?
— Arthur D. Howland

12

Eu costumo fazer filtragem genérica em linhas como esta:

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

10
FYI, este é muito mais lento do que soln @DSM que é vetorizado
— Jeff

@ Jeff, eu esperaria isso, mas é nisso que eu recorro quando preciso filtrar algo indisponível nos pandas diretamente. (Eu estava prestes a dizer "como .startwith ou correspondência de regex, mas só descobri sobre Series.str que tem tudo isso!)
— Kos

7

Eu queria filtrar as linhas dfbc que tinham um BUSINESS_ID que também estava no BUSINESS_ID de dfProfilesBusIds

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

5
Você pode negar o ISIN (como feito na resposta aceita) em vez de comparar para Falso
— OneCricketeer

6

Reunindo possíveis soluções a partir das respostas:

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

Para 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
Isso repete principalmente informações de outras respostas. Usar logical_noté um equivalente da boca do ~operador.
— cs95

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

implementar em :

df[df.countries.isin(countries)]

implementar não nos países de descanso:

df[df.countries.isin([x for x in np.unique(df.countries) if x not in countries])]
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.