Pandas - 连续的累计和
Pandas - Cumulative sum of consecutive ones
我有一个这样的数据框:
Name_A ¦ date1 ¦ 1
Name_A ¦ date2 ¦ 0
Name_A ¦ date3 ¦ 1
Name_A ¦ date4 ¦ 1
Name_A ¦ date5 ¦ 1
Name_B ¦ date6 ¦ 1
Name_B ¦ date7 ¦ 1
Name_B ¦ date8 ¦ 0
Name_B ¦ date9 ¦ 1
我想得到这个:
Name_A ¦ date1 ¦ 1
Name_A ¦ date2 ¦ 0
Name_A ¦ date3 ¦ 1
Name_A ¦ date4 ¦ 2
Name_A ¦ date5 ¦ 3
Name_B ¦ date6 ¦ 1
Name_B ¦ date7 ¦ 2
Name_B ¦ date8 ¦ 0
Name_B ¦ date9 ¦ 1
基本上我想得到连续1的累加和。如果名字变了或者出现了0,就应该重新从0开始计数。
有ideas/suggestions吗?谢谢
我像这样重建了你的数据:
import pandas as pd
df = pd.DataFrame(
{'col1': ['Name_A'] * 5 + ['Name_B'] * 4,
'col2': ['date{}'.format(x) for x in list(range(1,10,1))],
'col3': [1,0,1,1,1,1,1,0,1]})
对于您建议的那种分组,我喜欢使用 itertools.groupby
而不是 pd.groupby
,这样我就可以明确说明您指定的两个条件(名称更改和值为 0列):
from itertools import groupby
groups = []
uniquekeys = []
for k, g in groupby(df.iterrows(),
lambda row: (row[1]['col1'], row[1]['col3'] == 0)):
groups.append(list(g))
uniquekeys.append(k)
现在正确的组已经存在,剩下的就是迭代然后计算累计和:
cumsum = pd.concat([pd.Series([y[1]['col3'] for y in x]).cumsum() for x in groups])
df['cumsum'] = list(cumsum)
结果:
col1 col2 col3 cumsum
0 Name_A date1 1 1
1 Name_A date2 0 0
2 Name_A date3 1 1
3 Name_A date4 1 2
4 Name_A date5 1 3
5 Name_B date6 1 1
6 Name_B date7 1 2
7 Name_B date8 0 0
8 Name_B date9 1 1
作为参考,请参阅有关 itertools.groupby
here 的详细解释。
这是一个不需要显式循环的矢量化解决方案:
df = pd.DataFrame.from_dict({'name': list('AAAAABBBB'), 'bit': (1,0,1,1,1,1,1,0,1)})
>>> df
bit name
0 1 A
1 0 A
2 1 A
3 1 A
4 1 A
5 1 B
6 1 B
7 0 B
8 1 B
>>> reset = (df['bit'] == 0) | (df['name'] != df['name'].shift(1))
>>> reset, = np.where(np.concatenate([reset, [True]]))
>>> df['count'] = np.arange(reset[-1]) + (df['bit'].values[reset[:-1]]-reset[:-1]).repeat(np.diff(reset))
>>> df
bit name count
0 1 A 1
1 0 A 0
2 1 A 1
3 1 A 2
4 1 A 3
5 1 B 1
6 1 B 2
7 0 B 0
8 1 B 1
这是我自己的看法:
In [145]: group_ids = df[2].diff().ne(0).cumsum()
In [146]: df["count"] = df[2].groupby([df[0], group_ids]).cumsum()
In [147]: df
Out[147]:
0 1 2 count
0 Name_A date1 1 1
1 Name_A date2 0 0
2 Name_A date3 1 1
3 Name_A date4 1 2
4 Name_A date5 1 3
5 Name_B date6 1 1
6 Name_B date7 1 2
7 Name_B date8 0 0
8 Name_B date9 1 1
这使用 compare-cumsum-groupby 模式来查找连续的组,因为只要值与前一个值不同,df[2].diff().ne(0)
就会给我们一个 True,并且这些值的累积和给我们每当新的一组 1 开始时,一个新数字。
这意味着我们对于跨不同名称的二进制值具有相同的 group_id,当然,但是因为我们在 both df[0] 上分组(名字)和 group_ids,我们没事。
我有一个这样的数据框:
Name_A ¦ date1 ¦ 1
Name_A ¦ date2 ¦ 0
Name_A ¦ date3 ¦ 1
Name_A ¦ date4 ¦ 1
Name_A ¦ date5 ¦ 1
Name_B ¦ date6 ¦ 1
Name_B ¦ date7 ¦ 1
Name_B ¦ date8 ¦ 0
Name_B ¦ date9 ¦ 1
我想得到这个:
Name_A ¦ date1 ¦ 1
Name_A ¦ date2 ¦ 0
Name_A ¦ date3 ¦ 1
Name_A ¦ date4 ¦ 2
Name_A ¦ date5 ¦ 3
Name_B ¦ date6 ¦ 1
Name_B ¦ date7 ¦ 2
Name_B ¦ date8 ¦ 0
Name_B ¦ date9 ¦ 1
基本上我想得到连续1的累加和。如果名字变了或者出现了0,就应该重新从0开始计数。
有ideas/suggestions吗?谢谢
我像这样重建了你的数据:
import pandas as pd
df = pd.DataFrame(
{'col1': ['Name_A'] * 5 + ['Name_B'] * 4,
'col2': ['date{}'.format(x) for x in list(range(1,10,1))],
'col3': [1,0,1,1,1,1,1,0,1]})
对于您建议的那种分组,我喜欢使用 itertools.groupby
而不是 pd.groupby
,这样我就可以明确说明您指定的两个条件(名称更改和值为 0列):
from itertools import groupby
groups = []
uniquekeys = []
for k, g in groupby(df.iterrows(),
lambda row: (row[1]['col1'], row[1]['col3'] == 0)):
groups.append(list(g))
uniquekeys.append(k)
现在正确的组已经存在,剩下的就是迭代然后计算累计和:
cumsum = pd.concat([pd.Series([y[1]['col3'] for y in x]).cumsum() for x in groups])
df['cumsum'] = list(cumsum)
结果:
col1 col2 col3 cumsum
0 Name_A date1 1 1
1 Name_A date2 0 0
2 Name_A date3 1 1
3 Name_A date4 1 2
4 Name_A date5 1 3
5 Name_B date6 1 1
6 Name_B date7 1 2
7 Name_B date8 0 0
8 Name_B date9 1 1
作为参考,请参阅有关 itertools.groupby
here 的详细解释。
这是一个不需要显式循环的矢量化解决方案:
df = pd.DataFrame.from_dict({'name': list('AAAAABBBB'), 'bit': (1,0,1,1,1,1,1,0,1)})
>>> df
bit name
0 1 A
1 0 A
2 1 A
3 1 A
4 1 A
5 1 B
6 1 B
7 0 B
8 1 B
>>> reset = (df['bit'] == 0) | (df['name'] != df['name'].shift(1))
>>> reset, = np.where(np.concatenate([reset, [True]]))
>>> df['count'] = np.arange(reset[-1]) + (df['bit'].values[reset[:-1]]-reset[:-1]).repeat(np.diff(reset))
>>> df
bit name count
0 1 A 1
1 0 A 0
2 1 A 1
3 1 A 2
4 1 A 3
5 1 B 1
6 1 B 2
7 0 B 0
8 1 B 1
这是我自己的看法:
In [145]: group_ids = df[2].diff().ne(0).cumsum()
In [146]: df["count"] = df[2].groupby([df[0], group_ids]).cumsum()
In [147]: df
Out[147]:
0 1 2 count
0 Name_A date1 1 1
1 Name_A date2 0 0
2 Name_A date3 1 1
3 Name_A date4 1 2
4 Name_A date5 1 3
5 Name_B date6 1 1
6 Name_B date7 1 2
7 Name_B date8 0 0
8 Name_B date9 1 1
这使用 compare-cumsum-groupby 模式来查找连续的组,因为只要值与前一个值不同,df[2].diff().ne(0)
就会给我们一个 True,并且这些值的累积和给我们每当新的一组 1 开始时,一个新数字。
这意味着我们对于跨不同名称的二进制值具有相同的 group_id,当然,但是因为我们在 both df[0] 上分组(名字)和 group_ids,我们没事。