如何为Pandas数据帧实现'in'和'not in'

Lon*_*Rob 353 python sql-function dataframe pandas

我怎样才能实现SQL的的等价物INNOT IN

我有一个包含所需值的列表.这是场景:

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

# pseudo-code:
df[df['countries'] not in countries]
Run Code Online (Sandbox Code Playgroud)

我目前的做法如下:

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'])]
Run Code Online (Sandbox Code Playgroud)

但这似乎是一个可怕的kludge.任何人都可以改进吗?

DSM*_*DSM 673

你可以用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
Run Code Online (Sandbox Code Playgroud)

  • 这个答案令人困惑,因为你不断重复使用`countries`变量.好吧,OP做到了,而且这是继承的,但是之前做得很糟糕并不能证明现在做得不好. (5认同)
  • 如果您实际上正在处理一维数组(就像您的示例一样),那么您在第一行使用 Series 而不是 DataFrame,就像使用的 @DSM 一样: `df = pd.Series({'countries' :['美国','英国','德国','中国']})` (2认同)
  • @TomAugspurger:像往常一样,我可能会遗漏一些东西.`df`,我和他的,都是`DataFrame`.`countries`是一个列表.`df [~df.countries.isin(countries)]`产生一个`DataFrame`,而不是`Series`,似乎甚至可以在0.11.0.dev-14a04dd中工作. (2认同)
  • @ifly6:同意,我犯了同样的错误,并在收到错误时意识到:“‘DataFrame’对象没有属性‘国家’ (2认同)
  • 对于那些对波浪号感到困惑的人(像我一样):/sf/ask/581363961/ (2认同)

Max*_*axU 49

使用.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
Run Code Online (Sandbox Code Playgroud)

  • @LondonRob`query`不再是实验性的。 (10认同)
  • .query 更具可读性。特别是对于“不在”的情况,而不是遥远的波形符。谢谢! (3认同)
  • @国家是什么?另一个数据框?一个列表 ? (3认同)
  • @FlorianCastelain,有人在原来的问题中重命名了一个变量:“countries”->“countries_to_keep”,所以我的答案变得无效。我相应地更新了我的答案。`countries_to_keep` - 是一个列表。 (3认同)
  • 确实是最具可读性的解决方案。我想知道是否存在语法来避免创建“countries_to_keep”。是否可以直接在查询中指定值列表? (2认同)

cs9*_*s95 27

Pandas DataFrame如何实现“ in”和“ not in”?

Pandas提供两种方法:Series.isinDataFrame.isin分别用于Series和DataFrames。


基于一个列过滤DataFrame(也适用于Series)

最常见的情况是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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

# Filter with `set` (tuples work too)
df[df['countries'].isin(c2)]

  countries
2   Germany
Run Code Online (Sandbox Code Playgroud)

# Filter with another Series
df[df['countries'].isin(c3)]

  countries
0        US
4     China
Run Code Online (Sandbox Code Playgroud)

# Filter with array
df[df['countries'].isin(c4)]

  countries
0        US
1        UK
Run Code Online (Sandbox Code Playgroud)

在许多列上过滤

有时,您可能希望对多个列应用带有某些搜索字词的“参与”成员资格检查,

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']
Run Code Online (Sandbox Code Playgroud)

要将isin条件应用于“ A”和“ B”列,请使用DataFrame.isin

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

      A      B
0   True   True
1  False  False
2  False  False
3  False   True
Run Code Online (Sandbox Code Playgroud)

由此,要保留至少一个列为的行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
Run Code Online (Sandbox Code Playgroud)

请注意,如果要搜索每列,则只需省略列选择步骤,然后执行

df2.isin(c1).any(axis=1)
Run Code Online (Sandbox Code Playgroud)

同样,要保留ALLTrueall列为的,请使用与以前相同的方式。

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

   A  B  C
0  x  w  0
Run Code Online (Sandbox Code Playgroud)

值得注意的提及: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
Run Code Online (Sandbox Code Playgroud)

为什么值得考虑?NumPy函数通常比同等的熊猫要快一些,因为它们的开销较低。由于这是不依赖于索引对齐的元素操作,因此在极少数情况下此方法不能适当地替代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
Run Code Online (Sandbox Code Playgroud)

但是,指定起来要麻烦得多,因此,除非您知道自己在做什么,否则不要使用它。

最后,此答案中DataFrame.query涵盖了这些内容。numexpr FTW!

  • 我喜欢它,但是如果我想比较 df3 中 df1 列中的列怎么办?那会是什么样子? (2认同)

Kos*_*Kos 12

我一直在对这样的行进行泛型过滤:

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

  • 仅供参考,这比矢量化的@DSM soln要慢得多 (9认同)
  • @Jeff我希望如此,但这就是当我需要直接过滤 pandas 中不可用的内容时我会回退的内容。(我本来想说“像 .startwith 或正则表达式匹配,但刚刚发现 Series.str 拥有所有这些!) (2认同)

