
本文介绍如何在 Polars 中根据每行数据自定义周末(WKND1/WKND2)动态生成 week_mask,并调用 add_business_days 实现真正灵活的业务日计算——避免因全局固定周末导致的日期偏移。
本文介绍如何在 polars 中根据每行数据自定义周末(wknd1/wknd2)动态生成 `week_mask`,并调用 `add_business_days` 实现真正灵活的业务日计算——避免因全局固定周末导致的日期偏移。
在 Polars 中,pl.Expr.dt.add_business_days() 的 week_mask 参数要求是 Python 原生的可迭代对象(如 tuple 或 list),不支持直接传入表达式(Expr)。这意味着你无法通过 pl.col("W_MASK") 动态传递每行不同的掩码——这是初学者常遇的 TypeError: argument 'week_mask': 'Expr' object cannot be converted to 'Sequence' 根源。
要实现「每条记录独立指定周末」,核心思路是:按周末组合分组 → 在每组内统一构造掩码 → 分别计算 → 合并结果。Polars 提供了高效且语义清晰的 partition_by(..., as_dict=True) 来完成这一流程。
以下是完整、可运行的解决方案:
import polars as pl
# 示例数据
df = pl.DataFrame({
"DT": [date(2025, 1, 2), date(2025, 1, 9), date(2025, 1, 10), date(2025, 1, 15)],
"N_DAYS": [2, 2, 2, 1],
"WKND1": ["Saturday", "Friday", "Saturday", "Saturday"],
"WKND2": ["Sunday", "Saturday", None, "Sunday"]
})
# 一周七天标准顺序(周一至周日)
weekdays = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
# 按 WKND1 + WKND2 分组,对每组构造 week_mask 并计算 NEW_DT
result = pl.concat([
df_group.with_columns(
pl.col("DT")
.dt.add_business_days(
pl.col("N_DAYS"),
# ✅ 关键:基于分组键 k 构造布尔列表
# k 是元组 (wknd1, wknd2),例如 ("Friday", "Saturday")
week_mask=[day not in k for day in weekdays]
)
.alias("NEW_DT")
)
for k, df_group in df.partition_by("WKND1", "WKND2", as_dict=True).items()
]).sort("DT")
print(result)
输出结果将严格匹配预期:
shape: (4, 5) ┌────────────┬────────┬──────────┬──────────┬────────────┐ │ DT ┆ N_DAYS ┆ WKND1 ┆ WKND2 ┆ NEW_DT │ │ --- ┆ --- ┆ --- ┆ --- ┆ --- │ │ date ┆ i64 ┆ str ┆ str ┆ date │ ╞════════════╪════════╪══════════╪══════════╪════════════╡ │ 2025-01-02 ┆ 2 ┆ Saturday ┆ Sunday ┆ 2025-01-06 │ │ 2025-01-09 ┆ 2 ┆ Friday ┆ Saturday ┆ 2025-01-14 │ ← 注意:正确跳过 Fri/Sat,+2 → Mon/Tue │ 2025-01-10 ┆ 2 ┆ Saturday ┆ null ┆ 2025-01-13 │ ← WKND2=null → 仅跳过 Sat,+2 → Mon/Tue │ 2025-01-15 ┆ 1 ┆ Saturday ┆ Sunday ┆ 2025-01-16 │ └────────────┴────────┴──────────┴──────────┴────────────┘
? 关键注意事项:
-
partition_by(..., as_dict=True)返回的是{(wknd1, wknd2): DataFrame}字典,k即为该组的周末标识元组(含None); -
week_mask中True表示工作日,False表示非工作日(即周末);因此使用day not in k逻辑——若某天在WKND1/WKND2中,则标记为False(非工作日); -
None值(如第三行WKND2=null)在元组中保留为None,"Saturday" not in ("Saturday", None)仍为False,逻辑安全; - 此方法保持 Polars 的惰性计算优势(分组内仍为表达式链),且比逐行
apply高效得多; - 若数据量极大(百万级+),建议先
drop_nulls(subset=["WKND1"])过滤无效配置,避免空掩码异常。
✅ 总结:当 week_mask 需动态化时,分组 + 掩码构造 + concat 是 Polars 中最简洁、健壮且符合其计算范式的解法。它绕过了 Expr 与 Python 原生类型之间的边界限制,同时保证了语义清晰与执行效率。










