如何加速加入非相等时间值的大型数据集

how to speed up joining large datasets on nonequal time values

我有 2 个大型数据框 df1df2,它们都有一列 time。我想加入这 2 tables。但是,可能并不总是完全匹配。在这种情况下,我想加入,以便我以有效的方式获取 df2 中发生在 df1 时间值之前的最新时间值。

例如,给定 tables

df1
Time             | val_1
------------------------
1/1/1980 1:00:00 | 1
1/1/1980 2:00:00 | 2
1/1/1980 3:00:00 | 3
1/1/1980 4:00:00 | 4

df2
time             | val_2
------------------------
1/1/1980 1:00:00 | 5
1/1/1980 1:59:59 | 6
1/1/1980 3:00:01 | 7
1/1/1980 3:30:30 | 8

最后的table应该是

time             | val_1 | val_2
--------------------------------
1/1/1980 1:00:00 | 1     | 5
1/1/1980 2:00:00 | 2     | 6
1/1/1980 3:00:00 | 3     | 6
1/1/1980 4:00:00 | 4     | 8

我目前正在这样做,但运行时间太长

def prevrow(t):
    return df2.iloc[df2['time'].apply(lambda x: t - x if t >= x else np.nan).idxmin()]
pd.concat([df1,df1['Time'].apply(prevrow)], axis=1)

我该如何加快速度?

我们可以尝试使用 merge_asof 代替:

# df1 = df1.rename(columns={'Time': 'time'})
new_df = pd.merge_asof(df1, df2, on='time', direction='backward')

*注意direction='backward'是默认方向,所以不需要指定,但是,这是我们要查找的匹配方向。

new_df:

                 time  val_1  val_2
0 1980-01-01 01:00:00      1      5
1 1980-01-01 02:00:00      2      6
2 1980-01-01 03:00:00      3      6
3 1980-01-01 04:00:00      4      8

需要注意的是 time 列必须在两个 DataFrame 中排序,这可以通过 sort_values

完成(如果尚未完成)
# df1 = df1.rename(columns={'Time': 'time'})
new_df = pd.merge_asof(df1.sort_values('time'),
                       df2.sort_values('time'), 
                       on='time')

一些时间信息来自 %timeit:

原始方法:

def prevrow(t):
    return df2.iloc[df2['time'].apply(lambda x: t - x if t >= x else np.nan).idxmin()]

%timeit pd.concat([df1,df1['time'].apply(prevrow)], axis=1)
2.29 ms ± 172 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

merge_asof 不排序:

%timeit pd.merge_asof(df1, df2, on='time')
1.13 ms ± 50.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

merge_asof 排序:

%timeit pd.merge_asof(df1.sort_values('time'), df2.sort_values('time'), on='time')
1.46 ms ± 27.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

数据和导入:

import pandas as pd

df1 = pd.DataFrame({
    'time': pd.to_datetime(['1/1/1980 1:00:00', '1/1/1980 2:00:00',
                            '1/1/1980 3:00:00', '1/1/1980 4:00:00']),
    'val_1': [1, 2, 3, 4]
})

df2 = pd.DataFrame({
    'time': pd.to_datetime(['1/1/1980 1:00:00', '1/1/1980 1:59:59',
                            '1/1/1980 3:00:01', '1/1/1980 3:30:30']),
    'val_2': [5, 6, 7, 8]
})