【问题标题】:Pandas Datetime index: Number of current events over timePandas 日期时间索引:随着时间的推移当前事件的数量
【发布时间】:2020-07-07 05:36:49
【问题描述】:

我正在分析一组事件,每个事件都有一个类型、开始和结束时间戳。 我正在尝试总结在该时间范围内正在进行的每个事件时间的并发数。

考虑下面的数据集,列出事件 N1-N4,每个事件都有重叠范围:

>>> data = {
...    'name' : [ 'N1', 'N2', 'N3', 'N4', 'N1',  'N2', 'N7'],
...    'start_dt_str' : ['01-01-2020', '01-03-2020', '01-01-2020', '01-01-2020', '01-03-2020', '01-04-2020','01-10-2020'],
...    'end_dt_str' : ['01-03-2020', '01-05-2020', '01-05-2020', '01-02-2020', '01-04-2020', '01-05-2020', '01-11-2020']
... }
>>> df = pd.DataFrame(data)
>>> df['start_dt'] = pd.to_datetime(df['start_dt_str'])
>>> df['end_dt'] = pd.to_datetime(df['end_dt_str'])
>>> del df['start_dt_str']
>>> del df['end_dt_str']
>>> df 
  name   start_dt     end_dt
0   N1 2020-01-01 2020-01-03
1   N2 2020-01-03 2020-01-05
2   N3 2020-01-01 2020-01-05
3   N4 2020-01-01 2020-01-02
4   N1 2020-01-03 2020-01-04
5   N2 2020-01-04 2020-01-05
6   N7 2020-01-10 2020-01-11

我的目标是生成此摘要,即范围内每个日期的并发事件数(按类型)。这将是正确的答案:

               N1 N2 N3 N4 N7
2020-01-01     1  0  1  1  0
2020-01-02     1  0  1  1  0 
2020-01-03     2  1  1  0  0
2020-01-04     1  2  1  0  0
2020-01-05     1  2  0  0  0
2020-01-06     0  0  0  0  0
2020-01-07     0  0  0  0  0
2020-01-08     0  0  0  0  0
2020-01-09     0  0  0  0  0
2020-01-10     0  0  0  0  1
2020-01-11     0  0  0  0  1

请注意,start_dt 和 end_dt 列中都有重复的日期。

另请注意,该解决方案必须能够对数据重新采样,以便用包含全零的行填充缺失的日期。在此示例中,日期 01-09 不会显示为开始日期或结束日期,但必须出现在输出中。在一般情况下,我希望能够进行重新采样以选择任意间隔。

为简单说明问题,上述数据集中的报告期和数据均采用天精度。在实际数据集中,start_dt 和 end_dt 是毫秒级精度(但仍然包含重复),报告周期可以是小时、天、周等。

另请注意,数据中存在差距,因此需要重新采样以生成日期时间序列。 (即,即使数据是毫秒精度,也有一整天的缺失)。

我尝试了几种不起作用的方法。首先,这似乎很简单,我尝试了:

df.set_index(['name','start_dt']).groupby('name').resample('D',level='start_dt').ffill()

ValueError: Upsampling from level= or on= selection is not supported, use .set_index(...) to explicitly set index to datetime-like

这导致this pandas issue 关于上采样是开放的,并提供了一些解决方法。不幸的是,我们不能只使用 start_dt(或 end_dt)作为索引,因为它是非唯一的:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/dcowden/envs/analysis-env/lib/python3.6/site-packages/pandas/core/resample.py", line 453, in pad
    return self._upsample("pad", limit=limit)
  File "/home/dcowden/envs/analysis-env/lib/python3.6/site-packages/pandas/core/resample.py", line 1095, in _upsample
    res_index, method=method, limit=limit, fill_value=fill_value
  File "/home/dcowden/envs/analysis-env/lib/python3.6/site-packages/pandas/util/_decorators.py", line 227, in wrapper
    return func(*args, **kwargs)
  File "/home/dcowden/envs/analysis-env/lib/python3.6/site-packages/pandas/core/frame.py", line 3856, in reindex
    return super().reindex(**kwargs)
  File "/home/dcowden/envs/analysis-env/lib/python3.6/site-packages/pandas/core/generic.py", line 4544, in reindex
    axes, level, limit, tolerance, method, fill_value, copy
  File "/home/dcowden/envs/analysis-env/lib/python3.6/site-packages/pandas/core/frame.py", line 3744, in _reindex_axes
    index, method, copy, level, fill_value, limit, tolerance
  File "/home/dcowden/envs/analysis-env/lib/python3.6/site-packages/pandas/core/frame.py", line 3760, in _reindex_index
    new_index, method=method, level=level, limit=limit, tolerance=tolerance
  File "/home/dcowden/envs/analysis-env/lib/python3.6/site-packages/pandas/core/indexes/base.py", line 3149, in reindex
    "cannot reindex a non-unique index "
ValueError: cannot reindex a non-unique index with a method or limit

This question 这似乎与我的问题相似,但并未填写每种事件类型范围内的所有日期:

>>> df.set_index('start_dt').groupby('name').resample('D').asfreq()
                name     end_dt
name start_dt                  
N1   2020-01-01   N1 2020-01-03
     2020-01-02  NaN        NaT
     2020-01-03   N1 2020-01-04
N2   2020-01-03   N2 2020-01-05
     2020-01-04   N2 2020-01-05
N3   2020-01-01   N3 2020-01-05
N4   2020-01-01   N4 2020-01-02

This solution 看起来很有希望,但也不是我所需要的。它本质上是在一个范围内查找单个事件,但不计算正在进行的总数。虽然使用 IntervalIndex 似乎是一个好的开始。

我觉得这应该很容易,但显然我的 pandas foo 严重不足。

非常感谢您的帮助!

编辑:

【问题讨论】:

    标签: python pandas datetime


    【解决方案1】:

    想法是将每个date_range 的值重复到助手DataFrame,然后将SeriesGroupBy.value_countsSeries.unstack 一起使用:

    L = [pd.Series(r.name, pd.date_range(r.start_dt, r.end_dt)) for r in df.itertuples()]
    s = pd.concat(L)
    
    df1 = s.groupby(level=0).value_counts().unstack(fill_value=0)
    print (df1)
                N1  N2  N3  N4
    2020-01-01   1   0   1   1
    2020-01-02   1   0   1   1
    2020-01-03   2   1   1   0
    2020-01-04   1   2   1   0
    2020-01-05   0   2   1   0
    

    另一种通过DataFrame.melt 重塑的解决方案,但首先必须通过Series.shiftSeries.cumsum 技巧区分连续值,然后使用DataFrameGroupBy.resample 和最后一个crosstab

    df['g'] = df['name'].ne(df['name'].shift()).cumsum()
    df1 = (df.melt(['name','g'])
             .set_index('value')
             .groupby(['g','name'])['variable']
             .resample('d')
             .first()
             .reset_index())
    
    df1 = pd.crosstab(df1['value'], df1['name'])
    print (df1)
    name        N1  N2  N3  N4
    value                     
    2020-01-01   1   0   1   1
    2020-01-02   1   0   1   1
    2020-01-03   2   1   1   0
    2020-01-04   1   2   1   0
    2020-01-05   0   2   1   0
    

    【讨论】:

    • 这非常接近,但没有为原始系列中未表示为开始或结束日期的日期提供输出。但是,只需添加 df1.resample('D').asfreq() 即可产生正确的结果。那非常接近,所以我认为这已经足够接近答案了。感谢您的帮助!
    猜你喜欢
    • 2012-11-25
    • 2019-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-07
    • 1970-01-01
    相关资源
    最近更新 更多