You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于功能键与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)

关键步骤解释

  1. 时间格式转换:把字符串类型的日期时间转为datetime,是计算时间差的前提,注意用%I:%M %p解析12小时制的AM/PM时间。
  2. 排序要求:merge_asof必须按on参数(这里是datetime)排序,同时by参数指定的分组列(Name、Location)也要有序,确保只在同组内匹配时间。
  3. 时间范围匹配:direction='nearest'会找同组内时间最接近的记录,tolerance=timedelta(hours=1)直接过滤掉时间差超过1小时的匹配,比先做笛卡尔积再过滤高效得多。
  4. 补充未匹配记录:merge_asof的外连接是基于左表(df1)的,所以需要单独提取df2中未匹配的行,填充df1的字段为n/a后合并,保证所有数据都被保留。
  5. 时间列处理:按照规则优先保留df1的日期时间,没有匹配的行则用df2的时间,最后整理出目标需要的列。

内容的提问来源于stack exchange,提问作者TBallard34

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 13:31:02