如何从PyO3传递PyList至Polars表达式?编译错误求助
如何将PyList传递给Polars的.over()方法?
我尝试将PyList传递给Polars表达式,想把period_group传入.over()方法,但遭遇编译错误。
初始尝试代码
#[pyfunction] fn data_bleach(pydf: PyDataFrame, period_group: &Bound<'_, PyList>) -> PyResult<PyDataFrame> { let df: DataFrame = pydf.into(); let df_bleached = df .lazy() .select([ when(col("*").over([period_group]).count().gt(5)) .then(col("*").fill_null(median("*").over(["country", "sector"]))) .otherwise(col("*").fill_null(mean("*").over(["country", "sector"]))) ]) .collect().expect("could not collect"); Ok(PyDataFrame(df_bleached)) }
对应错误
error[E0277]: the trait bound `Expr: From<&pyo3::Bound<'_, PyList>>` is not satisfied --> src/lib.rs:20:27 | 20 | when(col("*").over([period_group]).count().gt(5)) | ^^^^ the trait `From<&pyo3::Bound<'_, PyList>>` is not implemented for `Expr`, which is required by `&pyo3::Bound<'_, PyList>: Into<Expr>`
尝试转换为Vec后的代码
#[pyfunction] fn data_bleach(pydf: PyDataFrame, period_group:&Bound<'_, PyList>) -> PyResult<PyDataFrame> { let list: Vec<String> = pyo3::Bound::extract_bound(&period_group).into_iter() .map(|item| item.to_string()) .collect(); let df: DataFrame = pydf.into(); let df_bleached = df .lazy() .select([ when(col("*").over(list).count().gt(5)) .then(col("*").fill_null(median("*").over(["country", "sector"]))) .otherwise(col("*").fill_null(mean("*").over(["country", "sector"]))) ]) .collect().expect("could not collect"); Ok(PyDataFrame(df_bleached)) }
对应错误
error[E0277]: the trait bound `Expr: From<std::string::String>` is not satisfied --> src/lib.rs:25:27 | 25 | when(col("*").over(list).count().gt(5)) | ^^^^ the trait `From<std::string::String>` is not implemented for `Expr`, which is required by `std::string::String: Into<Expr>` | = help: the following other types implement trait `From<T>`: <Expr as From<bool>> <Expr as From<i8>> <Expr as From<i16>> <Expr as From<i32>> <Expr as From<i64>> <Expr as From<u32>> <Expr as From<u64>> <Expr as From<f32>> and 3 others = note: required for `std::string::String` to implement `Into<Expr>` note: required by a bound in `polars_plan::dsl::<impl Expr>::over` --> /home/bourbon/.cargo/registry/src/index.crates.io-6f17d22bba15001f/polars-plan-0.39.1/src/dsl/mod.rs:908:37 | 908 | pub fn over<E: AsRef<[IE]>, IE: Into<Expr> + Clone>(self, partition_by: E) -> Self { | ^^^^^^^^^^ required by this bound in `polars_plan::dsl::<impl Expr>::over`
另一种尝试的代码片段
let list: Vec<String> = period_group.extract_bound::<Vec<String>>().into_iter().map(|c| col(&c)).collect::<Vec<_>>();
对应错误
error[E0599]: no method named `extract_bound` found for reference `&pyo3::Bound<'_, PyList>` in the current scope --> src/lib.rs:16:42 | 16 | ...ist: Vec<String> = period_group.extract_bound::<Vec<String>>().into_iter().map(|c| col(&c))... | -------------^^^^^^^^^^^^^----------------- | | | | | this is an associated function, not a method | help: use associated function syntax instead: `pyo3::Bound<'_, PyList>::extract_bound()` | = note: found the following associated functions; to be used as methods, functions must have a `self` parameter note: the candidate is defined in the trait `pyo3::FromPyObject` --> /home/bourbon/.cargo/registry/src/index.crates.io-6f17d22bba15001f/pyo3-0.21.2/src/conversion.rs:226:5 | 226 | fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the size for values of type `str` cannot be known at compilation time --> src/lib.rs:16:90 | 16 | ...ng>>().into_iter().map(|c| col(&c)).collect::<Vec<_>>(); | ^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `str` = note: all function arguments must have a statically known size
解决方案
问题核心在于.over()方法接收的是可转换为Expr的类型的集合,而非直接的字符串或PyList。需要先把PyList中的字符串提取出来,再转换成Polars的col表达式:
#[pyfunction] fn data_bleach(pydf: PyDataFrame, period_group: &Bound<'_, PyList>) -> PyResult<PyDataFrame> { // 1. 从PyList提取Vec<String>,处理Python转换错误 let group_cols: Vec<String> = Bound::extract_bound(period_group)?; // 2. 将每个字符串转换为Polars Expr(列表达式) let partition_exprs: Vec<Expr> = group_cols.into_iter().map(|col_name| col(col_name)).collect(); let df: DataFrame = pydf.into(); let df_bleached = df .lazy() .select([ when(col("*").over(&partition_exprs).count().gt(5)) .then(col("*").fill_null(median("*").over(["country", "sector"]))) .otherwise(col("*").fill_null(mean("*").over(["country", "sector"]))) ]) .collect()?; // 用?替代expect,规范处理错误 Ok(PyDataFrame(df_bleached)) }
关键修正点
- 正确使用
Bound::extract_bound提取PyList内容,通过?处理可能的转换错误 - 将提取的字符串逐个转为
col()表达式,得到Vec<Expr>,这是.over()能接受的类型 - 在
.over()中传入&partition_exprs,Vec<Expr>实现了AsRef<[Expr]>,符合方法的类型约束 - 用
?替代expect,遵循PyO3错误处理规范,将错误向上传递为PyResult
内容的提问来源于stack exchange,提问作者Trevor Seibert
相关产品推荐
相关产品推荐

