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

如何在Polars中执行不重复使用右侧DataFrame行的最近匹配Asof连接?

如何在Polars中执行不重复使用右侧DataFrame行的最近匹配Asof连接?

嘿,这个需求我之前做项目的时候刚好碰到过!Polars自带的join_asof虽然支持strategy="nearest"做最近匹配,但确实没限制右侧行的复用,得自己加点小逻辑来实现“右侧每行只能用一次”的要求。我给你分享两种实用的方法,一个是容易理解的遍历方式,另一个是更高效的批量处理方式,你按需选~

先补全咱们的示例测试数据,方便你上手验证:

import polars as pl

df1 = pl.from_repr("""
┌───────┐
│ value │
│ ---   │
│ i64   │
├───────┤
│ 1     │
│ 3     │
│ 6     │
│ 8     │
└───────┘
""")

df2 = pl.from_repr("""
┌───────┐
│ value │
│ ---   │
│ i64   │
├───────┤
│ 2     │
│ 4     │
│ 7     │
│ 9     │
└───────┘
""")

方法一:贪心遍历匹配(适合小数据量)

这个思路特别直接,就是按顺序给左侧每一行找右侧还没被用的最近值,匹配上就标记右侧行已使用,避免重复:

# 先给两个DataFrame按匹配列排序,最近匹配的前提是数据有序
df1_sorted = df1.sort("value")
df2_sorted = df2.sort("value").with_columns(used=pl.lit(False))

# 准备存储匹配结果的列表
matches = []

# 逐个处理左侧的每一行
for left_row in df1_sorted.iter_rows(named=True):
    left_val = left_row["value"]
    # 只筛选右侧还没被匹配过的行
    available_right = df2_sorted.filter(~pl.col("used"))
    
    if available_right.is_empty():
        # 右侧没有可用行时,就存一个空匹配结果
        matches.append({"left_value": left_val, "right_value": None})
        continue
    
    # 计算每个可用右侧行和当前左侧值的差值绝对值
    diffs = available_right.with_columns(
        diff=pl.abs(pl.col("value") - left_val)
    )
    # 挑出差值最小的那一行(也就是最近匹配的目标行)
    nearest = diffs.sort("diff").row(0, named=True)
    
    # 记录这次的匹配结果
    matches.append({
        "left_value": left_val,
        "right_value": nearest["value"]
    })
    # 把右侧这行标记为已使用,防止后续再被匹配
    df2_sorted = df2_sorted.with_columns(
        used=pl.when(pl.col("value") == nearest["value"])
               .then(pl.lit(True))
               .otherwise(pl.col("used"))
    )

# 把匹配结果转换成Polars DataFrame格式
result = pl.DataFrame(matches)
print(result)

这种方法逻辑简单,一眼就能看懂,但如果你的数据量特别大,Python循环的速度会有点跟不上,这时候就可以用下面的批量处理方法。

方法二:批量高效匹配(适合大数据量)

利用Polars的批量操作能力,不用写循环,通过交叉连接和窗口函数来实现,速度会快很多,大数据量场景下优势特别明显:

# 先给两个DataFrame排序,加上行索引方便后续的匹配筛选
df1_sorted = df1.sort("value").with_row_index("left_idx")
df2_sorted = df2.sort("value").with_row_index("right_idx").rename({"value": "value_right"})

# 生成所有左右行的组合,计算每对组合的差值绝对值
cross_df = df1_sorted.join(df2_sorted, how="cross").with_columns(
    diff=pl.abs(pl.col("value") - pl.col("value_right"))
)

# 按差值从小到大排序,给每个右侧行只保留第一次出现的匹配(也就是最优的那个匹配)
ranked = cross_df.sort(["diff", "left_idx"]).with_columns(
    rn=pl.row_number().over("right_idx")
).filter(pl.col("rn") == 1)

# 再给每个左侧行筛选出唯一的最优匹配
final_result = ranked.sort(["left_idx", "diff"]).with_columns(
    rn_left=pl.row_number().over("left_idx")
).filter(pl.col("rn_left") == 1).drop(["rn", "rn_left", "diff"])

print(final_result)

这个方法的核心是先找出每个右侧行能匹配到的最优左侧行,再给每个左侧行确定唯一的可用匹配,全程用Polars的内置函数处理,避开了Python循环的性能瓶颈。

备注:内容来源于stack exchange,提问作者miroslaavi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.13 17:14:39