基于多条件与通配符的Polars DataFrame列值查找优化方案
问题描述
我有如下Polars DataFrame:
df = pl.DataFrame({'Col1': ['a', 'a', 'a', 'b', 'b', 'b', 'b', 'aa', 'aa', 'aa'] , 'Col2': ['c', 'd', 'e', 'c', 'd', 'e', 'f', 'd', 'e', 'f'] , 'Col3': ['i', 'i', 'j', 'T', 'j', 'k', 'l', 'j', 'k', 'l'] , 'Col4': ['m', 'n', 'o', 'p', 'r', 's', 'z', 'r', 's', 'z']})
数据结构如下:
shape: (10, 4) ┌──────┬──────┬──────┬──────┐ │ Col1 ┆ Col2 ┆ Col3 ┆ Col4 │ │ --- ┆ --- ┆ --- ┆ --- │ │ str ┆ str ┆ str ┆ str │ ╞══════╪══════╪══════╪══════╡ │ a ┆ c ┆ i ┆ m │ │ a ┆ d ┆ i ┆ n │ │ a ┆ e ┆ j ┆ o │ │ b ┆ c ┆ T ┆ p │ │ … ┆ … ┆ … ┆ … │ │ b ┆ f ┆ l ┆ z │ │ aa ┆ d ┆ j ┆ r │ │ aa ┆ e ┆ k ┆ s │ │ aa ┆ f ┆ l ┆ z │ └──────┴──────┴──────┴──────┘
同时有一套按优先级排序的输出匹配规则(最后一条为默认规则):
┌──────┬──────┬──────┬──────┬────────┐ │ Col1 ┆ Col2 ┆ Col3 ┆ Col4 ┆ Output │ ╞══════╪══════╪══════╪══════╪════════╡ │ a ┆ * ┆ * ┆ n ┆ out2 │ │ a ┆ * ┆ * ┆ * ┆ out1 │ │ b ┆ * ┆ <>T ┆ * ┆ out2 │ │ aa ┆ d ┆ * ┆ * ┆ out3 │ │ aa ┆ e ┆ * ┆ * ┆ out4 │ │ aa ┆ f ┆ * ┆ * ┆ out5 │ │ * ┆ * ┆ * ┆ * ┆ Unknown│ └──────┴──────┴──────┴──────┴────────┘
期望输出结果:
shape: (10, 5) ┌──────┬──────┬──────┬──────┬─────────┐ │ Col1 ┆ Col2 ┆ Col3 ┆ Col4 ┆ Output │ │ --- ┆ --- ┆ --- ┆ --- ┆ --- │ │ str ┆ str ┆ str ┆ str ┆ str │ ╞══════╪══════╪══════╪══════╪═════════╡ │ a ┆ c ┆ i ┆ m ┆ out1 │ │ a ┆ d ┆ i ┆ n ┆ out2 │ │ a ┆ e ┆ j ┆ o ┆ out1 │ │ b ┆ c ┆ T ┆ p ┆ Unknown │ │ b ┆ d ┆ j ┆ r ┆ out2 │ │ b ┆ e ┆ k ┆ s ┆ out2 │ │ b ┆ f ┆ l ┆ z ┆ out2 │ │ aa ┆ d ┆ j ┆ r ┆ out3 │ │ aa ┆ e ┆ k ┆ s ┆ out4 │ │ aa ┆ f ┆ l ┆ z ┆ out5 │ └──────┴──────┴──────┴──────┴─────────┘
目前我可以用嵌套的pl.when(...).then(...).otherwise(...)实现,但规则数量增多时会非常繁琐。想知道有没有更优的实现方式,考虑过用join,但不确定如何处理通配符和规则优先级。
解决方案
方法一:结构化规则+动态构建条件链
把规则整理成结构化的条件列表,按优先级排序后循环生成when-then逻辑,避免手动嵌套,扩展性更好:
import polars as pl df = pl.DataFrame({'Col1': ['a', 'a', 'a', 'b', 'b', 'b', 'b', 'aa', 'aa', 'aa'] , 'Col2': ['c', 'd', 'e', 'c', 'd', 'e', 'f', 'd', 'e', 'f'] , 'Col3': ['i', 'i', 'j', 'T', 'j', 'k', 'l', 'j', 'k', 'l'] , 'Col4': ['m', 'n', 'o', 'p', 'r', 's', 'z', 'r', 's', 'z']}) # 按优先级从高到低定义规则:(条件表达式, 对应输出值) rules = [ (pl.col("Col1") == "a") & (pl.col("Col4") == "n", "out2"), (pl.col("Col1") == "a", "out1"), (pl.col("Col1") == "b") & (pl.col("Col3") != "T", "out2"), (pl.col("Col1") == "aa") & (pl.col("Col2") == "d", "out3"), (pl.col("Col1") == "aa") & (pl.col("Col2") == "e", "out4"), (pl.col("Col1") == "aa") & (pl.col("Col2") == "f", "out5"), ] # 初始化输出表达式,默认值为Unknown output_expr = pl.lit("Unknown") # 按规则优先级倒序构建条件链(高优先级规则覆盖低优先级) for cond, val in reversed(rules): output_expr = pl.when(cond).then(val).otherwise(output_expr) # 添加Output列到原始DataFrame result_df = df.with_columns(Output=output_expr) print(result_df)
当需要新增规则时,只需要在rules列表中添加对应的(条件, 值)对即可,无需修改嵌套逻辑。
方法二:规则DataFrame+笛卡尔积匹配+优先级过滤
如果规则数量极大或需要从外部加载(比如CSV/数据库),可以把规则转换成DataFrame,通过笛卡尔积匹配后筛选优先级最高的结果:
import polars as pl df = pl.DataFrame({'Col1': ['a', 'a', 'a', 'b', 'b', 'b', 'b', 'aa', 'aa', 'aa'] , 'Col2': ['c', 'd', 'e', 'c', 'd', 'e', 'f', 'd', 'e', 'f'] , 'Col3': ['i', 'i', 'j', 'T', 'j', 'k', 'l', 'j', 'k', 'l'] , 'Col4': ['m', 'n', 'o', 'p', 'r', 's', 'z', 'r', 's', 'z']}) # 规则DataFrame,添加priority列标记优先级(数字越小优先级越高) rules_df = pl.DataFrame({ "Col1": ["a", "a", "b", "aa", "aa", "aa", "*"], "Col2": ["*", "*", "*", "d", "e", "f", "*"], "Col3": ["*", "*", "<>T", "*", "*", "*", "*"], "Col4": ["n", "*", "*", "*", "*", "*", "*"], "Output": ["out2", "out1", "out2", "out3", "out4", "out5", "Unknown"], "priority": [1, 2, 3, 4, 5, 6, 7] }) # 笛卡尔积关联原始数据和规则 cross_df = df.join(rules_df, how="cross") # 构建匹配条件:处理*通配符和<>X不等于规则 match_cond = pl.lit(True) for col in ["Col1", "Col2", "Col3", "Col4"]: match_cond = match_cond & pl.when( pl.col(f"{col}_right") == "*" ).then(True).when( pl.col(f"{col}_right").str.starts_with("<>") ).then(pl.col(col) != pl.col(f"{col}_right").str.slice(2)).otherwise( pl.col(col) == pl.col(f"{col}_right") ) # 筛选匹配的规则行,按原始数据分组后取优先级最高的Output result_df = cross_df.filter(match_cond).group_by(df.columns).agg( pl.col("Output").sort_by("priority").first() ) # 按原始列排序恢复顺序 print(result_df.sort(df.columns))
这种方法完全通过规则DataFrame配置逻辑,无需修改代码,适合规则频繁变动或数量庞大的场景。
内容的提问来源于stack exchange,提问作者Greg Vaysman
相关产品推荐
相关产品推荐

