【问题标题】:Take the sum of every N rows in a pandas series取熊猫系列中每 N 行的总和
【发布时间】:2018-04-24 15:50:01
【问题描述】:

假设

s = pd.Series(range(50))

0      0
1      1
2      2
3      3
...
48     48
49     49

如何获得包含每 n 行总和的新系列?

当 n = 5 时,预期结果如下所示;

0      10
1      35
2      60
3      85
...
8      210
9      235

如果使用 loc 或 iloc 并通过 python 循环,当然可以完成,但我相信它可以简单地以 Pandas 的方式完成。

另外,这是一个非常简化的例子,我不期望序列的解释:)。我正在尝试的实际数据系列将时间索引和每秒发生的事件数作为值。

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    GroupBy.sum

    N = 5
    s.groupby(s.index // N).sum()
         
    0     10
    1     35
    2     60
    3     85
    4    110
    5    135
    6    160
    7    185
    8    210
    9    235
    dtype: int64
    

    将索引分成 5 个一组并进行相应的分组。


    numpy.reshape + sum

    如果大小是N的倍数(或5),可以reshape加:

    s.values.reshape(-1, N).sum(1)
    # array([ 10,  35,  60,  85, 110, 135, 160, 185, 210, 235])
    

    numpy.add.at

    b = np.zeros(len(s) // N)
    np.add.at(b, s.index // N, s.values)
    b
    # array([ 10.,  35.,  60.,  85., 110., 135., 160., 185., 210., 235.])
    

    【讨论】:

    • 如果我想每 7 行求和,那我用7*7 吗?
    • @alwaysaskingquestions 是的!
    • 我想知道将数组作为 s.values 并应用 numpy 解决方案是否更好。
    • @user32185 也许吧,但是由于OP没有提到性能,我给了OP我能想到的最简单的解决方案。
    • @user32185 当然!没有冒犯。而且我还添加了一个 numpy 解决方案,因为我很确定这会更快。
    【解决方案2】:

    在下面的示例中,我能想到的最有效的解决方案是f1()。它比在另一个答案中使用 groupby 快几个数量级。 请注意,当数组的长度不是精确的倍数时, f1() 不起作用,例如如果您想每 2 项求和一个 3 项数组。 对于这些情况,您可以使用 f1v2():

    f1v2( [0,1,2,3,4] ,2 ) = [1,5,4]
    

    我的代码如下。我使用timeit 进行比较:

    import timeit
    import numpy as np
    import pandas as pd
    
    
    def f1(a,x):
        if isinstance(a, pd.Series):
            a = a.to_numpy()
        return a.reshape((int(a.shape[0]/x), int(x) )).sum(1)
    
    def f2(myarray, x):
      return [sum(myarray[n: n+x]) for n in range(0, len(myarray), x)]
    
    def f3(myarray, x):
        s = pd.Series(myarray)
        out = s.groupby(s.index // 2).sum()
        return out
    
    def f1v2(a,x):
        if isinstance(a, pd.Series):
            a = a.to_numpy()
            
        mod = a.shape[0] % x
        if  mod != 0:
            excl = a[-mod:]
            keep = a[: len(a) - mod]
            out = keep.reshape((int(keep.shape[0]/x), int(x) )).sum(1)
            out = np.hstack( (excl.sum() , out) ) 
        else:       
            out = a.reshape((int(a.shape[0]/x), int(x) )).sum(1)
        
        return out
        
    
    a = np.arange(0,1e6)
    
    out1 = f1(a,2)
    out2 = f2(a,2)
    out3 = f2(a,2)
    
    t1 = timeit.Timer( "f1(a,2)" , globals = globals() ).repeat(repeat = 5, number = 2)
    t1v2 = timeit.Timer( "f1v2(a,2)" , globals = globals() ).repeat(repeat = 5, number = 2)
    t2 = timeit.Timer( "f2(a,2)" , globals = globals() ).repeat(repeat = 5, number = 2)
    t3 = timeit.Timer( "f3(a,2)" , globals = globals() ).repeat(repeat = 5, number = 2)
    
    resdf = pd.DataFrame(index = ['min time'])
    resdf['f1'] = [min(t1)]
    resdf['f1v2'] = [min(t1v2)]
    resdf['f2'] = [min(t2)]
    resdf['f3'] = [min(t3)]
    #the docs explain why it makes more sense to take the min than the avg
    resdf = resdf.transpose()
    resdf['% difference vs fastes'] = (resdf /resdf.min() - 1) * 100
    
    b = np.array( [0,1,2,4,5,6,7] )
    
    out1v2 = f1v2(b,2)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-09-23
      • 2018-11-08
      • 1970-01-01
      • 2022-10-17
      • 2014-02-19
      • 2018-05-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多