Как отфильтровать кадр данных 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])]
Для НЕ В:
-
df[-df["A"].isin([3, 6])]
-
df[~df["A"].isin([3, 6])]
-
df[df["A"].isin([3, 6]) == False]
-
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