如何在Polars中执行仅向后匹配特定条件的join_asof操作
在Polars中实现匹配历史行的需求
完全可以实现你的需求,而且用窗口函数比join_asof更简洁高效,以下是两种可行方案:
方案一:窗口函数(推荐)
通过分组窗口的shift操作直接获取同组内的最近历史值,无需额外连接操作:
import polars as pl pl.Config(tbl_rows=15) df = pl.from_repr(""" ┌────────┬─────┬───────┬───────┬───────────────┐ │ Season ┆ Wk ┆ Team ┆ Field ┆ GeneralLambda │ │ --- ┆ --- ┆ --- ┆ --- ┆ --- │ │ i64 ┆ i64 ┆ str ┆ str ┆ f64 │ ╞════════╪═════╪═══════╪═══════╪═══════════════╡ │ 2024 ┆ 25 ┆ TeamA ┆ Away ┆ 2.123456 │ │ 2024 ┆ 25 ┆ TeamB ┆ Home ┆ 1.234567 │ │ 2024 ┆ 25 ┆ TeamC ┆ Away ┆ 0.987654 │ │ 2024 ┆ 25 ┆ TeamD ┆ Home ┆ 1.345678 │ │ 2024 ┆ 25 ┆ TeamE ┆ Away ┆ 1.456789 │ │ 2024 ┆ 26 ┆ TeamA ┆ Home ┆ 1.234567 │ │ 2024 ┆ 26 ┆ TeamB ┆ Away ┆ 1.345678 │ │ 2024 ┆ 26 ┆ TeamC ┆ Home ┆ 0.876543 │ │ 2024 ┆ 26 ┆ TeamD ┆ Away ┆ 1.456789 │ │ 2024 ┆ 26 ┆ TeamE ┆ Home ┆ 1.56789 │ │ 2024 ┆ 27 ┆ TeamA ┆ Away ┆ 2.278759 │ │ 2024 ┆ 27 ┆ TeamB ┆ Away ┆ 1.103829 │ │ 2024 ┆ 27 ┆ TeamC ┆ Home ┆ 0.992563 │ │ 2024 ┆ 27 ┆ TeamD ┆ Home ┆ 1.089324 │ │ 2024 ┆ 27 ┆ TeamE ┆ Home ┆ 1.074221 │ └────────┴─────┴───────┴───────┴───────────────┘ """) result = df.with_columns( pl.col("GeneralLambda") .shift(1) # 取同组内上一行的历史值 .over(["Team", "Field"]) # 按球队和主客场分组 .sort_by("Wk") # 分组内按周数排序,保证历史顺序正确 .fill_null( # 对无历史记录的行填充自定义格式字符串 pl.col("Team").str.extract(r"Team(.*)", 1) + "Last" + pl.col("Field") + "Value" ) .alias("Prev_GeneralLambda") ) print(result)
代码说明:
over(["Team", "Field"]):限定仅在相同球队+相同主客场的组内查找历史记录.shift(1):获取组内上一行的GeneralLambda,由于提前按Wk排序,上一行就是周数更小的最近记录.fill_null(...):对没有历史记录的行(如Wk=25的所有行),自动拼接成你需要的格式字符串
方案二:使用join_asof
如果坚持要用join_asof,可以按以下方式实现:
# 先对原表按分组键和周数排序 sorted_df = df.sort(["Team", "Field", "Wk"]) # 执行asof连接,匹配相同Team+Field且Wk更小的最近行 result = sorted_df.join_asof( sorted_df, by=["Team", "Field"], # 完全匹配的列 on="Wk", # 按周数进行范围匹配 strategy="backward", # 查找小于当前Wk的最大匹配值 suffix="_prev" ).with_columns( # 填充空值并整理列名 pl.col("GeneralLambda_prev") .fill_null( pl.col("Team").str.extract(r"Team(.*)", 1) + "Last" + pl.col("Field") + "Value" ) .alias("Prev_GeneralLambda") ).drop(["Season_prev", "Wk_prev", "GeneralLambda_prev"]) # 移除多余的连接列 print(result)
代码说明:
strategy="backward":确保仅匹配Wk小于当前行的最近记录by=["Team", "Field"]:保证只有球队和主客场完全匹配的行才会被连接- 最后需要手动移除连接产生的重复列,相比窗口函数步骤更繁琐
两种方案都会生成你期望的结果,其中窗口函数方案性能更优,代码更简洁。
内容的提问来源于stack exchange,提问作者Pedro_Siqueira
相关产品推荐
相关产品推荐

