【问题标题】:How to merge two dataframe based on time intervals and transform them如何根据时间间隔合并两个数据帧并对其进行转换
【发布时间】:2019-12-21 18:49:58
【问题描述】:

我有两个数据框,第一个是用户手动创建的,第二个是机器错误。 我想根据第一个数据帧(df_a)中的时间间隔合并它们

这里是数据框;

d_a = {'Station' : ['A1','A2'],
       'Reason_a' : ['Electronic','Feed'],
       'StartTime_a' : ['2019-01-02 02:00:00','2019-01-02 04:22:00'],
       'EndTime_a' : ['2019-01-02 02:20:00', '2019-01-02 04:45:00']}

d_b = {'Station' : ['A1','A1','A1','A2','A2','A2'],
       'Reason_b' : ['a','n','c','d','e','n'],
       'StartTime_b' : ['2019-01-02 00:00:00.000','2019-01-02 00:05:00.000','2019-01-01 23:55:00.000','2019-01-02 04:19:53.000','2019-01-02 04:19:37.000','2019-01-02 04:23:00.000'],
       'EndTime_b' : ['2019-01-02 00:19:15.000','2019-01-02 00:29:45.000','2019-01-02 00:12:12.000','2019-01-02 04:27:12.000','2019-01-02 04:47:16.000','2019-01-02 04:52:45.000']}

df_a = pd.DataFrame(d_a)
df_b = pd.DataFrame(d_b)

两个数据帧的时间间隔的任何交点都被视为有效记录。

condition1 = df_b start_time 在 df_a 开始时间之后开始,在 df_a 结束时间之前结束

condition2 = df_b start_time 在 df_a 开始时间之前开始,但在 df_a 结束时间之前结束

condition3 = df_b start_timestarts 在 df_a starttime 和 df_a endtime 之间,但在 df_a endtime 之后结束

最后我想根据条件合并这两个数据框。我的理想表如下所示

 Station     Reason_a        a     n     c     d    e 
  A1         Electronic      1     1     1     0    0
  A2          Feed           0     1     0     1    0

我应该如何解决这个问题? 任何评论都会有所帮助。

提前致谢。

