Pandas:如何按符号分组并对每n行取平均值

4
假设我有以下数据框df:
        date        symbol_a  symbol_b   ratio  
    0    2017/01/01    AAAA       AA       10
    1    2017/01/02    AAAA       AA       20
    2    2017/01/03    AAAA       AA       30
    3    2017/01/04    AAAA       AA       10
    4    2017/01/05    AAAA       AA       10 
    5    2017/01/06    AAAA       AA       10
    6    2017/01/01    BBBB       BB       10
    7    2017/01/02    BBBB       BB       20
    8    2017/01/03    BBBB       BB       30
    9    2017/01/04    BBBB       BB       10
   10    2017/01/01    CCCC       CC       10
   11    2017/01/02    CCCC       CC       20
   12    2017/01/03    CCCC       CC       30
   13    2017/01/04    CCCC       CC       10
   14    2017/01/05    CCCC       CC       10  
   15    2017/01/06    CCCC       CC        5

我对ratio列的平均值很感兴趣(这来自于之前的数据框,其中还有两列value_a和value_b,ratio=value_a/value_b,更多或者更少)。我想要做的是:

根据symbol_a(或者_b,其实是一样的)每n个元素取平均值。比如说n=3。

通常情况下,我会做如下操作:

df.groupby(['symbol_a','symbol_b']).mean()

然而,我希望每三天获得子平均值(实际时间跨度显然要长得多,我需要每五天)。

最初我以为我总是会有相同数量的符号,可以被n整除,所以我尝试了这样的方法:

df.groupby([df.index/n, 'symbol_a', 'symbol_b']).mean().reset_index()

