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

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

Но это кажется ужасным клочем. Может ли кто-нибудь улучшить его?

Ответ 1

Вы можете использовать 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

Ответ 2

Альтернативное решение, в котором используется метод .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

Ответ 3

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

Pandas предлагает два метода: Series.isin и DataFrame.isin для серий и фреймов данных соответственно.


Фильтр DataFrame на основе одного столбца (также относится к серии)

Наиболее распространенный сценарий - применение условия isin к определенному столбцу для фильтрации строк в кадре данных.

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

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

Иногда вам может потребоваться проверка членства "в" с некоторыми поисковыми терминами в нескольких столбцах,

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

Процедуры Pandas обычно итеративны при работе со строками, потому что строковые операции трудно векторизовать. Существует много свидетельств того, что составление списков здесь будет быстрее.. Мы прибегаем к проверке in сейчас.

c1_set = set(c1) # Using 'in' with 'sets' is a constant time operation... 
                 # This does not 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!

Ответ 4

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

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

Ответ 5

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

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

Ответ 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]))]

Ответ 7

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

Ответ 8

Я предпочитаю, чтобы создать функцию, где я могу использовать %in% %!in% операторе, который используется в языке программирования R и .query и выбор столбцов.

def subset(df, query="", select=("")):
    query = str(query)
    query = query.replace("|", "¤")
    query = query.replace("&", "½")
    query = query.replace(" ", "")
    queries = re.split('¤|½', query)

    # the %!in% and %in% operator will have to be evaluated after
    in_operator = ["%in%", "%!in%"]
    queries_lc = [q for q in queries if any(x in q for x in in_operator)]
    queries = [q for q in queries if not any(x in q for x in in_operator)]

    if len(select) == 0:select = df.columns
    query = "".join(queries)
    query = query.replace("¤", "|")
    query = query.replace("½", "&")

    if len(queries_lc) > 0:
        for lc_q in queries_lc:

            if in_operator[0] in lc_q:  # %in%
                var, list_con = re.split(in_operator[0], lc_q)
                globals()["list_condition_used_in_subset"] = eval(list_con)
                df = df[df[var].isin(list_condition_used_in_subset)]

            else:  # %!in% - not in
                var, list_con = re.split(in_operator[1], lc_q)
                globals()["list_condition_used_in_subset"] = eval(list_con)
                df = df[~df[var].isin(list_condition_used_in_subset)]

    if len(queries) == 0 and len(queries_lc) > 0: df = df[select]  # if only a list condition query
    else:df = pd.DataFrame(df.query(query)[select])  # perform query and return selected - normal thing

    return df

df = pd.DataFrame({'countries':['US','UK','Germany','China'],"GDP":[1,2,3,4]})
countries = ['UK','China']
subset(df,query="countries %in% countries & GDP > 2")

  countries  GDP
3     China    4

subset(df,query="countries %!in% countries",select=["GDP"])

   GDP
0    1
2    3

Может быть довольно длинным, но его можно использовать для нескольких вещей

Ответ 9

np.isin немного быстрее, чем Series.isin для числовых данных, и намного быстрее для строковых данных из-за меньших издержек.

df[~np.isin(df['countries'].to_numpy(), countries)]

  countries
0        US
2   Germany