如何用Pandas高效实现基于时间区间的DataFrame合并
高效实现基于时间区间的DataFrame合并
需求说明
需要合并两个DataFrame:
- 第一个DataFrame(记为
df1)包含time_1、time_2及其他列 - 第二个DataFrame(记为
df2)包含time_3及其他列(通常行数更多)
合并规则:当df2中time_3的取值处于df1某行的time_1与time_2区间内时,将df1的该行与df2的对应行合并,df1的行需根据匹配次数重复。
示例数据
df1格式:
time_1 | time_2 | dummy_data |
|---|---|---|
| 2023-10-01 04:02:00 | 2023-10-01 08:29:00 | -245.669907 |
| 2023-10-01 04:03:00 | 2023-10-01 08:49:00 | -1772.948571 |
| ... | ... | ... |
df2格式:
time_3 | dummy_data2 |
|---|---|
| 2023-10-01 06:21:13.238024 | -131.367901 |
| 2023-10-01 06:47:19.796628 | -236.277444 |
| 2023-10-01 07:37:06.438740 | 5.915493 |
| 2023-10-01 08:16:16.995256 | -134.032433 |
| 2023-10-01 08:33:53.081095 | -103.733212 |
预期合并结果:
time_1 | time_2 | dummy_data | time_3 | dummy_data2 |
|---|---|---|---|---|
| 2023-10-01 04:02:00 | 2023-10-01 08:29:00 | -245.669907 | 2023-10-01 06:21:13.238024 | -131.367901 |
| 2023-10-01 04:02:00 | 2023-10-01 08:29:00 | -245.669907 | 2023-10-01 06:47:19.796628 | -236.277444 |
| 2023-10-01 04:02:00 | 2023-10-01 08:29:00 | -245.669907 | 2023-10-01 07:37:06.438740 | 5.915493 |
| 2023-10-01 04:02:00 | 2023-10-01 08:29:00 | -245.669907 | 2023-10-01 08:16:16.995256 | -134.032433 |
| 2023-10-01 04:03:00 | 2023-10-01 08:49:00 | -1772.948571 | 2023-10-01 06:21:13.238024 | -131.367901 |
| 2023-10-01 04:03:00 | 2023-10-01 08:49:00 | -1772.948571 | 2023-10-01 06:47:19.796628 | -236.277444 |
| 2023-10-01 04:03:00 | 2023-10-01 08:49:00 | -1772.948571 | 2023-10-01 07:37:06.438740 | 5.915493 |
| 2023-10-01 04:03:00 | 2023-10-01 08:49:00 | -1772.948571 | 2023-10-01 08:16:16.995256 | -134.032433 |
| 2023-10-01 04:03:00 | 2023-10-01 08:49:00 | -1772.948571 | 2023-10-01 08:33:53.081095 | -103.733212 |
低效原代码问题
原代码使用嵌套循环和append方法,每次append都会创建新的DataFrame,内存开销大且速度极慢,尤其在数据量较大时表现明显。
高效Pandas实现方案
利用Pandas的向量化操作实现交叉连接+条件过滤,性能远高于循环方案:
import pandas as pd # 读取数据 df1 = pd.read_csv("datetime_list.csv") df2 = pd.read_csv('dataframe.csv') # 转换所有时间列为datetime类型(必须步骤,否则无法进行时间比较) df1['time_1'] = pd.to_datetime(df1['time_1']) df1['time_2'] = pd.to_datetime(df1['time_2']) df2['time_3'] = pd.to_datetime(df2['time_3']) # 添加临时键实现交叉连接 df1['temp_key'] = 1 df2['temp_key'] = 1 # 交叉连接后过滤符合时间区间的行,最后删除临时键 merged_df = pd.merge(df1, df2, on='temp_key') \ .query('time_3 >= time_1 and time_3 < time_2') \ .drop('temp_key', axis=1) # 调整列顺序以匹配预期结果(可选) merged_df = merged_df[['time_1', 'time_2', 'dummy_data', 'time_3', 'dummy_data2']]
方案说明
- 时间类型转换:确保所有时间列是
datetime64类型,才能正确进行区间比较。 - 交叉连接:通过添加临时相同键实现两个DataFrame的全量匹配,这一步是向量化操作,效率远高于循环。
- 条件过滤:用
query方法快速筛选出time_3落在[time_1, time_2)区间内的行,同样是向量化操作,性能优异。 - 清理临时列:删除用于交叉连接的临时键,得到最终结果。
性能优化补充
如果数据量极大(如百万级行),交叉连接可能占用较多内存,可以改用numpy广播来生成匹配索引,进一步降低内存开销:
import numpy as np # 转换时间列为数值类型(timestamp)以加速比较 t1 = df1['time_1'].values.astype(np.int64) t2 = df1['time_2'].values.astype(np.int64) t3 = df2['time_3'].values.astype(np.int64) # 广播生成匹配矩阵 mask = (t3[:, None] >= t1) & (t3[:, None] < t2) # 获取匹配的索引对 df1_idx, df2_idx = np.where(mask) # 合并结果 merged_df = pd.concat([ df1.iloc[df1_idx].reset_index(drop=True), df2.iloc[df2_idx].reset_index(drop=True) ], axis=1)
这种方法直接通过numpy的广播生成布尔矩阵,再提取匹配索引,内存占用更低,速度更快。
内容的提问来源于stack exchange,提问作者A10
相关产品推荐
相关产品推荐

