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

如何在Polars IO插件中解析并转换predicate(谓词)?

解析Polars IO插件中的谓词并转换为Python对象

问题背景

Polars的自定义IO插件能实现自定义数据读取逻辑,查询引擎还会自动传递谓词(predicate)来做读取阶段的过滤优化,但官方未明确说明如何将这些Polars表达式转成纯Python可用的对象。

比如你写了这样的自定义scan_data函数:

def scan_data(path: str) -> pl.LazyFrame:

    def source_generator(
        with_columns: list[str] | None,
        predicate: pl.Expr | None,
        n_rows: int | None,
        batch_size: int | None,
    ) -> Iterator[pl.DataFrame]:
        print(predicate.to_string())
        # 这里需要用谓词优化数据读取,但不知道怎么解析它

    return register_io_source(io_source=source_generator, schema=schema)

当你执行带filter的查询时:

from polars_data import scan_data # 你的自定义库

df = scan_data("file-path")
df = df.filter(pl.col("col-name").is_in(["a", "b"]))
df.count()

插件里打印出的谓词是col("channel").is_in([Series])——这是个Polars表达式,里面的列表是Polars Series对象,没法直接给asammdf用,你需要把它转成Python原生列表来过滤通道:

from asammdf import MDF

with MDF(mdf_path, channels=<要填筛选后的通道列表>) as mdf:
    signals = []
    for channel in mdf.iter_channels():
        signal = pa.Table.from_pydict({"timestamp": channel.timestamps, "samples": channel.samples.astype(float)})
        signals.append(signal)
    
    signals = pa.concat_tables(signals)
    signals = signals.sort_by("timestamp")

    return pl.from_arrow(signals)

解决方法

你可以通过Polars表达式的元数据(meta)API来拆解谓词,提取出需要的列名和筛选值:

1. 编写谓词解析函数

针对is_in类型的谓词,写一个解析函数把它转成Python原生的列名和值列表:

def parse_is_in_predicate(predicate: pl.Expr | None) -> tuple[str, list] | None:
    if not predicate:
        return None
    
    # 判断是否是is_in表达式
    if predicate.meta.has_named_function("is_in"):
        # 获取目标列名
        col_name = predicate.meta.root_names()[0].meta.output_name()
        # 获取is_in里的Series参数,转成Python列表
        filter_series = predicate.meta.args()[0]
        filter_values = filter_series.to_list()
        
        return (col_name, filter_values)
    return None

2. 在读取逻辑中应用解析结果

修改你的source_generator,用解析出来的值来过滤asammdf的通道:

def source_generator(
    with_columns: list[str] | None,
    predicate: pl.Expr | None,
    n_rows: int | None,
    batch_size: int | None,
) -> Iterator[pl.DataFrame]:
    parsed_pred = parse_is_in_predicate(predicate)
    target_channels = None
    
    # 如果解析出了针对channel列的is_in筛选,就用这些值过滤通道
    if parsed_pred:
        col_name, filter_vals = parsed_pred
        if col_name == "channel":
            target_channels = filter_vals
    
    with MDF(mdf_path, channels=target_channels) as mdf:
        signals = []
        for channel in mdf.iter_channels():
            signal = pa.Table.from_pydict({
                "timestamp": channel.timestamps, 
                "samples": channel.samples.astype(float)
            })
            signals.append(signal)
        
        signals = pa.concat_tables(signals)
        signals = signals.sort_by("timestamp")
        
        yield pl.from_arrow(signals)

扩展说明

如果需要支持更多类型的谓词(比如等于、大于这类比较操作),可以继续扩展解析函数:

  • 用predicate.meta.has_named_function("eq")判断是否是等于操作
  • 用predicate.meta.args()获取左右操作数,左边是列,右边是常量值
  • 类似地拆解其他表达式结构,把Polars Expr转成Python能直接用的条件判断

内容的提问来源于stack exchange,提问作者Herman Jonsson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 07:06:03