【问题标题】:Applying a custom groupby aggregate function to output a binary outcome in pandas python应用自定义 groupby 聚合函数在 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 中做到这一点?

【问题讨论】:

    标签: python pandas group-by aggregate-functions


    【解决方案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_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
      

      【讨论】:

        猜你喜欢
        • 2017-08-04
        • 2023-01-12
        • 2019-04-12
        • 2019-06-08
        • 2019-11-05
        • 2014-11-23
        • 1970-01-01
        • 2016-02-09
        • 1970-01-01
        相关资源
        最近更新 更多