在Python大DataFrame中实现近似匹配Vlookup的方法求助
问题解决:基于日期时间近似匹配替换DataFrame列值
问题背景
基础DataFrame(df)
| Type | Bid | Ask | Hora | Data | Two Seconds |
|---|---|---|---|---|---|
| Buyer | 5711.0 | 5711.5 | 09:00:49 | 2022-01-07 | 09:00:51 |
| Seller | 5710.0 | 5710.5 | 09:00:52 | 2022-01-07 | 09:00:54 |
| Buyer | 5707.5 | 5708.0 | 09:00:53 | 2022-01-07 | 09:00:55 |
| Buyer | 5700.5 | 5701.0 | 17:59:59 | 2022-01-14 | 18:00:01 |
价格DataFrame(prices)
| Bid | Ask | Hora | Data |
|---|---|---|---|
| 5713.0 | 5713.5 | 09:00:51 | 2022-01-07 |
| 5708.0 | 5708.5 | 09:00:55 | 2022-01-07 |
| 5703.5 | 5704.0 | 18:00:00 | 2022-01-14 |
需求
将df的Two Seconds列替换为:基于Data日期和Two Seconds时间在prices中近似匹配后的值,规则为:
- 当
Type为Buyer时取prices中的Ask值 - 当
Type为Seller时取prices中的Bid值
预期结果:
| Type | Bid | Ask | Hora | Data | Two Seconds |
|---|---|---|---|---|---|
| Buyer | 5711.0 | 5711.5 | 09:00:49 | 2022-01-07 | 5713.5 |
| Seller | 5710.0 | 5710.5 | 09:00:52 | 2022-01-07 | 5713.0 |
| Buyer | 5707.5 | 5708.0 | 09:00:53 | 2022-01-07 | 5708.5 |
| Buyer | 5700.5 | 5701.0 | 17:59:59 | 2022-01-14 | 5704.0 |
原代码问题
原代码使用apply逐行处理,大数据量下速度极慢,且仅支持精确匹配,无法实现近似匹配:
df["Two_seconds"] = df.apply(lambda x: prices.loc[(prices["Data"] == x["Data"]) & (prices["Hora"] == x["Two_seconds"]),'Bid' if x["Tipo"] == "Buyer" else 'Ask'], axis = 1)
解决方案
使用pandas.merge_asof实现高效的近似时间匹配,这是矢量化操作,远快于逐行apply,具体步骤如下:
步骤1:合并日期与时间为完整datetime类型
单独的日期和时间列无法直接进行时间近似匹配,需合并为datetime类型:
import pandas as pd # 为df创建目标时间列 df['target_datetime'] = pd.to_datetime(df['Data'] + ' ' + df['Two Seconds']) # 为prices创建时间列 prices['datetime'] = pd.to_datetime(prices['Data'] + ' ' + prices['Hora'])
步骤2:对prices按时间排序
merge_asof要求右表(prices)必须按匹配的时间列排序:
prices_sorted = prices.sort_values('datetime')
步骤3:执行近似匹配
使用merge_asof按日期分组(by='Data'),并匹配不大于目标时间的最近记录(direction='backward'):
merged = pd.merge_asof( df.sort_values('target_datetime'), # 左表也需按时间排序 prices_sorted, left_on='target_datetime', right_on='datetime', by='Data', direction='backward' )
步骤4:按Type替换Two Seconds列
根据Type选择对应的Ask或Bid值,替换原列:
merged['Two Seconds'] = merged.apply(lambda x: x['Ask'] if x['Type'] == 'Buyer' else x['Bid'], axis=1)
步骤5:整理结果格式
保留原列顺序并修正列名:
result = merged[['Type', 'Bid_x', 'Ask_x', 'Hora', 'Data', 'Two Seconds']].rename(columns={'Bid_x': 'Bid', 'Ask_x': 'Ask'}) print(result)
关键优势
- 速度快:
merge_asof是矢量化操作,处理大数据量远优于逐行apply - 支持近似匹配:通过
direction参数灵活控制匹配规则(backward/forward/nearest) - 精准分组:
by='Data'确保仅在同一天内匹配时间,避免跨日期错误匹配
内容的提问来源于stack exchange,提问作者Jadson
相关产品推荐
相关产品推荐