【问题讨论】:

    标签: python pandas datetime conditional-statements nested-loops


    【解决方案1】:

    可以使用 pandas merge_asof 执行这些类型的合并。

    假设“站”是合并过程的附加键,您可以使用如下内容:

    df_a['StartTime_a'] = pd.to_datetime(df_a['StartTime_a'])
    df_b['StartTime_b'] = pd.to_datetime(df_b['StartTime_b'])
    df_a['EndTime_a'] = pd.to_datetime(df_a['EndTime_a'])
    df_b['EndTime_b'] = pd.to_datetime(df_b['EndTime_b'])
    ##before using merge_asof sorting is needed
    df_a.sort_values(by='StartTime_a', inplace=True)
    df_b.sort_values(by='StartTime_b', inplace=True)
    ##merge and filter based on first condition
    cond_1 = pd.merge_asof(df_a, df_b, by='Station', left_on='StartTime_a', 
    right_on='StartTime_b', direction='forward')
    cond_1 = cond_1[cond_1['StartTime_b'] <= cond_1['EndTime_a']]
    ##merge and filter based on second condition
    cond_2 = pd.merge_asof(df_a, df_b, by='Station', left_on='StartTime_a', 
    right_on='StartTime_b', direction='backward')
    cond_2 = cond_2[cond_2['EndTime_b'] <= cond_2['EndTime_a']]
    ##merge and filter based on third condition
    cond_3 = pd.merge_asof(df_a, df_b, by='Station', left_on='StartTime_a', 
    right_on='StartTime_b', direction='forward')
    cond_3 = cond_3[cond_3['StartTime_b'] <= cond_3['EndTime_a']]
    cond_3 = cond_3[cond_3['EndTime_b'] >= cond_3['EndTime_a']]
    ##concatenating all matches
    res_df = pd.concat([cond_1, cond_2, cond_3], sort=False)
    

    【讨论】:

    • 感谢您的回答,但是此方法保留了 df_a 的唯一值,因此如果 df_b 中存在与 df_a 对应的多个事件,则这些数据将丢失。
    【解决方案2】:

    我想出了这个:

        df_c = pd.merge(df_a,df_b, left_on = 'Station', right_on = 'Station')
    

    制作日期时间:

        df_c['StartTime_a'] = pd.to_datetime(df_c['StartTime_a'])
        df_c['StartTime_b'] = pd.to_datetime(df_c['StartTime_b'])
        df_c['EndTime_a'] = pd.to_datetime(df_c['EndTime_a'])
        df_c['EndTime_b'] = pd.to_datetime(df_c['EndTime_b'])
    

    应用 lambda 函数:

        df_c['c'] = df_c.apply(lambda x : 1 if (x.StartTime_b > x.StartTime_a) and (x.EndTime_b < x.EndTime_a) 
                       else (1 if  (x.StartTime_b < x.StartTime_a) and (x.EndTime_b < x.EndTime_a) 
                       else (1 if ((x.StartTime_b > x.StartTime_a) and (x.StartTime_b < x.EndTime_a)) and (x.EndTime_b > x.EndTime_a) else 0)), axis=1)
    

    接着是:

        df_d = df_c.groupby(['Station','Reason_a','Reason_b'])['c'].sum().unstack()
        df_d.fillna(0, inplace=True)
    

    【讨论】:

      【解决方案3】:

      我会通过合并station 上的表格并计算交叉点来解决它:D

      import numpy as np
      
      df = pd.merge(df_a, df_b, on="Station")
      
      # Convert to date
      for datevar in ["StartTime_a", "StartTime_b", "EndTime_a", "EndTime_b"]:
          df[datevar] = pd.to_datetime(df[datevar])
      
      # Intersections definition
      df["intersection"] = (((df.StartTime_a > df.StartTime_b) & (df.StartTime_a < df.EndTime_b)) |
                            ((df.StartTime_a < df.StartTime_b) & (df.EndTime_a > df.StartTime_b)))
      
      # Filter only intersections
      (df[["Station", "Reason_a", "Reason_b", "intersection"]]
      .pivot_table(index=["Station", "Reason_a"], columns="Reason_b", aggfunc=np.sum)
      .fillna(0).astype(int)) 
      

      【讨论】:

      • 嗨,是的,您的解决方案有效,但不幸的是我有更大的数据,我的 df_b 大约 1.5m 行,df_a 大约 50k,所以我在合并步骤时出现内存错误。你有其他的想法吗?谢谢你的一切
      • 当然!因此,如果您有内存错误,我会做的是批量计算它。我的意思是通过每次选择站点子集来运行我建议的代码
      【解决方案4】:

      如果你想避免合并,可以这样:

          def check_condition(x):
      
              df_1 = df_a[(df_a['StartTime_a'] < x.StartTime_b) & (df_a['EndTime_a'] > x.EndTime_b)]
              df_2 = df_a[(df_a['StartTime_a'] > x.StartTime_b) & (df_a['EndTime_a'] > x.EndTime_b)]
              df_3 = df_a[(df_a['StartTime_a'] < x.StartTime_b) & (df_a['EndTime_a'] > x.StartTime_b) 
                  & (df_a['EndTime_a'] < x.EndTime_b)]
      
              if df_1.shape[0]+df_2.shape[0] + df_3.shape[0] !=0:
                  return 1
              else:
                  return 0
      
      
      
           df_b['c'] = df_b.apply(lambda x: check_condition(x), axis=1)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-06-23
        • 2022-11-02
        • 2020-04-20
        • 2017-10-21
        • 2019-04-28
        • 2021-09-10
        • 1970-01-01
        • 2019-06-22
        相关资源
        最近更新 更多