有效地选择与Pandas DataFrame中的多个值之一匹配的行

时间:2014-03-18 16:29:20

标签: python pandas

问题

给出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. 执行某种联接

  4. 这里的表现权衡是什么?何时一种解决方案比其他方案更好?我错过了哪些解决方案?

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

2 个答案:

答案 0 :(得分:55)

您可以使用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

答案 1 :(得分:5)

因为在您的实际用例中,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)