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
并使用 where
到 NaN
跨组的行。转变还确保 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
的本地序列中进一步按 A
对 GroupBy.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 列 year
和 income
分组,您可以使用:
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']
相反。
在下面的数据框中,我想创建一个新列 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
并使用 where
到 NaN
跨组的行。转变还确保 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
的本地序列中进一步按 A
对 GroupBy.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 列 year
和 income
分组,您可以使用:
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']
相反。