rol*_*and 8 python group-by aggregate-functions pandas
我有一个交易者交易的数据集,其中感兴趣的变量Buy/Sell
是二进制的,并且当交易是买入时取值为1,如果是卖出则取0.一个例子如下:
Trader Buy/Sell
A 1
A 0
B 1
B 1
B 0
C 1
C 0
C 0
Run Code Online (Sandbox Code Playgroud)
我想计算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
Run Code Online (Sandbox Code Playgroud)
最终我想计算总的聚合购买数量,在这种情况下是1,以及聚合的交易总数(无视NA),在这种情况下为2.我对第二个表格不感兴趣,我只是感兴趣在总计购买数量和总计数量(计数)中Buy/Sell
.
我怎么能在熊猫中做到这一点?
unu*_*tbu 16
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)
Run Code Online (Sandbox Code Playgroud)
产量
Buy/Sell sum count
Trader
A NaN 1 2
B 1 2 3
C 0 1 3
Run Code Online (Sandbox Code Playgroud)
我的原始答案使用了自定义聚合器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'})
Run Code Online (Sandbox Code Playgroud)
虽然调用自定义函数可能很方便,但与内置聚合器(例如groupby/agg/mean
)相比,使用自定义函数时性能通常会明显变慢.内置聚合器是Cython化的,而自定义函数将性能降低到普通Python for循环速度.
当组的数量很大时,速度的差异尤其显着.例如,对于具有1000个组的10000行DataFrame,
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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
小智 5
Pandascut()
对 @unutbu 的答案进行了改进,只需一半的时间即可得到结果。
def using_select(df):\n grouped = df.groupby(['Trader'])\n result = grouped['Buy/Sell'].agg(['sum', 'count'])\n means = grouped['Buy/Sell'].mean()\n result['Buy/Sell'] = np.select(condlist=[means>0.5, means<0.5], choicelist=[1, 0], \n default=np.nan)\n return result\n\n\ndef using_cut(df):\n grouped = df.groupby(['Trader'])\n result = grouped['Buy/Sell'].agg(['sum', 'count', 'mean'])\n result['Buy/Sell'] = pd.cut(result['mean'], [0, 0.5, 1], labels=[0, 1], include_lowest=True)\n result['Buy/Sell']=np.where(result['mean']==0.5,np.nan, result['Buy/Sell'])\n return result\n
Run Code Online (Sandbox Code Playgroud)\n\nusing_cut()
在我的系统中,每个循环的平均运行时间为 5.21 毫秒,而using_select()
每个循环的平均运行时间为 10.4 毫秒。
%timeit using_select(df)\n10.4 ms \xc2\xb1 1.07 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 100 loops each)\n\n%timeit using_cut(df)\n5.21 ms \xc2\xb1 147 \xc2\xb5s per loop (mean \xc2\xb1 std. dev. of 7 runs, 100 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n