如何将Polars字符串Series转为String数组/Vec并适配lit()表达式
问题:Polars字符串Series转可迭代数组用于循环时的lit()适配问题
核心需求:将Polars中的日期字符串Series转为可迭代对象,遍历每个日期用lit()过滤DataFrame,后续将过滤后的数据转为ndarray做矩阵运算,最终把结果存入新DataFrame并收集到Vec中。
遇到的问题:
- 直接迭代Series的
AnyValue元素时,lit(date)报错AnyValue<'_>未实现Literaltrait - 尝试用
date.to_string()传入lit()虽能编译,但实际传递的是带包裹格式的String("日期"),导致过滤后数据异常,触发LAPACK计算失败 - 手动指定纯字符串数组(如
["2022-12-01","2022-11-11"])则可正常运行
相关代码
#[pyfunction] fn factor_returns(pydf: PyDataFrame, endog: Vec<String>) -> PyResult<PyDataFrame> { let endog_str: Vec<&str> = endog.iter().map(|s| s.as_str()).collect(); let df: DataFrame = pydf.into(); let unique_dates = df.clone() .lazy() .select([col("date").unique()]) .collect() .expect("could not collect"); // let dates = ["2022-12-01","2022-11-11"]; let dates = &unique_dates["date"]; let mut factor_data_dfs: Vec<DataFrame> = vec![]; for date in dates.iter(){ println!("{:?}", date); let df_date = df.clone() .lazy() .filter(col("date").eq(lit(date))) .select([ cols(&endog_str), col("log_returns"), ]) .collect() .expect("could not collect"); let exposures = df_date.clone() .lazy() .select([ cols(&endog_str), ]) .collect() .expect("could not collect"); let returns_vector = df_date.clone() .lazy() .select([col("log_returns")]) .collect() .expect("could not collect"); let ndarray_exposures = exposures.to_ndarray::<Float64Type>(IndexOrder::Fortran).unwrap(); let ndarray_returns = returns_vector.to_ndarray::<Float64Type>(IndexOrder::Fortran).unwrap(); let ma_mb = ndarray_exposures.t().dot(&ndarray_returns); let ma_ma = ndarray_exposures.t().dot(&ndarray_exposures); let ma_ma_inv = ma_ma.inv().unwrap(); let ma = ma_ma_inv.dot(&ma_mb); let ma_vec: Vec<f64> = ma.iter().cloned().collect(); let ma = Series::new("factor_returns", &ma_vec); let endog_factor_names = Series::new("factor_names", &endog_str); let data_ma_df = df!( "date" => [date], "factor_names" => [endog_factor_names], "factor_returns" => [ma], ).unwrap(); factor_data_dfs.push(data_ma_df); } Ok(PyDataFrame()) }
错误信息
错误1:Literal trait未实现
error[E0277]: the trait bound `AnyValue<'_>: polars::prelude::Literal` is not satisfied --> src/lib.rs:144:40 | 144 | .filter(col("date").eq(lit(date))) | --- ^^^^ the trait `polars::prelude::Literal` is not implemented for `AnyValue<'_>` | | | required by a bound introduced by this call | = help: the following other types implement trait `polars::prelude::Literal`: bool i8 i16 i32 i64 u32 u64 f32 and 11 others note: required by a bound in `polars::prelude::lit`
错误2:LAPACK计算失败
String("2023-09-07") thread '<unnamed>' panicked at src/lib.rs:170:37: called `Result::unwrap()` on an `Err` value: Lapack(LapackComputationalFailure { return_code: 1 }) note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace Traceback (most recent call last): line 59, in <module> df3 = factor_analysis.factor_returns(df2,endog) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pyo3_runtime.PanicException: called `Result::unwrap()` on an `Err` value: Lapack(LapackComputationalFailure { return_code: 1 }) python example.py 112.76s user 12.06s system 1031% cpu 12.104 total
Polars Series示例
shape: (781,) Series: 'date' [str] [ "2023-02-02" "2022-11-17" "2022-03-08" "2021-06-24" "2022-04-13" … "2021-01-15" "2023-08-24" "2022-10-07" "2021-08-16" "2023-01-10" ]
解决方案
问题根源在于对Polars Series的迭代方式:Series.iter()返回的是AnyValue枚举类型,无法直接适配lit()的Literal trait要求,且AnyValue的to_string()会输出带包裹的格式,导致过滤逻辑失效。
1. 将Series转为纯字符串Vec
利用Polars的str()方法提取原始字符串值,转换为标准的String类型数组:
// 替换原有的let dates = &unique_dates["date"]; let dates: Vec<String> = unique_dates["date"] .str() .unwrap() .into_iter() .filter_map(|s| s.map(|s| s.to_string())) .collect();
2. 遍历字符串Vec执行逻辑
此时直接将字符串传入lit()即可正常工作,同时避免了AnyValue带来的格式问题:
for date in dates { let df_date = df.clone() .lazy() .filter(col("date").eq(lit(date.clone()))) // 克隆避免所有权转移问题 .select([cols(&endog_str), col("log_returns")]) .collect() .expect("could not collect"); // 后续矩阵运算、结果存储逻辑保持不变 }
3. 性能优化建议
原代码中df.clone()会带来较大性能开销,若数据量较大,建议用Polars的分组操作替代循环,更符合向量化处理的设计风格:
// 可选:用group_by替代循环,需自定义聚合函数实现矩阵运算 let result = df.lazy() .group_by(col("date")) .agg([ // 此处封装矩阵运算逻辑,需实现Polars的Aggregation trait ]) .collect()?;
内容的提问来源于stack exchange,提问作者Trevor Seibert
相关产品推荐
相关产品推荐

