从其他数据帧创建新的 pandas 时间序列数据帧

Create new pandas timeseries dataframe from other dataframe

如何从一个现有的 df.dataframe 创建一个新的 pandas 时间序列数据框。

假设事件 A 于 11/28 11:35 开始并于 11/29 19:53 结束,这是计数 1。事件 A 第二个实例再次于 11/28 11:37 开始并于 11/ 结束29 19:53 - 计算另一个 1。所以我将 A 的值增加到 2。(抱歉,数据输入错误地是 11/28 而不是 11/29)

源 df 给出了事件的开始和结束时间。并且同一事件可以同时发生多次。 新的 df 应该有给定分钟内事件累计计数的时间序列,范围从最小(开始时间)到最大(结束时间)。

来源 Df:

Start-Time       |  End-Time         | Event
11/28/2014 11:35 |  11/29/2014 19:53 | A
11/28/2014 11:36 |  11/28/2014 11:37 | B
11/28/2014 11:32 |  11/28/2014 19:53 | C
11/28/2014 11:37 |  11/28/2014 19:53 | A
......

新DF:

TimeStamp        | A |  B | C
11/28/2014 11:35 | 1 |  0 | 1
11/28/2014 11:36 | 1 |  1 | 1
11/28/2014 11:37 | 2 |  1 | 1
.....
11/29/2014 19:53 | 2 |  0 | 1

这有点棘手,因为您希望结束时间算作 "on" 状态,但我认为这样的事情应该可行(警告:考虑到奇怪的边缘情况,我花了零时间,所以买家要当心):

df = pd.melt(df, id_vars="Event", var_name="Which", value_name="Time")
df["Signal"] = df.pop("Which").replace({"Start-Time": 1, "End-Time": -1})
pivoted = df.pivot(columns="Event", index="Time").fillna(0)
pivoted = pivoted.sort_index() # just in case; can't remember if this is guaranteed
df_out = pivoted.cumsum() + (pivoted == -1)

产生

>>> df_out
                 Signal      
Event                 A  B  C
Time                         
11/28/2014 11:32      0  0  1
11/28/2014 11:35      1  0  1
11/28/2014 11:36      1  1  1
11/28/2014 11:37      2  1  1
11/28/2014 19:53      2  0  1
11/29/2014 19:53      1  0  0

基本思路是添加一个带符号的 "Signal" 列并使用它来跟踪更改:

>>> df
  Event              Time  Signal
0     A  11/28/2014 11:35       1
1     B  11/28/2014 11:36       1
2     C  11/28/2014 11:32       1
3     A  11/28/2014 11:37       1
4     A  11/29/2014 19:53      -1
5     B  11/28/2014 11:37      -1
6     C  11/28/2014 19:53      -1
7     A  11/28/2014 19:53      -1

然后我们可以旋转它来获得状态变化:

>>> pivoted
                 Signal      
Event                 A  B  C
Time                         
11/28/2014 11:32      0  0  1
11/28/2014 11:35      1  0  0
11/28/2014 11:36      0  1  0
11/28/2014 11:37      1 -1  0
11/28/2014 19:53     -1  0 -1
11/29/2014 19:53     -1  0  0

并累加得到状态:

>>> pivoted.cumsum()
                 Signal      
Event                 A  B  C
Time                         
11/28/2014 11:32      0  0  1
11/28/2014 11:35      1  0  1
11/28/2014 11:36      1  1  1
11/28/2014 11:37      2  0  1
11/28/2014 19:53      1  0  0
11/29/2014 19:53      0  0  0

这几乎是我们想要的,但您希望包括结束时间,因此我们可以通过撤消关闭来延迟效果:

>>> pivoted.cumsum() + (pivoted == -1)
                 Signal      
Event                 A  B  C
Time                         
11/28/2014 11:32      0  0  1
11/28/2014 11:35      1  0  1
11/28/2014 11:36      1  1  1
11/28/2014 11:37      2  1  1
11/28/2014 19:53      2  0  1
11/29/2014 19:53      1  0  0

这里的方法与@DSM 的方法略有不同。我将 startend 列堆叠在一起,然后在 length 上使用 groupbyaggregate 函数进行过滤。然后为了达到想要的输出效果我pivottable。

start = [35, 36, 37, 36, 35]
end = [56, 56, 56, 58, 58]
events = ['A', 'B', 'C', 'A', 'A']

df = pd.DataFrame( {'start': start, 'end': end, 'events': events})

# stack the 'start' and 'end' columns here
new_df = pd.DataFrame({ 'times': df['start'].append(df['end']), 'events': df['events'].append(df['events']) })

new_df = new_df.groupby(['times', 'events']).agg(len)

# massage the data frame to conform to desired output
new_df = new_df.reset_index().pivot('times', 'events').fillna(0)

连接后的数据框如下所示:

  events  times
0      A     35
1      B     36
2      C     37
3      A     36
4      A     35
0      A     56
1      B     56
2      C     56
3      A     58
4      A     58

groupby分组后的数据框:

times  events
35     A         2
36     A         1
       B         1
37     C         1
56     A         1
       B         1
       C         1
58     A         2

最后是枢轴后的数据框:

events  A  B  C
times          
35      2  0  0
36      1  1  0
37      0  0  1
56      1  1  1
58      2  0  0

我认为@DSM 的解决方案在计算时间方面比我的解决方案更有效,因为 append 方法相当昂贵,因为它需要在每次调用时构建一个全新的对象。我还没有对这两种方法进行计时,所以我不确定。