这个方法很有效,但是强烈依赖于除数为n的假设。不幸的是,我想要放弃这种假设,并且我也注意到并非所有符号都出现了n次:例如注意到symbol_a "BBBB"仅出现了四次(天)。这当然使上述尝试完全不可靠,因为它会混合不同符号的比例值。
总之,我需要一些东西,让我每隔n个元素就获得“比率”列的平均值;如果符号数不能被n整除,则每隔n个元素获得平均值,最后获得剩余部分的平均值(如果符号数
例如,结果可能如下所示(假设n=3):
      symbol_a  symbol_b   3_mean_ratio
0       AAAA       AA          20       
1       AAAA       AA          10
2       BBBB       BB          20   
4       BBBB       BB          10   
5       CCCC       CC          20       
6       CCCC       CC         8.33   

有没有类似这样的方法?非常感谢您的帮助,谢谢。
编辑:感谢迄今为止的答案。最理想的是一个简单的方法,可以将新的n-days-ratio列放在原始列旁边。当然,这个新列的长度更短,但如果元素重复,我也不介意。基本上,我需要一种根据这个平均值选择“好行”的方法。到目前为止,我正在创建一个字典作为中间步骤,但我相信有更好的方法。如果我能获得下面这样的东西就太好了:
        date        symbol_a  symbol_b   ratio  n-days-ratio
    0    2017/01/01    AAAA       AA       10      20
    1    2017/01/02    AAAA       AA       20      20
    2    2017/01/03    AAAA       AA       30      20
    3    2017/01/04    AAAA       AA       10      10
    4    2017/01/05    AAAA       AA       10      10
    5    2017/01/06    AAAA       AA       10      10
    6    2017/01/01    BBBB       BB       10      20
    7    2017/01/02    BBBB       BB       20      20
    8    2017/01/03    BBBB       BB       30      20
    9    2017/01/04    BBBB       BB       10      10
   10    2017/01/01    CCCC       CC       10      20
   11    2017/01/02    CCCC       CC       20      20
   12    2017/01/03    CCCC       CC       30      20
   13    2017/01/04    CCCC       CC       10     8.3
   14    2017/01/05    CCCC       CC       10     8.3
   15    2017/01/06    CCCC       CC        5     8.3
2个回答

3

编辑添加n天平均列

g = df.groupby('symbol_a').cumcount()
df['n-days-ratio'] = df.groupby(['symbol_a','symbol_b',g // 3]).transform(lambda x: x.mean())
df

输出:

          date symbol_a symbol_b  ratio  n-days-ratio
0   2017/01/01     AAAA       AA     10     20.000000
1   2017/01/02     AAAA       AA     20     20.000000
2   2017/01/03     AAAA       AA     30     20.000000
3   2017/01/04     AAAA       AA     10     10.000000
4   2017/01/05     AAAA       AA     10     10.000000
5   2017/01/06     AAAA       AA     10     10.000000
6   2017/01/01     BBBB       BB     10     20.000000
7   2017/01/02     BBBB       BB     20     20.000000
8   2017/01/03     BBBB       BB     30     20.000000
9   2017/01/04     BBBB       BB     10     10.000000
10  2017/01/01     CCCC       CC     10     20.000000
11  2017/01/02     CCCC       CC     20     20.000000
12  2017/01/03     CCCC       CC     30     20.000000
13  2017/01/04     CCCC       CC     10      8.333333
14  2017/01/05     CCCC       CC     10      8.333333
15  2017/01/06     CCCC       CC      5      8.333333

让我们使用:

g = df.groupby('symbol_a')['ratio'].transform(lambda x:x.astype(bool).cumsum().add(-1))

相比之下,让我们使用piRSquare的cumcount方法。

g = df.groupby('symbol_a').cumcount()

df_out = df.groupby(['symbol_a','symbol_b',g // 3]).mean().reset_index(level=2, drop=True).reset_index()

输出:

  symbol_a symbol_b      ratio
0     AAAA       AA  20.000000
1     AAAA       AA  10.000000
2     BBBB       BB  20.000000
3     BBBB       BB  10.000000
4     CCCC       CC  20.000000
5     CCCC       CC   8.333333

这看起来很不错。如果您能详细解释一下答案就更好了。我想整数除法保证元素会被分为三个一组,并且余数会按原样分组。也许我不确定我理解您是如何构建“g”,以及为什么在“df_out”中两次重置索引。谢谢您的回答。 - Tommy
@Tommy... 确定。我构建g的方式基本上是通过使用cumsum计算每个组中的记录数,然后减去-1以获得整数除法来分组每三个记录。但是,piRSquared有一种更好的使用cumcount计算g的方法。我们正在做同样的事情,只是他的方法更加优雅,我将把我的g更改为他的代码。而且,我不得不两次重置索引,因为我的一个索引名称已经在列名中,因此我必须先重置和删除该索引,然后使用reset_index将所有内容恢复到列中。请查看我的编辑。 - Scott Boston
感谢您的编辑和解释。现在更清晰了,似乎也运行良好。说到优雅,有没有一种简单的方法将新的n天平均列放在原始列旁边?让我稍微修改一下我的问题,以使其更清晰。 - Tommy
@Tommy 确定。请查看答案中的编辑。使用带有lambda函数的transform来计算该组的平均值。 - Scott Boston
谢谢!看起来很好用,非常感谢你的帮助。我刚开始在一个新项目中使用pandas,对数据框架还不太熟悉。再次感谢。 - Tommy

3

使用 cumcount() // 3 生成一个新的列进行分组

cols = ['symbol_a', 'symbol_b']
cc = df.groupby(cols).cumcount() // 3
cols += ['Cumcount']

d1 = df.assign(Cumcount=cc)

d1.groupby(cols).ratio.mean().reset_index('Cumcount', drop=True).reset_index()

  symbol_a symbol_b      ratio
0     AAAA       AA  20.000000
1     AAAA       AA  10.000000
2     BBBB       BB  20.000000
3     BBBB       BB  10.000000
4     CCCC       CC  20.000000
5     CCCC       CC   8.333333

谢谢,这是一个非常好的回复,正如@Scott Boston所指出的那样。最终我决定接受他的答案,因为它首先提供了有用的评论。非常感谢你的回答,如果可以的话,在这种情况下我很乐意接受两个答案。 - Tommy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接