簡體   English   中英

應用自定義 groupby 聚合函數在 Pandas python 中輸出二進制結果

[英]Applying a custom groupby aggregate function to output a binary outcome in pandas python

我有一個交易者交易數據集,其中感興趣的變量是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,如果他Buy/Sell少於 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,而在這種情況下為 2 的總交易總數(不考慮 NA)。我對第二個表不感興趣,我只是感興趣在買入的合計數量和Buy/Sell的合計總數(計數)中。

我怎樣才能在 Pandas 中做到這一點?

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 循環速度。

當組數較多時,速度差異尤為顯着。 例如,具有 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

using_selectusing_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

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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM