【问题标题】:pandas: join dataframes based on time intervalpandas:根据时间间隔加入数据帧
【发布时间】:2019-01-13 18:27:49
【问题描述】:

我有一个数据框,每 10 分钟有一个日期时间列和一个数值:

df1 = pd.DataFrame({'time' : pd.date_range('1/1/2018', periods=20, freq='10min'), 'value' : np.random.randint(2, 20, size=20)})

还有一个有活动时间表,有开始时间和结束时间。可以同时发生多个事件:

df2 = pd.DataFrame({'start_time' : ['2018-01-01 00:00:00', '2018-01-01 00:00:00','2018-01-01 01:00:00', '2018-01-01 01:00:00', '2018-01-01 01:00:00', '2018-01-01 02:00:00' ], 'end_time' : ['2018-01-01 01:00:00', '2018-01-01 01:00:00', '2018-01-01 02:00:00','2018-01-01 02:00:00', '2018-01-01 02:00:00', '2018-01-01 03:00:00'], 'event' : ['A', 'B', 'C', 'D', 'E', 'F'] })
df2[['start_time', 'end_time']] = df2.iloc[:,0:2].apply(pd.to_datetime)

我想在 df1 上进行左连接,所有事件都在开始时间和结束时间之内。我的输出表应该是:

                  time  value event
0  2018-01-01 00:00:00      5     A
1  2018-01-01 00:00:00      5     B
2  2018-01-01 00:10:00     15     A
3  2018-01-01 00:10:00     15     B
4  2018-01-01 00:20:00     16     A
5  2018-01-01 00:20:00     16     B
.....
17 2018-01-01 02:50:00      7     F

我尝试了these SO solutions,但由于重复的时间间隔而失败。

【问题讨论】:

  • 我不明白你从哪里得到前两个value=5
  • @user3483203 value 设置为随机数,所以结果为示例。
  • 啊,应该先看,谢谢:P

标签: python pandas datetime


【解决方案1】:

设置(为简洁起见,仅使用来自df1 的一些条目):

df1 = pd.DataFrame({'time' : pd.date_range('1/1/2018', periods=20, freq='10min'), 'value' : np.random.randint(2, 20, size=20)})
df2 = pd.DataFrame({'start_time' : ['2018-01-01 00:00:00', '2018-01-01 00:00:00','2018-01-01 01:00:00', '2018-01-01 01:00:00', '2018-01-01 01:00:00', '2018-01-01 02:00:00' ], 'end_time' : ['2018-01-01 01:00:00', '2018-01-01 01:00:00', '2018-01-01 02:00:00','2018-01-01 02:00:00', '2018-01-01 02:00:00', '2018-01-01 03:00:00'], 'event' : ['A', 'B', 'C', 'D', 'E', 'F'] })

df1 = df1.sample(5)
df2[['start_time', 'end_time']] = df2.iloc[:,0:2].apply(pd.to_datetime)

您可以使用几个简单的列表推导来实现您的结果。此答案假定所有日期列实际上都是您的 DataFrame 中的 datetime 类型:

第 1 步
使用列表推导和简单的间隔检查查找在特定时间范围内发生的所有事件:

packed = list(zip(df2.start_time, df2.end_time, df2.event))
df1['event'] = [[ev for strt, end, ev in packed if strt <= el <= end] for el in df1.time]

                  time  value      event
2  2018-01-01 00:20:00      8     [A, B]
14 2018-01-01 02:20:00     14        [F]
8  2018-01-01 01:20:00      6  [C, D, E]
19 2018-01-01 03:10:00     16         []
4  2018-01-01 00:40:00      7     [A, B]

第 2 步

最后,使用另一个列表推导将每个列表从最后一个结果分解为新行:

pd.DataFrame(
    [[t, val, e] for t, val, event in zip(df1.time, df1.value, df1.event)
    for e in event
    ], columns=df1.columns
)

输出:

                 time  value event
0 2018-01-01 00:20:00      8     A
1 2018-01-01 00:20:00      8     B
2 2018-01-01 02:20:00     14     F
3 2018-01-01 01:20:00      6     C
4 2018-01-01 01:20:00      6     D
5 2018-01-01 01:20:00      6     E
6 2018-01-01 00:40:00      7     A
7 2018-01-01 00:40:00      7     B

【讨论】:

  • 这适用于提供的示例数据框。如果我想在最终输出中保留更多 df1 中的列,您是否看到了一种抽象它的方法?
  • 您应该能够将它们添加到您在第 2 步中 zip 的列中。您可以在列表中跟踪它们,然后将其反向压缩。
【解决方案2】:

您可以使用df2 来创建一个列,始终为每个事件重新采样'10min'(如df1),然后使用merge。这是很多操作,所以可能不是最有效的。

df2_manip = (df2.set_index('event').stack().reset_index().set_index(0)
                .groupby('event').resample('10T').ffill().reset_index(1))

df2_manip 看起来像:

                        0 event     level_1
event                                      
A     2018-01-01 00:00:00     A  start_time
A     2018-01-01 00:10:00     A  start_time
A     2018-01-01 00:20:00     A  start_time
A     2018-01-01 00:30:00     A  start_time
A     2018-01-01 00:40:00     A  start_time
A     2018-01-01 00:50:00     A  start_time
A     2018-01-01 01:00:00     A    end_time
B     2018-01-01 00:00:00     B  start_time
B     2018-01-01 00:10:00     B  start_time
B     2018-01-01 00:20:00     B  start_time
B     2018-01-01 00:30:00     B  start_time
...

现在你可以merge:

df1 = df1.merge(df2_manip[[0, 'event']].rename(columns={0:'time'}))

你会得到df1:

                  time  value event
0  2018-01-01 00:00:00      9     A
1  2018-01-01 00:00:00      9     B
2  2018-01-01 00:10:00     16     A
3  2018-01-01 00:10:00     16     B
...
33 2018-01-01 02:00:00      6     D
34 2018-01-01 02:00:00      6     E
35 2018-01-01 02:00:00      6     F
36 2018-01-01 02:10:00      2     F
37 2018-01-01 02:20:00     18     F
38 2018-01-01 02:30:00     14     F
39 2018-01-01 02:40:00      5     F
40 2018-01-01 02:50:00      3     F
41 2018-01-01 03:00:00      9     F

【讨论】:

    【解决方案3】:

    我不完全确定您的问题,但是如果您尝试加入“在开始时间和结束时间范围内的事件”,那么听起来您需要类似于 SQL 中的“介于”运算符的东西。你的数据并没有特别清楚。

    Pandas 本身没有这个功能,但 Pandasql 有。它允许您针对您的数据框运行 sqlite。我认为你需要这样的东西:

    import pandasql as ps
    
    sqlcode = '''
    select *
    from df1
    inner join df2 on df1.event=df2.event
    where df2.time >= d1.start_time and df2.fdate <= d1.stop_time
    '''
    
    newdf = ps.sqldf(sqlcode,locals())
    

    相关问题: Merge pandas dataframes where one value is between two others

    【讨论】:

    • df1 没有event
    • 看起来很方便。 pandasql 在幕后做了什么?
    猜你喜欢
    • 1970-01-01
    • 2018-07-02
    • 1970-01-01
    • 2019-06-23
    • 2021-05-07
    • 1970-01-01
    • 2022-07-11
    • 1970-01-01
    • 2018-07-22
    相关资源
    最近更新 更多