Pandas按日期精确/字符串部分匹配合并DataFrame及后处理方法
跨酒店房型匹配问题
需求描述
现有跨酒店房型匹配业务场景:具备可比性的同类房型可能存在不同名称变体。其中df1存储指定酒店(酒店X)的数据,df2存储其竞争对手(酒店A、酒店B)的数据,匹配规则为:同日期下,竞品房型名以酒店X房型名为前缀即判定为匹配,同一房型匹配到多个竞品房型时,取名称最短的匹配项。
逐行处理的方案在数据规模增长时扩展性差,现提供示例数据集,需要用高效的方式得到预期输出。
示例数据集构造代码
import pandas as pd df1 = pd.DataFrame({"date": ["2022-06-15", "2022-06-15", "2022-06-15", "2022-06-26", "2022-06-26"], "type": ["superior", "premier", "grand", "suite", "suite"]}) df2 = pd.DataFrame({"date": ["2022-06-15", "2022-06-15", "2022-06-15", "2022-06-15", "2022-06-15", "2022-06-15", "2022-06-15", "2022-06-26", "2022-06-26", "2022-06-26", "2022-06-26"], "competitor": ["A", "A", "A", "A", "B", "B", "B", "A", "A", "B", "B"], "type": ["superior studio", "superior double studio", "premier studio", "premier double room", "superior", "superior double", "grand suite", "superior studio", "premier studio", "grand suite", "superior"], "value": [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110]})
已尝试方案
目前已通过pandasql实现部分匹配逻辑,但不清楚如何用纯pandas完成后续处理得到最终结果,已实现代码如下:
from pandasql import sqldf df_A = df2[df2["competitor"] == "A"].reset_index(drop=True) df_B = df2[df2["competitor"] == "B"].reset_index(drop=True) sql = """SELECT df1.date, df1.type, df_A.type as A, df_A.value as val_A, df_B.type as B, df_B.value as val_B FROM df1 LEFT JOIN df_A ON df_A.type LIKE df1.type || '%' AND df1.date = df_A.date LEFT JOIN df_B ON df_B.type LIKE df1.type || '%' AND df1.date = df_B.date""" temp = sqldf(sql, locals())
纯Pandas高效实现方案
采用向量化操作替代逐行遍历,数据量增长时性能表现稳定,实现逻辑如下:
- 先通过
merge做日期维度的关联,避免全量笛卡尔积 - 用字符串前缀匹配规则过滤符合要求的房型对
- 按房型名称长度排序,同匹配组取长度最短的结果作为最优匹配
- 透视转换为要求的宽表格式,补全未匹配到的行
# 1. 按日期关联两张表,缩小匹配范围 merged = df1.merge(df2, on="date", how="left", suffixes=("", "_comp")) # 2. 过滤前缀匹配的房型 merged = merged[merged.apply(lambda x: x["type_comp"].startswith(x["type"]), axis=1)] # 3. 按匹配优先级排序:名称越短匹配度越高,同组保留最优匹配 merged["name_len"] = merged["type_comp"].str.len() merged = (merged.sort_values(["date", "type", "competitor", "name_len"]) .drop_duplicates(["date", "type", "competitor"], keep="first")) # 4. 长表转宽表,调整列格式 pivot = merged.pivot(index=["date", "type"], columns="competitor", values=["type_comp", "value"]) pivot.columns = [c[1] if c[0] == "type_comp" else f"val_{c[1]}" for c in pivot.columns] pivot = pivot[["A", "val_A", "B", "val_B"]].reset_index() # 5. 补全df1中无匹配结果的行 final_result = df1.merge(pivot, on=["date", "type"], how="left")
运行后得到的结果与预期完全一致:
- 2022-06-15的
superior房型匹配到A的superior studio(val_A=10)、B的superior(val_B=50) - 2022-06-15的
premier房型匹配到A的premier studio(val_A=30),B无匹配 - 2022-06-15的
grand房型匹配到B的grand suite(val_B=70),A无匹配 - 2022-06-26的
suite房型匹配到B的grand suite(val_B=100),A无匹配
内容的提问来源于stack exchange,提问作者Ratchainant Thammasudjarit
相关产品推荐
相关产品推荐

