使用pandas.merge_asof进行完全外部连接

问题描述

嗨,我需要将一些时间序列数据与最近的时间戳对齐,因此我认为pandas.merge_asof可能是一个不错的选择。但是,它没有像标准how='outer'方法那样设置merge的选项。

一个例子可以是:

df1:

                                   Value1
Time
2020-07-17 14:25:03.535906075      108
2020-07-17 14:25:05.457247019      110
2020-07-17 14:25:07.467777014      126

df2:

                                  Value2
Time
2020-07-17 14:25:03.535018921     222
2020-07-17 14:25:04.545104980     150
2020-07-17 14:25:07.476825953      60

然后例如,执行此merge_asof

pd.merge_asof(df1,df2,left_index=True,right_index=True,direction='nearest',tolerance=pd.timedelta('0.3s'))

结果将是:

                               Value1  Value2
Time
2020-07-17 14:25:03.535906075     108   222.0
2020-07-17 14:25:05.457247019     110     NaN
2020-07-17 14:25:07.467777014     126    60.0

但是我想要的是:

                               Value1  Value2
Time
2020-07-17 14:25:03.535906075     108   222.0
2020-07-17 14:25:04.545104980     NaN   150.0   <---- this is the difference
2020-07-17 14:25:05.457247019     110     NaN
2020-07-17 14:25:07.467777014     126    60.0

基本上就像一个完整的外部联接。

有什么建议吗?预先感谢。

编辑:

因此,这是2个数据帧的情况。例如,如果有10个数据帧(即df1,...,df10)需要进行此“最近”合并,那将是一个方法吗?

解决方法

  1. 不幸的是,how中没有pd.merge_asof中的pd.merge参数,否则您可以简单地传递how='outer'
  2. 作为一种解决方法,您可以手动append另一个数据框中的不匹配值
  3. 然后,用.sort_index()
  4. 对索引进行排序

df3 = pd.merge_asof(df1,df2,left_index=True,right_index=True,direction='nearest',tolerance=pd.Timedelta('0.3s'))
df4 = pd.merge_asof(df2,df1,tolerance=pd.Timedelta('0.3s'))
df5 = df3.append(df4[df4['Value1'].isnull()]).sort_index()
df5
Out[1]: 
                               Value1  Value2
Time                                         
2020-07-17 14:25:03.535906075   108.0   222.0
2020-07-17 14:25:04.545104980     NaN   150.0
2020-07-17 14:25:05.457247019   110.0     NaN
2020-07-17 14:25:07.467777014   126.0    60.0
,

这似乎很简单,但没有直接解决方案。有一个选项可以再次合并以引入缺少的行:

# enumerate the rows of `df2` to later identify which are missing
df2 = df2.reset_index().assign(idx=np.arange(df2.shape[0]))
(pd.merge_asof(df1.reset_index(),df2[['Time','idx']],on='Time',tolerance=pd.Timedelta('0.3s'))
  .merge(df2,on='idx',how='outer')                        # merge back on row number
  .assign(Time=lambda x: x['Time_x'].fillna(x['Time_y']))   # fill the time
  .set_index(['Time'])                                      # set index back
  .drop(['Time_x','Time_y','idx'],axis=1)
  .sort_index()
)

                               Value1  Value2
Time                                         
2020-07-17 14:25:03.535906075   108.0   222.0
2020-07-17 14:25:04.545104980     NaN   150.0
2020-07-17 14:25:05.457247019   110.0     NaN
2020-07-17 14:25:07.467777014   126.0    60.0