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

如何将Polars字符串Series转为String数组/Vec并适配lit()表达式

问题:Polars字符串Series转可迭代数组用于循环时的lit()适配问题

核心需求:将Polars中的日期字符串Series转为可迭代对象,遍历每个日期用lit()过滤DataFrame,后续将过滤后的数据转为ndarray做矩阵运算,最终把结果存入新DataFrame并收集到Vec中。

遇到的问题:

  • 直接迭代Series的AnyValue元素时,lit(date)报错AnyValue<'_>未实现Literal trait
  • 尝试用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 05:21:08