pandas 中的累积总和,以零开始,以除最后一个条目以外的所有相关组的总和结束

Cumulative sum in pandas starting with a zero and ending with the sum of all but the last entry respecting groups

在下面的数据框中,我想创建一个新列 C,它将是 B 中每个组的 累积总和 A 列,但这些总和必须从零开始并且只添加值直到该组的倒数第二个条目。

  A B
0 1 5
1 1 6
2 2 3
3 2 4
4 2 5
5 3 2
5 3 7
6 4 3

所以,我的结果应该是:

  A B C
0 1 5 0
1 1 6 5
2 2 3 0
3 2 4 3
4 2 5 7
5 3 2 0
5 3 7 2
6 4 3 0

(我认为这个问题真的很明显,但不知何故我自己弄不明白,也看不到已经问过的任何地方。)

尝试:

df["C"] = df.groupby("A")["B"].transform(
    lambda x: x.shift().fillna(0).cumsum().astype(int)
)
print(df)

打印:

   A  B  C
0  1  5  0
1  1  6  5
2  2  3  0
3  2  4  3
4  2  5  7
5  3  2  0
5  3  7  2
6  4  3  0

鉴于您的组已经连续,您可以 shift 并使用 whereNaN 跨组的行。转变还确保 cumsum 相对于先前的行和统计数据为 0,如您所愿。然后使用内置的 groupby + cumsum 来避免较慢的 lambda.

s = df['A'].shift()

df['C'] = (df.shift()
             .where(df['A'].eq(s))
             .groupby('A')['B'].cumsum()
             .fillna(0, downcast='infer'))


#   A  B  C
#0  1  5  0
#1  1  6  5
#2  2  3  0
#3  2  4  3
#4  2  5  7
#5  3  2  0
#5  3  7  2
#6  4  3  0

如果发现分组行不连续,我们也可以处理。唯一的小修改是我们需要根据分组列进行稳定排序(这样组内的顺序被保留并且 cumsum 是正确的)然后最后我们可以对索引进行排序以返回到DataFrame 的原始顺序,如果这很重要的话。

# Create DataFrame with non-consecutive groups, 
df = pd.concat([df[::2], df[1::2]], ignore_index=True)
#   A  B
#0  1  5
#1  2  3
#2  2  5
#3  3  7
#4  1  6
#5  2  4
#6  3  2
#7  4  3

df = df.sort_values('A', kind='mergesort')

s = df['A'].shift()

df['C'] = (df.shift()
             .where(df['A'].eq(s))
             .groupby('A')['B'].cumsum()
             .fillna(0, downcast='infer'))

df = df.sort_index()
#   A  B  C
#0  1  5  0
#1  2  3  0
#2  2  5  3  <- Previous row `1` has value 3
#3  3  7  0
#4  1  6  5  <- Previous row `0` has value 5
#5  2  4  8  <- Previous rows `1` and `2` has values 3 + 5 = 8
#6  3  2  7  <- Previous row `3` has value 7 
#7  4  3  0

另一种选择是使用.groupby()两次,如下:

DataFrameGroupBy.shift() value of B under A so that for each group of A, the first entry of B will be reset and become NaN for later .fillna()0

A 的本地序列中进一步按 AGroupBy.cumsum() 进行分组以获得所需的输出:

df['C'] = (df.groupby('A')['B'].shift()
             .groupby(df['A']).cumsum()
             .fillna(0, downcast='infer')
          )

此解决方案向量化并且支持非连续组

结果:

print(df)


   A  B  C
0  1  5  0
1  1  6  5
2  2  3  0
3  2  4  3
4  2  5  7
5  3  2  0
5  3  7  2
6  4  3  0

编辑

如果您要对多列进行分组并出现“KeyError”,请检查您的语法是否输入正确,例如:

如果按 2 列 yearincome 分组,您可以使用:

df['C'] = (df.groupby(['year', 'income'])['B'].shift()
             .groupby([df['year'], df['income']]).cumsum()
             .fillna(0, downcast='infer')
          )

Pandas 支持带或不带引号的语法 df 将参数传递给 .groupby()。但是,对于任何 groupby() 被分组的实体不是 df 本身,我们可能无法使用缩写形式仅引用列标签,例如'year',我们需要使用完整的列名,例如df['year'] 相反。