Pandas优化:两个DataFrame近时间戳记录匹配与数据复制
时间戳近似匹配的高效实现方案
问题背景
有两个结构相似的DataFrame,均包含datetime格式的Timestamp列(已按时间排序),需匹配时间戳相差10秒以内的记录,并在两表间复制指定字段数据。当前暴力匹配法处理2-3k条数据时,搜索阶段耗时50-60秒,急需高效替代方案。
原暴力匹配代码
import pandas as pd from datetime import timedelta c = ['Timestamp','Val1', 'Val2', 'Val3', 'Val4', 'Val5'] d1 = [['2000-11-08 23:30:40', 20, '', 11, '', ''], # 应匹配 ['2000-11-08 23:30:42', 25, '', 22, '', ''], ['2000-11-08 23:31:40', 5, '', 5, '', ''], ['2000-11-08 23:32:35', 6, '', 3, '', ''], ['2000-11-08 23:34:22', 15, '', 2, '', ''], ['2000-11-08 23:35:30', 40, '', 22, '', ''], # 应匹配 ['2000-11-08 23:37:40', 32, '', 6, '', ''], ['2000-11-08 23:38:40', 1, '', 9, '', ''], ['2000-11-08 23:39:40', 0, '', 12, '', ''], # 应匹配 ['2000-11-08 23:43:40', 11, '', 3, '', ''], ['2000-11-08 23:48:40', 61, '', 2, '', ''], # 应匹配 ['2000-11-08 23:49:40', 55, '', 0, '', ''], # 应匹配 ['2000-11-08 23:52:40', 15, '', 8, '', ''], ['2000-11-08 23:55:40', 7, '', 17, '', '']] d2 = [['2000-11-08 23:30:42', '', 'a', '', '', ''], # 应匹配 ['2000-11-08 23:30:55', '', 'b', '', '', ''], ['2000-11-08 23:35:25', '', 'a', '', '', ''], # 应匹配 ['2000-11-08 23:38:20', '', 'd', '', '', ''], ['2000-11-08 23:39:41', '', 'e', '', '', ''], # 应匹配 ['2000-11-08 23:43:19', '', 'f', '', '', ''], ['2000-11-08 23:48:44', '', 'g', '', '', ''], # 应匹配 ['2000-11-08 23:49:40', '', 'g', '', '', ''], # 应匹配 ['2000-11-08 23:55:29', '', 'e', '', '', '']] df1 = pd.DataFrame(d1, columns=c) df2 = pd.DataFrame(d2, columns=c) # 转换时间戳格式 df1['Timestamp'] = pd.to_datetime(df1['Timestamp']) df2['Timestamp'] = pd.to_datetime(df2['Timestamp']) # 按时间戳排序(原数据已排序,此处为冗余操作) df1 = df1.sort_values('Timestamp') df2 = df2.sort_values('Timestamp') # 设置时间差阈值 numSecondsBetweenValues = timedelta(seconds=10) index1 = [] index2 = [] startIndex2 = 0 currIndex2 = 0 for currIndex1 in range(len(df1)): currIndex2 = startIndex2 while currIndex2 < len(df2): if abs(df1['Timestamp'][currIndex1] - df2['Timestamp'][currIndex2]) < numSecondsBetweenValues: print('found 1') startIndex2 = currIndex2 + 1 index1.append(currIndex1) index2.append(currIndex2) break currIndex2 += 1 # 复制匹配字段 for i, ii in zip(index1, index2): df1['Val2'].iloc[i] = df2['Val2'].iloc[ii] df2['Val1'].iloc[ii] = df1['Val1'].iloc[i] df2['Val3'].iloc[ii] = df1['Val3'].iloc[i]
高效实现方案:使用merge_asof
Pandas的merge_asof专为排序后的时间序列近似匹配设计,基于二分查找实现,时间复杂度为O(n log n),远优于暴力法的O(n*m),处理万级数据也能在毫秒级完成。
实现步骤
- 确保两DataFrame的Timestamp列已转换为datetime类型且按时间排序(
merge_asof强制要求) - 调用
merge_asof,设置匹配的时间差阈值tolerance - 根据匹配结果,将需要的字段映射回原DataFrame
代码示例
import pandas as pd from datetime import timedelta c = ['Timestamp','Val1', 'Val2', 'Val3', 'Val4', 'Val5'] d1 = [['2000-11-08 23:30:40', 20, '', 11, '', ''], ['2000-11-08 23:30:42', 25, '', 22, '', ''], ['2000-11-08 23:31:40', 5, '', 5, '', ''], ['2000-11-08 23:32:35', 6, '', 3, '', ''], ['2000-11-08 23:34:22', 15, '', 2, '', ''], ['2000-11-08 23:35:30', 40, '', 22, '', ''], ['2000-11-08 23:37:40', 32, '', 6, '', ''], ['2000-11-08 23:38:40', 1, '', 9, '', ''], ['2000-11-08 23:39:40', 0, '', 12, '', ''], ['2000-11-08 23:43:40', 11, '', 3, '', ''], ['2000-11-08 23:48:40', 61, '', 2, '', ''], ['2000-11-08 23:49:40', 55, '', 0, '', ''], ['2000-11-08 23:52:40', 15, '', 8, '', ''], ['2000-11-08 23:55:40', 7, '', 17, '', '']] d2 = [['2000-11-08 23:30:42', '', 'a', '', '', ''], ['2000-11-08 23:30:55', '', 'b', '', '', ''], ['2000-11-08 23:35:25', '', 'a', '', '', ''], ['2000-11-08 23:38:20', '', 'd', '', '', ''], ['2000-11-08 23:39:41', '', 'e', '', '', ''], ['2000-11-08 23:43:19', '', 'f', '', '', ''], ['2000-11-08 23:48:44', '', 'g', '', '', ''], ['2000-11-08 23:49:40', '', 'g', '', '', ''], ['2000-11-08 23:55:29', '', 'e', '', '', '']] df1 = pd.DataFrame(d1, columns=c) df2 = pd.DataFrame(d2, columns=c) # 转换时间戳格式并排序(merge_asof要求必须排序) df1['Timestamp'] = pd.to_datetime(df1['Timestamp']) df2['Timestamp'] = pd.to_datetime(df2['Timestamp']) df1 = df1.sort_values('Timestamp').reset_index(drop=True) df2 = df2.sort_values('Timestamp').reset_index(drop=True) # 设置时间差阈值 tolerance = timedelta(seconds=10) # 执行近似匹配:以df2为左表,匹配df1中最近的、时间差在阈值内的记录 # direction='nearest'表示找最近的时间戳,也可根据需求选'backward'或'forward' merged = pd.merge_asof( df2, df1, on='Timestamp', tolerance=tolerance, direction='nearest', suffixes=('_df2', '_df1') ) # 将匹配到的df1字段复制到df2 df2.loc[merged['Val1_df1'].notna(), 'Val1'] = merged['Val1_df1'] df2.loc[merged['Val3_df1'].notna(), 'Val3'] = merged['Val3_df1'] # 将匹配到的df2字段复制到df1 # 先构建df1到merged的映射 merged_df1 = merged.dropna(subset=['Val2_df2']).set_index('Timestamp')['Val2_df2'] df1['Val2'] = df1['Timestamp'].map(merged_df1).fillna(df1['Val2'])
处理后df2示例输出
Timestamp Val1 Val2 Val3 Val4 Val5 0 2000-11-08 23:30:42 20 a 11 1 2000-11-08 23:30:55 b 2 2000-11-08 23:35:25 40 a 22 3 2000-11-08 23:38:20 d 4 2000-11-08 23:39:41 0 e 12 5 2000-11-08 23:43:19 f 6 2000-11-08 23:48:44 61 g 2 7 2000-11-08 23:49:40 55 g 0 8 2000-11-08 23:55:29 e
内容的提问来源于stack exchange,提问作者gerrgheiser
相关产品推荐
相关产品推荐

