【问题标题】:Efficiently select rows that match one of several values in Pandas DataFrame [duplicate]有效地选择与 Pandas DataFrame 中多个值之一匹配的行
【发布时间】:2014-04-24 11:43:40
【问题描述】:

问题

给定 Pandas DataFrame 中的数据,如下所示:

Name     Amount
---------------
Alice       100
Bob          50
Charlie     200
Alice        30
Charlie      10

我想选择Name 是集合{Alice, Bob} 中多个值之一的所有行

Name     Amount
---------------
Alice       100
Bob          50
Alice        30

问题

在 Pandas 中执行此操作的有效方法是什么?

我看到的选项

  1. 遍历行,使用 Python 处理逻辑
  2. 选择并合并许多语句,如下所示

    merge(df[df.name = specific_name] for specific_name in names) # something like this
    
  3. 执行某种连接

这里的性能权衡是什么?什么时候一种解决方案比其他解决方案更好?我缺少什么解决方案?

虽然上面的示例使用字符串,但我的实际工作使用匹配数百万行的 10-100 个整数,因此快速 NumPy 操作可能是相关的。

【问题讨论】:

  • “我的实际工作是数字”:您的实际问题很重要。例如,如果您需要对浮点数进行容忍,您将无法使用与字符串相同的解决方案。
  • @DSM 已编辑以指定我正在匹配整数。还增加了问题的规模。

标签: python pandas


【解决方案1】:

你可以使用isin系列方法:

In [11]: df['Name'].isin(['Alice', 'Bob'])
Out[11]: 
0     True
1     True
2    False
3     True
4    False
Name: Name, dtype: bool

In [12]: df[df.Name.isin(['Alice', 'Bob'])]
Out[12]: 
    Name  Amount
0  Alice     100
1    Bob      50
3  Alice      30

【讨论】:

  • 这确实是我要找的界面。它在后台是如何工作的?
  • 看起来它使用 Cython for 循环遍历数据和 set.__contains__ 来测试成员资格
  • 是的,检查 pandas.lib.ismember,正如你所说!有趣的是它使用 set 而不是 khash。
  • 是否可以在此处为检查的值指定正则表达式。假设表中特定列的行中的值为“hello world foo bar”,如果列中存在字符串“foo”,我需要返回该行。
  • @aditya 是的,使用df['Name'].str.contains('foo')
【解决方案2】:

由于在您的实际用例中,df['Name'] 中的值是 ints,因此您可以使用 NumPy 索引而不是 Series.isin 更快地生成布尔掩码。

idx = np.zeros(N, dtype='bool')
idx[names] = True
df[idx[df['Name'].values]]

例如,给定这样的设置:

import pandas as pd
import numpy as np

N = 100000
df = pd.DataFrame(np.random.randint(N, size=(10**6, 2)), columns=['Name', 'Amount'])
names = np.random.choice(np.arange(N), size=100, replace=False)

In [81]: %timeit idx = np.zeros(N, dtype='bool'); idx[names] = True; df[idx[df['Name'].values]]
100 loops, best of 3: 9.88 ms per loop

In [82]: %timeit df[df.Name.isin(names)]
10 loops, best of 3: 107 ms per loop

In [83]: 107/9.88
Out[83]: 10.82995951417004

N (本质上)是df['Names'] 可以达到的最大值。 如果N 更小,则速度收益不会那么大。与N = 200

In [93]: %timeit idx = np.zeros(N, dtype='bool'); idx[names] = True; df[idx[df['Name'].values]]
10 loops, best of 3: 62.6 ms per loop

In [94]: %timeit df[df.Name.isin(names)]
10 loops, best of 3: 178 ms per loop

In [95]: 178/62.6
Out[95]: 2.8434504792332267

注意:如上所示,似乎有速度优势,尤其是当N 变大时。但是,如果N 太大,则形成idx = np.zeros(N, dtype='bool') 可能不可行。


完整性检查:

expected = df[df.Name.isin(names)]
idx = np.zeros(N, dtype='bool')
idx[names] = True
result = df[idx[df['Name'].values]]
assert expected.equals(result)

【讨论】:

  • 我想知道 isin 的性能是否可以使用 khash 来改进,而不是在 lib.ismember 中设置大型集合(特别是对于 int/非对象用例)。
  • @AndyHayden:不幸的是,我对 khash 几乎一无所知。我在哪里可以了解它?
  • 好吧,我刚刚查看了 pandas 哈希表中的源代码。看起来可以使用例如h = pd.hashtable.Int64HashTable()
  • 您的方式仍然明显快(尽管 HashTable 肯定比 set 好很多)。
  • 其实回顾你的时间它之前发生了一些奇怪的事情,将不得不再次检查这个。 (我看到你的方法快了一千倍,这不对,也不是你看到的)
猜你喜欢
  • 1970-01-01
  • 2021-02-22
  • 1970-01-01
  • 2022-08-19
  • 1970-01-01
  • 2014-06-21
  • 2022-09-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多