如何基于功能键与1小时时间差合并DataFrame,指定表为真值源
解决方案:带时间约束的Pandas DataFrame合并
核心思路
要实现Name+Location完全匹配+时间差≤1小时的外连接,关键是先把日期时间转为可计算的datetime类型,再用merge_asof高效处理时间范围匹配,最后补充未匹配的记录。
完整代码实现
import pandas as pd from datetime import timedelta # 构造示例数据(实际使用时替换为你的数据源) df1 = pd.DataFrame({ 'Date': ['02/11', '02/12', '02/12', '02/11', '02/12'], 'Time': ['11:59 PM', '02:00 PM', '05:00 PM', '12:00 AM', '05:30 AM'], 'Name': ['James', 'Harry', 'Harry', 'John', 'John'], 'location': ['LAX', 'IAD', 'IAD', 'LAX', 'IAD'], 'A': ['a', 'c', 'e', 'h', 'g'], 'B': ['b', 'd', 'f', 'i', 'k'] }) df2 = pd.DataFrame({ 'Date': ['02/12', '02/12', '02/12', '02/11', '02/13', '02/12'], 'Time': ['12:30 AM', '02:00 PM', '05:00 PM', '1:01 AM', '05:30 AM', '05:30 AM'], 'Name': ['James', 'Harry', 'Harry', 'John', 'John', 'Ender'], 'location': ['LAX', 'IAD', 'IAD', 'LAX', 'IAD', 'DAL'], 'A_1': ['l', 'n', 'k', 'p', 'r', 't'], 'B_2': ['m', 'o', 'l', 'q', 's', 'u'] }) # 1. 预处理:合并日期时间为可计算格式,统一列名 df1['datetime'] = pd.to_datetime(df1['Date'] + ' ' + df1['Time'], format='%m/%d %I:%M %p') df1 = df1.rename(columns={'location': 'Location'}) df2['datetime'] = pd.to_datetime(df2['Date'] + ' ' + df2['Time'], format='%m/%d %I:%M %p') df2 = df2.rename(columns={'location': 'Location'}) # 2. 按匹配键排序(merge_asof强制要求) df1_sorted = df1.sort_values(['Name', 'Location', 'datetime']) df2_sorted = df2.sort_values(['Name', 'Location', 'datetime']) # 3. 基于时间范围的匹配:找同组内时间差≤1小时的最近记录 merged = pd.merge_asof( df1_sorted, df2_sorted, on='datetime', by=['Name', 'Location'], direction='nearest', tolerance=timedelta(hours=1), suffixes=('', '_df2') ) # 4. 补充df2中未匹配到df1的记录 df2_unmatched = df2_sorted[~df2_sorted.index.isin(merged['index_df2'].dropna())] df2_unmatched = df2_unmatched.assign(A='n/a', B='n/a') # 5. 合并结果并整理列 final = pd.concat([merged, df2_unmatched], ignore_index=True) # 6. 按规则保留时间:优先用df1的日期时间,无则用df2的 final['Date'] = final['Date'].fillna(final['Date_df2']) final['Time'] = final['Time'].fillna(final['Time_df2']) # 7. 筛选目标列,替换空值为n/a final = final[['Date', 'Time', 'Name', 'Location', 'A', 'B', 'A_1', 'B_2']].fillna('n/a') # 8. 调整行顺序(和示例对齐,可选) final = final.sort_values(by=['Name', 'datetime'], ascending=[True, True]).reset_index(drop=True) print(final)
关键步骤解释
- 时间格式转换:把字符串类型的日期时间转为
datetime,是计算时间差的前提,注意用%I:%M %p解析12小时制的AM/PM时间。 - 排序要求:
merge_asof必须按on参数(这里是datetime)排序,同时by参数指定的分组列(Name、Location)也要有序,确保只在同组内匹配时间。 - 时间范围匹配:
direction='nearest'会找同组内时间最接近的记录,tolerance=timedelta(hours=1)直接过滤掉时间差超过1小时的匹配,比先做笛卡尔积再过滤高效得多。 - 补充未匹配记录:
merge_asof的外连接是基于左表(df1)的,所以需要单独提取df2中未匹配的行,填充df1的字段为n/a后合并,保证所有数据都被保留。 - 时间列处理:按照规则优先保留df1的日期时间,没有匹配的行则用df2的时间,最后整理出目标需要的列。
内容的提问来源于stack exchange,提问作者TBallard34
相关产品推荐
相关产品推荐

