Как отфильтровать данные Pandas, используя 'in' и 'not in', как в SQL

434

Как я могу достичь эквивалентов 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'])]

Но это похоже на ужасный клудж. Кто-нибудь может улучшить это?

LondonRob
источник
1
Я думаю, что ваше решение является лучшим решением. Ваш может охватывать IN, NOT_IN нескольких столбцов.
Брюс Юнг
Вы хотите протестировать один столбец или несколько столбцов?
smci
1
Связанные (производительность / внутренняя часть pandas): производительность Pandas pd.Series.isin с набором по сравнению с массивом
jpp

Ответы:

821

Вы можете использовать pd.Series.isin.

Для "IN" используйте: something.isin(somewhere)

Или для «НЕ В»: ~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
DSM
источник
1
Просто к вашему сведению, @LondonRob имел его в качестве DataFrame, а ваш - серия. DataFrame isinбыл добавлен в .13.
TomAugspurger
Любые предложения о том, как это сделать с пандами 0.12.0? Это текущая выпущенная версия. (Может, мне просто подождать 0,13 ?!)
LondonRob
Если вы на самом деле имеете дело с одномерными массивами (как в вашем примере), то в первой строке используйте Series вместо DataFrame, например, @DSM used:df = pd.Series({'countries':['US','UK','Germany','China']})
TomAugspurger
2
@TomAugspurger: как обычно, я, наверное, что-то упускаю. df, и мой, и его, это DataFrame. countriesэто список. df[~df.countries.isin(countries)]производит a DataFrame, а не a Seriesи, кажется, работает даже в 0.11.0.dev-14a04dd.
DSM
7
Этот ответ сбивает с толку, потому что вы продолжаете использовать countriesпеременную. Что ж, ОП делает это, и это наследуется, но то, что раньше что-то делалось плохо, не оправдывает того, чтобы делать это плохо сейчас.
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
MaxU
источник
10
@LondonRob queryбольше не экспериментальный.
Поль Ружье
39

Как реализовать «в» и «не в» для панды DataFrame?

Pandas предлагает два метода: Series.isinи DataFrame.isinдля Series и DataFrames, соответственно.


Фильтр 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

Исходя из этого, чтобы сохранить строки, где находится хотя бы один столбец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.isin, query, списочные (строка данных)

В дополнение к методам , описанным выше, вы можете также использовать 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 обычно немного быстрее, чем их эквиваленты в pandas, из-за меньших накладных расходов. Так как это поэлементная операция, которая не зависит от выравнивания индекса, очень мало ситуаций, когда этот метод не является подходящей заменой для pandas ' 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что было рассмотрено в этом ответе . Numxpr FTW!

cs95
источник
Мне это нравится, но что, если я хочу сравнить столбец в df3, который находится в столбце df1? Как это будет выглядеть?
Артур Д.
12

Я обычно делаю общую фильтрацию по строкам следующим образом:

criterion = lambda row: row['countries'] not in countries
not_in = df[df.apply(criterion, axis=1)]
Кос
источник
10
К вашему сведению, это намного медленнее, чем @DSM soln, который векторизован
Джефф
@ Джефф Я бы ожидал этого, но к этому я прибегаю, когда мне нужно отфильтровать что-то недоступное в пандах напрямую. (Я собирался сказать «как .startwith или сопоставление регулярных выражений, но только что узнал о Series.str, который имеет все это!)
Кос
7

Я хотел отфильтровать строки dfbc, которые имели BUSINESS_ID, который также был в BUSINESS_ID dfProfilesBusIds

dfbc = dfbc[~dfbc['BUSINESS_ID'].isin(dfProfilesBusIds['BUSINESS_ID'])]
Сэм Хендерсон
источник
5
Вы можете отрицать isin (как сделано в принятом ответе), а не сравнивать с False
OneCricketeer
6

Собирая возможные решения из ответов:

Для IN: df[df['A'].isin([3, 6])]

Для НЕ В:

  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])]
Иоаннис Насиос
источник