一组条纹(扭曲)
Group streaks of ones (with a twist)
我已阅读相关问题,例如 and this blog post。
很遗憾,我无法根据需要修改解决方案。
考虑一个带有 DatetimeIndex 的系列,它可能看起来像这样:
实例化示例的代码:
s = pd.Series([0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0], index=pd.date_range(start=0, freq='1d', periods=12, name='A')
最终,我想得到结果
(t4 - t1)
+ (t8 - t5)
+ (t10 - t8)
这意味着 我需要识别每边用 0
填充的 1
的条纹 。之后我可以自己做所有事情,即按连胜分组(可能使用 cumcount
)并区分每组中的第一个和最后一个时间戳。
有一些特殊情况,当系列 starts/ends 带有 1
时。在这种情况下,我想将其视为 preceded/followed 由 0
在同一时间戳,例如
目前尝试次数:
我将合并一些子解决方案以便于可视化。
在每一端用零填充系列,以避免出现特殊情况。
s = pd.Series([0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0], index=pd.date_range(start=0, freq='1d', periods=12, name='A')
s = pd.concat([pd.Series([0], index=s.index[:1]), s, pd.Series([0], index=s.index[-1:])])
获得一连串之前的最后一个 0
和之后的第一个 0
。
>>> tmp = pd.concat([s, s.diff(-1).eq(-1).astype(int).rename('starter'), s.diff(1).eq(-1).astype(int).rename('ender')], axis=1)
>>> tmp
A starter ender
1970-01-01 0 0 0
1970-01-02 0 1 0
1970-01-03 1 0 0
1970-01-04 1 0 0
1970-01-05 0 0 1
1970-01-06 0 1 0
1970-01-07 1 0 0
1970-01-08 1 0 0
1970-01-09 0 1 1
1970-01-10 1 0 0
1970-01-11 0 0 1
1970-01-12 0 0 0
用 1
填充 'A'
列中的单个零间隙,因为它们不会改变所需的结果。 (此步骤可能不是必需的,但有助于可视化。)
>>> tmp.loc[(both := tmp['starter'].eq(1) & tmp['ender'].eq(1)), 'A'] = 1
>>> tmp
A starter ender
1970-01-01 0 0 0
1970-01-02 0 1 0
1970-01-03 1 0 0
1970-01-04 1 0 0
1970-01-05 0 0 1
1970-01-06 0 1 0
1970-01-07 1 0 0
1970-01-08 1 0 0
1970-01-09 1 1 1
1970-01-10 1 0 0
1970-01-11 0 0 1
1970-01-12 0 0 0
调整 'starter'
和 'ender'
列。
>>> tmp.loc[both, ['starter', 'ender']] = 0
>>> tmp
A starter ender
1970-01-01 0 0 0
1970-01-02 0 1 0
1970-01-03 1 0 0
1970-01-04 1 0 0
1970-01-05 0 0 1
1970-01-06 0 1 0
1970-01-07 1 0 0
1970-01-08 1 0 0
1970-01-09 1 0 0
1970-01-10 1 0 0
1970-01-11 0 0 1
1970-01-12 0 0 0
这就是我卡住的地方。
这是您的问题的解决方案:
>>> s = pd.Series([0, 1, 1, 0, 1, 0], index=pd.date_range(start=0, freq='1d', periods=6))
1970-01-01 0
1970-01-02 1
1970-01-03 1
1970-01-04 0
1970-01-05 1
1970-01-06 0
>>> s2 = s.diff(1)
1970-01-01 NaN
1970-01-02 1.0
1970-01-03 0.0
1970-01-04 -1.0
1970-01-05 1.0
1970-01-06 -1.0
>>> s3 = s2.loc[s == 0].reset_index(name="d0")
index d0
0 1970-01-01 NaN
1 1970-01-04 -1.0
2 1970-01-06 -1.0
>>> s4 = s3["index"].diff(1).loc[s3["d0"] == -1]
1 3 days
2 2 days
>>> s4.sum().days
5
综合起来,
s.diff(1) \
.loc[s == 0] \
.reset_index(name="val_diff") \
.assign(date_diff=lambda x: x["index"].diff(1)) \
.loc[lambda x: x.val_diff == -1] \
.date_diff.sum()
对于特殊情况,如果它们以值 = 1 开始或结束,则将额外的行添加到数据框的顶部和底部。类似于:
if s.iloc[0] == 1:
s = pd.concat([
pd.Series([0], index=s.index[:1]),
s
])
我几乎完成了问题的尝试,这是完成:
result = (s.index[s.diff(1).eq(-1)] - s.index[s.diff(-1).eq(-1)]).sum()
假设 s
以零开始和结束。否则先用零填充。
我已阅读相关问题,例如
很遗憾,我无法根据需要修改解决方案。
考虑一个带有 DatetimeIndex 的系列,它可能看起来像这样:
实例化示例的代码:
s = pd.Series([0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0], index=pd.date_range(start=0, freq='1d', periods=12, name='A')
最终,我想得到结果
(t4 - t1)
+ (t8 - t5)
+ (t10 - t8)
这意味着 我需要识别每边用 0
填充的 1
的条纹 。之后我可以自己做所有事情,即按连胜分组(可能使用 cumcount
)并区分每组中的第一个和最后一个时间戳。
有一些特殊情况,当系列 starts/ends 带有 1
时。在这种情况下,我想将其视为 preceded/followed 由 0
在同一时间戳,例如
目前尝试次数:
我将合并一些子解决方案以便于可视化。
在每一端用零填充系列,以避免出现特殊情况。
s = pd.Series([0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0], index=pd.date_range(start=0, freq='1d', periods=12, name='A') s = pd.concat([pd.Series([0], index=s.index[:1]), s, pd.Series([0], index=s.index[-1:])])
获得一连串之前的最后一个
0
和之后的第一个0
。>>> tmp = pd.concat([s, s.diff(-1).eq(-1).astype(int).rename('starter'), s.diff(1).eq(-1).astype(int).rename('ender')], axis=1) >>> tmp A starter ender 1970-01-01 0 0 0 1970-01-02 0 1 0 1970-01-03 1 0 0 1970-01-04 1 0 0 1970-01-05 0 0 1 1970-01-06 0 1 0 1970-01-07 1 0 0 1970-01-08 1 0 0 1970-01-09 0 1 1 1970-01-10 1 0 0 1970-01-11 0 0 1 1970-01-12 0 0 0
用
1
填充'A'
列中的单个零间隙,因为它们不会改变所需的结果。 (此步骤可能不是必需的,但有助于可视化。)>>> tmp.loc[(both := tmp['starter'].eq(1) & tmp['ender'].eq(1)), 'A'] = 1 >>> tmp A starter ender 1970-01-01 0 0 0 1970-01-02 0 1 0 1970-01-03 1 0 0 1970-01-04 1 0 0 1970-01-05 0 0 1 1970-01-06 0 1 0 1970-01-07 1 0 0 1970-01-08 1 0 0 1970-01-09 1 1 1 1970-01-10 1 0 0 1970-01-11 0 0 1 1970-01-12 0 0 0
调整
'starter'
和'ender'
列。>>> tmp.loc[both, ['starter', 'ender']] = 0 >>> tmp A starter ender 1970-01-01 0 0 0 1970-01-02 0 1 0 1970-01-03 1 0 0 1970-01-04 1 0 0 1970-01-05 0 0 1 1970-01-06 0 1 0 1970-01-07 1 0 0 1970-01-08 1 0 0 1970-01-09 1 0 0 1970-01-10 1 0 0 1970-01-11 0 0 1 1970-01-12 0 0 0
这就是我卡住的地方。
这是您的问题的解决方案:
>>> s = pd.Series([0, 1, 1, 0, 1, 0], index=pd.date_range(start=0, freq='1d', periods=6))
1970-01-01 0
1970-01-02 1
1970-01-03 1
1970-01-04 0
1970-01-05 1
1970-01-06 0
>>> s2 = s.diff(1)
1970-01-01 NaN
1970-01-02 1.0
1970-01-03 0.0
1970-01-04 -1.0
1970-01-05 1.0
1970-01-06 -1.0
>>> s3 = s2.loc[s == 0].reset_index(name="d0")
index d0
0 1970-01-01 NaN
1 1970-01-04 -1.0
2 1970-01-06 -1.0
>>> s4 = s3["index"].diff(1).loc[s3["d0"] == -1]
1 3 days
2 2 days
>>> s4.sum().days
5
综合起来,
s.diff(1) \
.loc[s == 0] \
.reset_index(name="val_diff") \
.assign(date_diff=lambda x: x["index"].diff(1)) \
.loc[lambda x: x.val_diff == -1] \
.date_diff.sum()
对于特殊情况,如果它们以值 = 1 开始或结束,则将额外的行添加到数据框的顶部和底部。类似于:
if s.iloc[0] == 1:
s = pd.concat([
pd.Series([0], index=s.index[:1]),
s
])
我几乎完成了问题的尝试,这是完成:
result = (s.index[s.diff(1).eq(-1)] - s.index[s.diff(-1).eq(-1)]).sum()
假设 s
以零开始和结束。否则先用零填充。