小智 12

为什么没有人谈论各种过滤方法的性能?事实上,这个话题经常在这里出现(参见示例)。我自己对大型数据集进行了性能测试。这是非常有趣和有启发性的。

\n
df = pd.DataFrame({\'animals\': np.random.choice([\'cat\', \'dog\', \'mouse\', \'birds\'], size=10**7), \n                   \'number\': np.random.randint(0,100, size=(10**7,))})\n\ndf.info()\n\n<class \'pandas.core.frame.DataFrame\'>\nRangeIndex: 10000000 entries, 0 to 9999999\nData columns (total 2 columns):\n #   Column   Dtype \n---  ------   ----- \n 0   animals  object\n 1   number   int64 \ndtypes: int64(1), object(1)\nmemory usage: 152.6+ MB\n
Run Code Online (Sandbox Code Playgroud)\n
%%timeit\n# .isin() by one column\nconditions = [\'cat\', \'dog\']\ndf[df.animals.isin(conditions)]\n
Run Code Online (Sandbox Code Playgroud)\n
367 ms \xc2\xb1 2.34 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\n
Run Code Online (Sandbox Code Playgroud)\n
367 ms \xc2\xb1 2.34 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\n
Run Code Online (Sandbox Code Playgroud)\n
395 ms \xc2\xb1 3.9 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\n
Run Code Online (Sandbox Code Playgroud)\n
%%timeit\n# .query() by one column\nconditions = [\'cat\', \'dog\']\ndf.query(\'animals in @conditions\')\n
Run Code Online (Sandbox Code Playgroud)\n
987 ms \xc2\xb1 5.17 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\n
Run Code Online (Sandbox Code Playgroud)\n
395 ms \xc2\xb1 3.9 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\n
Run Code Online (Sandbox Code Playgroud)\n
41.9 s \xc2\xb1 490 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\n
Run Code Online (Sandbox Code Playgroud)\n
%%timeit\n# .loc[]\ndf.loc[(df.animals==\'cat\')|(df.animals==\'dog\')]\n
Run Code Online (Sandbox Code Playgroud)\n
3.64 s \xc2\xb1 62.5 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\n
Run Code Online (Sandbox Code Playgroud)\n
987 ms \xc2\xb1 5.17 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\n
Run Code Online (Sandbox Code Playgroud)\n
469 ms \xc2\xb1 8.98 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\n
Run Code Online (Sandbox Code Playgroud)\n
%%timeit\ndf[df.apply(lambda x: x[\'animals\'] in [\'cat\', \'dog\'], axis=1)]\n
Run Code Online (Sandbox Code Playgroud)\n
796 ms \xc2\xb1 30.9 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\n
Run Code Online (Sandbox Code Playgroud)\n

因此,该isin方法被证明是最快的,而 的方法apply()是最慢的,这并不奇怪。

\n


小智 11

从答案中整理可能的解决方案:

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

  • 这主要是重复来自其他答案的信息。使用`logical_not` 相当于`~` 运算符。 (6认同)

rac*_*hwa 7

.isin()您还可以在内部使用.query()

df.query('country.isin(@countries_to_keep).values')

# Or alternatively:
df.query('country.isin(["UK", "China"]).values')
Run Code Online (Sandbox Code Playgroud)

要否定您的查询,请使用~

df.query('~country.isin(@countries_to_keep).values')
Run Code Online (Sandbox Code Playgroud)

更新:

另一种方法是使用比较运算符:

df.query('country == @countries_to_keep')

# Or alternatively:
df.query('country == ["UK", "China"]')
Run Code Online (Sandbox Code Playgroud)

要否定查询,请使用!=

df.query('country != @countries_to_keep')
Run Code Online (Sandbox Code Playgroud)


小智 6

我想过滤出dfbc行,该行的BUSINESS_ID也在dfProfilesBusIds的BUSINESS_ID中

dfbc = dfbc[~dfbc['BUSINESS_ID'].isin(dfProfilesBusIds['BUSINESS_ID'])]
Run Code Online (Sandbox Code Playgroud)

  • 您可以否定isin(如接受的答案中所述),而不是与False比较 (5认同)

Ioa*_*ios 5

df = pd.DataFrame({'countries':['US','UK','Germany','China']})
countries = ['UK','China']
Run Code Online (Sandbox Code Playgroud)

实施于

df[df.countries.isin(countries)]
Run Code Online (Sandbox Code Playgroud)

不像其他国家那样实施:

df[df.countries.isin([x for x in np.unique(df.countries) if x not in countries])]
Run Code Online (Sandbox Code Playgroud)