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

Как я могу получить эквиваленты SQL IN и NOT IN?

У меня есть список с необходимыми значениями. Вот сценарий:

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

# pseudo-code:
df[df['country'] not in countries_to_keep]

Мой нынешний способ сделать это так:

df = pd.DataFrame({'country': ['US', 'UK', 'Germany', 'China']})
df2 = pd.DataFrame({'country': ['UK', 'China'], 'matched': True})

# IN
df.merge(df2, how='inner', on='country')

# NOT IN
not_in = df.merge(df2, how='left', on='country')
not_in = not_in[pd.isnull(not_in['matched'])]

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


person LondonRob    schedule 13.11.2013    source источник
comment
Связанные (производительность / внутреннее устройство pandas): Производительность Pandas pd.Series.isin с набором по сравнению с массивом   -  person jpp    schedule 28.06.2018
comment
Использование списка значений для выбора строк из фрейма данных pandas аналогично, но отрицание ~ было добавлено в качестве редактирования в 2019 году. .   -  person Trenton McKinney    schedule 27.12.2020


Ответы (9)


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

Для использования IN: something.isin(somewhere)

Или НЕ В: ~something.isin(somewhere)

В качестве рабочего примера:

import pandas as pd

>>> df
  country
0        US
1        UK
2   Germany
3     China
>>> countries_to_keep
['UK', 'China']
>>> df.country.isin(countries_to_keep)
0    False
1     True
2    False
3     True
Name: country, dtype: bool
>>> df[df.country.isin(countries_to_keep)]
  country
1        UK
3     China
>>> df[~df.country.isin(countries_to_keep)]
  country
0        US
2   Germany
person DSM    schedule 13.11.2013
comment
Если вы действительно имеете дело с одномерными массивами (например, в вашем примере), тогда в первой строке используйте Series вместо DataFrame, например, @DSM used: df = pd.Series({'countries':['US','UK','Germany','China']}) - person TomAugspurger; 13.11.2013
comment
@TomAugspurger: как обычно, наверное, я что-то упускаю. df, как мой, так и его, это DataFrame. countries - это список. df[~df.countries.isin(countries)] производит DataFrame, а не Series, и, похоже, работает даже в 0.11.0.dev-14a04dd. - person DSM; 14.11.2013
comment
Этот ответ сбивает с толку, потому что вы продолжаете повторно использовать переменную countries. Что ж, OP делает это, и это унаследовано, но то, что что-то было сделано плохо раньше, не оправдывает того, что делать это плохо сейчас. - person ifly6; 19.05.2018
comment
@ ifly6: Согласен, я сделал ту же ошибку и понял ее, когда получил сообщение об ошибке: объект DataFrame не имеет атрибута country - person le_llama; 23.04.2020
comment
Для людей, которых смущает тильда (например, меня): stackoverflow.com/questions/8305199/ - person bmurauer; 25.03.2021

Альтернативное решение, использующее .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
person MaxU    schedule 19.07.2017
comment
.query намного удобнее для чтения. Особенно для не в сценарии, против далекой тильды. Спасибо! - person Mike Honey; 03.09.2020

Как реализовать 'in' и 'not in' для pandas DataFrame?

Pandas предлагает два метода: _1 _ и 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 accepts various types as inputs. The following are all valid ways of getting what you want:

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

Фильтр по МНОГИМ столбцам

Sometimes, you will want to apply an 'in' membership check with some search terms over multiple columns,

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 обычно немного быстрее, чем их эквиваленты в пандах, из-за меньших накладных расходов. Поскольку это поэлементная операция, которая не зависит от выравнивания индекса, очень мало ситуаций, когда этот метод не подходит для замены isin pandas.

Подпрограммы 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, который был рассмотрен в этом ответе. numexpr FTW!

person cs95    schedule 07.04.2019
comment
Мне это нравится, но что, если я хочу сравнить столбец в df3, который находится в столбце df1? Как бы это выглядело? - person Arthur D. Howland; 21.10.2019

Обычно я выполняю общую фильтрацию по таким строкам:

criterion = lambda row: row['countries'] not in countries
not_in = df[df.apply(criterion, axis=1)]
person Kos    schedule 13.11.2013
comment
FYI, это намного медленнее, чем @DSM soln, который векторизован - person Jeff; 13.11.2013
comment
@Jeff, я бы ожидал этого, но именно к этому я прибегаю, когда мне нужно напрямую отфильтровать что-то недоступное в пандах. (Я собирался сказать, что такое соответствие .startwith или регулярному выражению, но только что узнал о Series.str, в котором есть все это!) - person Kos; 14.11.2013

Сопоставление возможных решений из ответов:

Для 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]))]

person Abhishek Gaur    schedule 01.06.2019
comment
Это в основном повторяет информацию из других ответов. Использование logical_not - это полный эквивалент оператора ~. - person cs95; 01.06.2019

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

dfbc = dfbc[~dfbc['BUSINESS_ID'].isin(dfProfilesBusIds['BUSINESS_ID'])]
person Sam Henderson    schedule 13.07.2017
comment
Вы можете отрицать isin (как это сделано в принятом ответе), а не сравнивать с False - person OneCricketeer; 19.07.2017

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])]
person Ioannis Nasios    schedule 04.04.2018

Уловка, если вы хотите сохранить порядок в списке:

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


ind=[df.index[df['country']==i].tolist() for i in countries_to_keep]
flat_ind=[item for sublist in ind for item in sublist]

df.reindex(flat_ind)

   country
2  Germany
0       US
person Billy Bonaros    schedule 16.09.2020

Моя ценность 2c: мне нужна была комбинация операторов in и ifelse для фрейма данных, и это сработало для меня.

sale_method = pd.DataFrame(model_data["Sale Method"].str.upper())
sale_method["sale_classification"] = np.where(
    sale_method["Sale Method"].isin(["PRIVATE"]),
    "private",
    np.where(
        sale_method["Sale Method"].str.contains("AUCTION"), "auction", "other"
    ),
)
person GenDemo    schedule 24.06.2021