应用自定义 groupby 聚合函数在 pandas python 中输出二进制结果
Posted
技术标签:
【中文标题】应用自定义 groupby 聚合函数在 pandas python 中输出二进制结果【英文标题】:Applying a custom groupby aggregate function to output a binary outcome in pandas python 【发布时间】:2015-01-04 22:11:39 【问题描述】:我有一个交易者交易数据集,其中感兴趣的变量是Buy/Sell
,它是二进制的,如果交易是买入则取值为 1,如果是卖出则取值为 0。一个示例如下所示:
Trader Buy/Sell
A 1
A 0
B 1
B 1
B 0
C 1
C 0
C 0
我想计算每个交易者的净Buy/Sell
,这样如果交易者有超过 50% 的交易作为买入,他的Buy/Sell
为 1,如果他的买入少于 50%那么他的Buy/Sell
将是 0,如果恰好是 50%,他将拥有 NA(并且在未来的计算中将被忽略)。
所以对于交易者 A,买入比例为(买入数量)/(交易总数)= 1/2 = 0.5,即 NA。
对于交易者 B,它是 2/3 = 0.67,即 1
对于交易者 C,它是 1/3 = 0.33,结果为 0
表格应如下所示:
Trader Buy/Sell
A NA
B 1
C 0
最终我想计算总购买次数,在本例中为 1,以及总交易次数(不考虑 NA),在本例中为 2。我对第二张表不感兴趣,我我只是对购买总数和Buy/Sell
的总数(计数)感兴趣。
如何在 Pandas 中做到这一点?
【问题讨论】:
【参考方案1】:Pandas cut()
改进了 @unutbu 的答案,用一半的时间得到结果。
def using_select(df):
grouped = df.groupby(['Trader'])
result = grouped['Buy/Sell'].agg(['sum', 'count'])
means = grouped['Buy/Sell'].mean()
result['Buy/Sell'] = np.select(condlist=[means>0.5, means<0.5], choicelist=[1, 0],
default=np.nan)
return result
def using_cut(df):
grouped = df.groupby(['Trader'])
result = grouped['Buy/Sell'].agg(['sum', 'count', 'mean'])
result['Buy/Sell'] = pd.cut(result['mean'], [0, 0.5, 1], labels=[0, 1], include_lowest=True)
result['Buy/Sell']=np.where(result['mean']==0.5,np.nan, result['Buy/Sell'])
return result
using_cut()
在我的系统中每个循环的平均运行时间为 5.21 毫秒,而using_select()
每个循环的平均运行时间为 10.4 毫秒。
%timeit using_select(df)
10.4 ms ± 1.07 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit using_cut(df)
5.21 ms ± 147 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
【讨论】:
【参考方案2】:import numpy as np
import pandas as pd
df = pd.DataFrame('Buy/Sell': [1, 0, 1, 1, 0, 1, 0, 0],
'Trader': ['A', 'A', 'B', 'B', 'B', 'C', 'C', 'C'])
grouped = df.groupby(['Trader'])
result = grouped['Buy/Sell'].agg(['sum', 'count'])
means = grouped['Buy/Sell'].mean()
result['Buy/Sell'] = np.select(condlist=[means>0.5, means<0.5], choicelist=[1, 0],
default=np.nan)
print(result)
产量
Buy/Sell sum count
Trader
A NaN 1 2
B 1 2 3
C 0 1 3
我的原始答案使用了自定义聚合器categorize
:
def categorize(x):
m = x.mean()
return 1 if m > 0.5 else 0 if m < 0.5 else np.nan
result = df.groupby(['Trader'])['Buy/Sell'].agg([categorize, 'sum', 'count'])
result = result.rename(columns='categorize' : 'Buy/Sell')
虽然调用自定义函数可能很方便,但性能通常
与内置函数相比,使用自定义函数时明显慢
聚合器(例如groupby/agg/mean
)。内置聚合器是
Cythonized,而自定义函数将性能降低到普通 Python
for 循环速度。
当组数为 大。例如,一个 10000 行的 DataFrame 有 1000 个组,
import numpy as np
import pandas as pd
np.random.seed(2017)
N = 10000
df = pd.DataFrame(
'Buy/Sell': np.random.randint(2, size=N),
'Trader': np.random.randint(1000, size=N))
def using_select(df):
grouped = df.groupby(['Trader'])
result = grouped['Buy/Sell'].agg(['sum', 'count'])
means = grouped['Buy/Sell'].mean()
result['Buy/Sell'] = np.select(condlist=[means>0.5, means<0.5], choicelist=[1, 0],
default=np.nan)
return result
def categorize(x):
m = x.mean()
return 1 if m > 0.5 else 0 if m < 0.5 else np.nan
def using_custom_function(df):
result = df.groupby(['Trader'])['Buy/Sell'].agg([categorize, 'sum', 'count'])
result = result.rename(columns='categorize' : 'Buy/Sell')
return result
using_select
比 using_custom_function
快 50 倍以上:
In [69]: %timeit using_custom_function(df)
10 loops, best of 3: 132 ms per loop
In [70]: %timeit using_select(df)
100 loops, best of 3: 2.46 ms per loop
In [71]: 132/2.46
Out[71]: 53.65853658536585
【讨论】:
以上是关于应用自定义 groupby 聚合函数在 pandas python 中输出二进制结果的主要内容,如果未能解决你的问题,请参考以下文章
pandas使用groupby函数进行分组聚合使用agg函数指定聚合统计计算的数值变量并自定义统计计算结果的名称(naming columns after aggregation)
pandas编写自定义函数计算多个数据列的加和(sum)使用groupby函数和apply函数聚合计算分组内多个数据列的加和
Pandas groupby 将特定函数聚合/应用到特定列(np.sum,sum)