【问题标题】:Sum a range of cells in a single column in pandas dataframe对 pandas 数据框中单列中的一系列单元格求和
【发布时间】:2018-01-07 03:05:37
【问题描述】:

我在 DataFrame 中有三列。我想获取 Streak_Count 列中的数字,并从 MON TOTAL 中的返回中总结出该单元格的数量。结果显示在 WANTED RESULT 中,如下所示。我无法弄清楚的问题是将可以是任意数字的单元格的数量相加>>在此示例中介于 1 和 4 之间。

              MON TOTAL STREAK_COUNT    WANTED RESULT
1/2/1992       1.123077       1          1.123077 (only 1 so 1.12)
2/3/1992      -1.296718       0 
3/2/1992      -6.355612       2          -7.65233 (sum of -1.29 and -6.35)
4/1/1992       5.634692       0 
5/1/1992       4.180605       2          9.815297 (sum of 5.63 and 4.18)
7/1/1992      -0.101016       0 
8/3/1992      -0.706125       2         -0.807141 (sum of -.10 and -.706)
10/1/1992      0.368579       0 
11/2/1992      3.822277       0 
1/4/1993       2.233359       0 
2/1/1993       15.219644      4         21.643859
3/1/1993       -2.647693      1         -2.647693
4/1/1993       1.599094       1         1.599094

【问题讨论】:

  • 你想要一些特殊的 pandas 函数还是循环可以工作?

标签: python pandas sum


【解决方案1】:

这一切都是为了找到合适的分组依据。在这种情况下,STREAK_COUNT 的反向累积总和将为您提供您想要的。

首先我们创建数据框:

import pandas as pd

>>> df = pd.DataFrame({'MON TOTAL':[1.123077, -1.296178, -6.355612, 5.634692, 4.180605, -0.101016, -0.706125,
                                    0.368579, 3.822277, 2.233359, 15.219644, -2.647693, 1.599094],
                       'STREAK_COUNT':[1, 0, 2, 0, 2, 0, 2, 0, 0, 0, 4, 1, 1]},
                      index=['1/2/1992', '2/3/1992', '3/2/1992', '4/1/1992', '5/1/1992', '7/1/1992', '8/3/1992',
                             '10/1/1992', '11/2/1992', '1/4/1993', '2/1/1993', '3/1/1993', '4/1/1993'])
>>> df
           MON TOTAL  STREAK_COUNT
1/2/1992    1.123077             1
2/3/1992   -1.296178             0
3/2/1992   -6.355612             2
4/1/1992    5.634692             0
5/1/1992    4.180605             2
7/1/1992   -0.101016             0
8/3/1992   -0.706125             2
10/1/1992   0.368579             0
11/2/1992   3.822277             0
1/4/1993    2.233359             0
2/1/1993   15.219644             4
3/1/1993   -2.647693             1
4/1/1993    1.599094             1

接下来找到组,计算每个组的总和,并将结果连接到原始数据帧:

>>> groups = df['STREAK_COUNT'][::-1].cumsum()[::-1]
>>> df['RESULT'] = df.groupby(groups)['MON TOTAL'].transform('sum')
>>> df
           MON TOTAL  STREAK_COUNT     RESULT
1/2/1992    1.123077             1   1.123077
2/3/1992   -1.296178             0  -7.651790
3/2/1992   -6.355612             2  -7.651790
4/1/1992    5.634692             0   9.815297
5/1/1992    4.180605             2   9.815297
7/1/1992   -0.101016             0  -0.807141
8/3/1992   -0.706125             2  -0.807141
10/1/1992   0.368579             0  21.643859
11/2/1992   3.822277             0  21.643859
1/4/1993    2.233359             0  21.643859
2/1/1993   15.219644             4  21.643859
3/1/1993   -2.647693             1  -2.647693
4/1/1993    1.599094             1   1.599094

如果您只想要每个连胜结束时的结果,请使用掩码对其进行过滤:

>>> df[df['STREAK_COUNT'] > 0]
          MON TOTAL  STREAK_COUNT     RESULT
1/2/1992   1.123077             1   1.123077
3/2/1992  -6.355612             2  -7.651790
5/1/1992   4.180605             2   9.815297
8/3/1992  -0.706125             2  -0.807141
2/1/1993  15.219644             4  21.643859
3/1/1993  -2.647693             1  -2.647693
4/1/1993   1.599094             1   1.599094

【讨论】:

  • jakevdp 很好的答案,你也可以使用内置的 cumsum 即df.groupby(df['STREAK_COUNT'][::-1].cumsum()[::-1])['MON TOTAL'].transform('sum')
【解决方案2】:

编辑 1:

对整个列执行操作而不是迭代并利用对象/迭代器从而避免列表的高效版本。

这里的变化是,

1) 我们首先获取“STREAK_COUNT”的系列并转换为tuple,索引为第一个值,“STREAK_COUNT”为第二个,使用enumerate。由于enumerate 是一个可迭代对象,我们可以在步骤(3) 中直接使用它,而不是转换为列表。

2) 定义getTotal()函数并对(1)中的每个值独立处理,生成对应的“RESULT”值。

3) 将getTotal() 映射到步骤(1) 中生成的整个“STREAK_COUNT”枚举对象以获得所需的“RESULT”列。

工作代码

def getTotal(x):        
        pos = [i for i in range(x[0],x[0]-x[1],-1)]
        total = sum(df.iloc[pos, 0])
        func = lambda x : x if x !=0 else ''
        return func(total)


df[ "RESULT" ] = map(lambda x: getTotal(x), enumerate(df["STREAK_COUNT"]))
print df

旧版本

效率不高,因为我们需要遍历行和列,但适用于 STREAK_COUNT 内的任何值,并且更易于理解。

示例代码

import pandas as pd

df = pd.read_csv("sample.csv", index_col = 0)

#iterate over rows with index
for idx, (lab, row) in  enumerate(df.iterrows()):
        #get current STREAK_COUNT value
    count = int(df.iloc[idx, 1])

    #get previous positions based on count
    pos = [i for i in range(idx,idx-count,-1)]

    #count total
    total = sum(df.iloc[pos, 0])

    #create new column value based on total
    func = lambda x : x if x !=0 else ''
    df.loc[lab, "RESULT"] = func(total)

print df

结果

Python 2.7.9 (default, Dec 10 2014, 12:24:55) [MSC v.1500 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> ================================ RESTART ================================
>>> 
           MON TOTAL  STREAK_COUNT    RESULT
1/2/1992    1.123077             1   1.12308
2/3/1992   -1.296718             0          
3/2/1992   -6.355612             2  -7.65233
4/1/1992    5.634692             0          
5/1/1992    4.180605             2    9.8153
7/1/1992   -0.101016             0          
8/3/1992   -0.706125             2 -0.807141
10/1/1992   0.368579             0          
11/2/1992   3.822277             0          
1/4/1993    2.233359             0          
2/1/1993   15.219644             4   21.6439
3/1/1993   -2.647693             1  -2.64769
4/1/1993    1.599094             1   1.59909
>>> 

【讨论】:

  • @jakevdp 有没有办法让你编写代码,如果它不是连胜的结束,则显示一个空白行。因此,如果列中有 0,那么您将不会在 WANTED RESULT 列中显示任何内容(或 NaN)。让我知道这是否可能。
  • @J Westwood,请查看我的更新,因为它现在可以直接作用于整个列,从而达到预期的效果。
猜你喜欢
  • 1970-01-01
  • 2014-10-24
  • 1970-01-01
  • 2014-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多