Pandas - 自上次值更改以来的连续计数
Pandas - count streak since last value change
我想获取特定列中值更改后的连续记录:
datetime val1 val2 val1-streak val2-streak
2018-04-01 00:00:00 4 1 0 0
2018-05-01 00:00:00 5 2 0 0
2018-06-01 00:00:00 5 2 1 1
2018-07-01 00:00:00 6 2 0 2
2018-08-01 00:00:00 7 2 0 3
2018-09-01 00:00:00 7 3 1 0
2018-10-01 00:00:00 7 3 2 1
2018-11-01 00:00:00 5 2 0 0
现在可以假设 dattime 列是等间距的。这样我就可以计算“周期”,因为目标列中的值发生了变化。理想情况下,该函数还可以计算自上次更改值以来 datetinme 的差异。
我在这个网站上找到了一些解决方案,但当我仔细查看时,它们并没有真正描述我的问题:
https://joshdevlin.com/blog/calculate-streaks-in-pandas/
.....至少我无法将给定的答案转移到我的问题上。
让我们从如何使用单列开始。首先我们需要找到值变化的每个点:
diffs = df['val1'].diff(1)
change_points = diffs != 0
由于以前遇到的值可以再次出现,我们需要找到一种正确的方法来区分相同值的不同条纹。我们将通过更改点数组的累加和来实现。为方便起见,我们将创建一个临时 DataFrame 来保存这些结果(您可以在 DataFrame 中创建一个新列,但这有点混乱)
change_points_cumsum = change_points.cumsum()
tmp_merged = pd.concat([serie, change_points_cumsum], axis=1, keys=['val1', 'change_points_cumsum'])
每个连胜都分配了不同的 change_points_cumsum
值,因此现在可以应用简单的分组依据和累积计数来获得最终结果
tmp_merged["val1-streak"] = tmp_merged.groupby(change_points_cumsum).cumcount()
print(tmp_merged)
val1 change_points_cumsum val1-streak
0 4 1 0
1 5 2 0
2 5 2 1
3 6 3 0
4 7 4 0
5 7 4 1
6 7 4 2
7 5 5 0
如果您正在寻找更短、更紧凑的解决方案
change_points_cumsum = df['val1'].diff(1).ne(0).cumsum()
change_points_cumsum.groupby(change_points_cumsum).cumcount()
0 0
1 0
2 1
3 0
4 0
5 1
6 2
7 0
让我们尝试以下操作:
(i) 使用 set_index
+ unstack
创建堆叠系列(这是为了不在两列上分别重复相同的功能)。
(ii) 使用groupby
+ diff
找出连续元素之间的差异:tmp
(iii) 我们对差异为 0 的位置感兴趣;我们再做一次 groupby
+ cumsum
以获得预期的结果。
(iv) 使用 pivot
将输出恢复为 df
.
的形状
tmp = df.set_index('datetime')[['val1','val2']].unstack().groupby(level=0).diff()
df[['val1-streak','val2-streak']] = pd.pivot(tmp.eq(0)
.groupby([tmp.index.get_level_values(0),
tmp.ne(0).cumsum()])
.cumsum()
.reset_index(),
'datetime', 'level_0', 0).to_numpy()
输出:
datetime val1 val2 val1-streak val2-streak
0 2018-04-01 00:00:00 4 1 0 0
1 2018-05-01 00:00:00 5 2 0 0
2 2018-06-01 00:00:00 5 2 1 1
3 2018-07-01 00:00:00 6 2 0 2
4 2018-08-01 00:00:00 7 2 0 3
5 2018-09-01 00:00:00 7 3 1 0
6 2018-10-01 00:00:00 7 3 2 1
7 2018-11-01 00:00:00 5 2 0 0
通过比较 Series.diff
, compare for not equal by Series.ne
with cumulative sum, last pass to GroupBy.cumcount
列表中指定的每列的差异,使用自定义函数按连续值生成计数器:
vals = ['val1','val2']
def f(x):
x = x.diff().ne(0).cumsum()
return x.groupby(x).cumcount()
df = df.join(df[vals].apply(f).add_suffix('_streak'))
我想获取特定列中值更改后的连续记录:
datetime val1 val2 val1-streak val2-streak
2018-04-01 00:00:00 4 1 0 0
2018-05-01 00:00:00 5 2 0 0
2018-06-01 00:00:00 5 2 1 1
2018-07-01 00:00:00 6 2 0 2
2018-08-01 00:00:00 7 2 0 3
2018-09-01 00:00:00 7 3 1 0
2018-10-01 00:00:00 7 3 2 1
2018-11-01 00:00:00 5 2 0 0
现在可以假设 dattime 列是等间距的。这样我就可以计算“周期”,因为目标列中的值发生了变化。理想情况下,该函数还可以计算自上次更改值以来 datetinme 的差异。
我在这个网站上找到了一些解决方案,但当我仔细查看时,它们并没有真正描述我的问题:
https://joshdevlin.com/blog/calculate-streaks-in-pandas/
.....至少我无法将给定的答案转移到我的问题上。
让我们从如何使用单列开始。首先我们需要找到值变化的每个点:
diffs = df['val1'].diff(1)
change_points = diffs != 0
由于以前遇到的值可以再次出现,我们需要找到一种正确的方法来区分相同值的不同条纹。我们将通过更改点数组的累加和来实现。为方便起见,我们将创建一个临时 DataFrame 来保存这些结果(您可以在 DataFrame 中创建一个新列,但这有点混乱)
change_points_cumsum = change_points.cumsum()
tmp_merged = pd.concat([serie, change_points_cumsum], axis=1, keys=['val1', 'change_points_cumsum'])
每个连胜都分配了不同的 change_points_cumsum
值,因此现在可以应用简单的分组依据和累积计数来获得最终结果
tmp_merged["val1-streak"] = tmp_merged.groupby(change_points_cumsum).cumcount()
print(tmp_merged)
val1 change_points_cumsum val1-streak
0 4 1 0
1 5 2 0
2 5 2 1
3 6 3 0
4 7 4 0
5 7 4 1
6 7 4 2
7 5 5 0
如果您正在寻找更短、更紧凑的解决方案
change_points_cumsum = df['val1'].diff(1).ne(0).cumsum()
change_points_cumsum.groupby(change_points_cumsum).cumcount()
0 0
1 0
2 1
3 0
4 0
5 1
6 2
7 0
让我们尝试以下操作:
(i) 使用 set_index
+ unstack
创建堆叠系列(这是为了不在两列上分别重复相同的功能)。
(ii) 使用groupby
+ diff
找出连续元素之间的差异:tmp
(iii) 我们对差异为 0 的位置感兴趣;我们再做一次 groupby
+ cumsum
以获得预期的结果。
(iv) 使用 pivot
将输出恢复为 df
.
tmp = df.set_index('datetime')[['val1','val2']].unstack().groupby(level=0).diff()
df[['val1-streak','val2-streak']] = pd.pivot(tmp.eq(0)
.groupby([tmp.index.get_level_values(0),
tmp.ne(0).cumsum()])
.cumsum()
.reset_index(),
'datetime', 'level_0', 0).to_numpy()
输出:
datetime val1 val2 val1-streak val2-streak
0 2018-04-01 00:00:00 4 1 0 0
1 2018-05-01 00:00:00 5 2 0 0
2 2018-06-01 00:00:00 5 2 1 1
3 2018-07-01 00:00:00 6 2 0 2
4 2018-08-01 00:00:00 7 2 0 3
5 2018-09-01 00:00:00 7 3 1 0
6 2018-10-01 00:00:00 7 3 2 1
7 2018-11-01 00:00:00 5 2 0 0
通过比较 Series.diff
, compare for not equal by Series.ne
with cumulative sum, last pass to GroupBy.cumcount
列表中指定的每列的差异,使用自定义函数按连续值生成计数器:
vals = ['val1','val2']
def f(x):
x = x.diff().ne(0).cumsum()
return x.groupby(x).cumcount()
df = df.join(df[vals].apply(f).add_suffix('_streak'))