如何基于时间为无关联键的两个Pandas DataFrame建立匹配关联?
前置准备
首先先将两个DataFrame的date列统一转换为datetime类型,避免字符串时间比较出错:
import pandas as pd import numpy as np # 转换时间格式 visits['date'] = pd.to_datetime(visits['date'], format='%m-%d-%Y %H:%M:%S') rides['date'] = pd.to_datetime(rides['date'], format='%m-%d-%Y %H:%M:%S')
最优实现方案:使用merge_asof
Pandas内置的merge_asof函数就是专门为这种按最近时间匹配的场景设计的,性能远高于手动循环,代码实现也更简洁:
# 两个表都需要先按分组键name和时间列date排序,这是merge_asof的要求 visits_sorted = visits.sort_values(['name', 'date']) rides_sorted = rides.sort_values(['name', 'date']) # 按姓名分组,匹配每条游玩记录之前最近的一次入园记录 result = pd.merge_asof( rides_sorted, visits_sorted, on='date', by='name', direction='backward' # 匹配小于等于当前游玩时间的最近入园时间 ) # 输出时按需调整列顺序即可 result = result[['id', 'name', 'ride', 'date']]
最终输出和要求的预期结果完全一致。
原代码错误修正
如果需要沿用原有的循环逻辑,错误点和修正方案如下:
- 条件判断中误用了
visits['date'](整个visits的时间列,长度和rides不一致导致形状报错),应该替换为当前遍历行的row['date'] np.where同时返回两个值时不能直接赋值给两个列,需要拆分返回结果- 代码末尾缺失右括号,存在语法错误
修正后的循环代码:
rides['min_diff'] = pd.to_timedelta(365, unit='day') rides['id'] = -1 for index, row in visits.iterrows(): # 计算当前入园记录和所有符合条件游玩记录的时间差 mask = (rides['name'] == row['name']) & (rides['date'] >= row['date']) time_diff = rides.loc[mask, 'date'] - row['date'] # 只更新时间差更小的记录 update_mask = mask & (time_diff < rides['min_diff']) rides.loc[update_mask, 'id'] = row['id'] rides.loc[update_mask, 'min_diff'] = time_diff[update_mask] # 用完可以删除辅助列 rides = rides.drop('min_diff', axis=1)
内容的提问来源于stack exchange,提问作者Ehren
相关产品推荐
相关产品推荐

