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

Rust Polars:基于现有Series创建新Series并解析字符串时间戳

问题描述

我有一列格式不规范的时间戳字符串,需要将其解析为时间戳类型。Polars文档指出ChunkedArray是字符串的类型化容器,但我无法完成完整实现。尝试编写了如下代码:

fn with_timestamps(mut df: DataFrame) -> Result<DataFrame, PolarsError> {
    let column = df.column("myTime")?.clone(); // clone, just for a good measure ...

    let ca = column.utf8()?; // ChunkedArray

    // I think I want something like this:
    let new_time = Series::new("newTime", ca.into_iter().map(|v: &str| 42).collect());

    // 42 is not a timestamp,
    // but maybe I can work on that from an integer

    df.with_column(new_time);
    df
}

我有几个疑问:

  • .with_column()是否为原地操作?
  • 应该遍历Series还是ChunkedArray?
  • 如何正确构造新的Series?

另外,我找到相关答案后写出了可行的lazy API示例,但不理解为什么必须使用.lazy()——如果不用,Expr(比如col("Foo").alias("bar"))无法直接使用,希望了解Polars的惯用实现方式:

let df = df!("Fruit" => &["Apple", "Apple", "Pear"],
                            "Color" => &["Red", "Yellow", "Green"],
                            "Date" => &["02/21/2022 07:51:00 AM", "2/21/2022 07:51:00 AM", "2/21/2022 07:51:00 AM"])?;
let options = StrpTimeOptions {
    fmt: Some("%-m/%-d/%Y %I:%M:%S %p".into()),
    date_dtype: polars::datatypes::DataType::Datetime(TimeUnit::Milliseconds, None),
    exact: true,
    ..Default::default()
};

let foo = df
    .clone()
    .lazy()
    .with_columns([
       col("Date")
       .str()
       .strptime(options)
       .alias("parsed date")
     ])
    .collect();
惯用实现方案

1. 关于.with_column()的原地操作问题

.with_column()不是原地操作,它会返回一个包含新列的DataFrame实例,原DataFrame不会被修改。你代码中df.with_column(new_time);没有重新赋值,相当于丢弃了新生成的DataFrame,正确写法是:

df = df.with_column(new_time)?;

注意要处理with_column()返回的Result,用?或match处理错误。

2. 遍历选择:优先用向量化操作,避免手动遍历

Polars的核心优势是向量化计算,手动遍历ChunkedArray迭代器会失去性能优化。解析时间戳不需要手动遍历,直接用Utf8ChunkedArray提供的strptime向量化方法即可,它会批量处理所有元素,效率远高于手动循环。

3. 两种惯用实现方式

方式一:Eager API(立即执行)

直接操作ChunkedArray完成解析,适合简单的列转换场景:

use polars::prelude::*;

fn with_timestamps(mut df: DataFrame) -> Result<DataFrame, PolarsError> {
    // 获取目标列的Utf8类型ChunkedArray
    let str_ca = df.column("myTime")?.utf8()?;
    
    // 定义时间解析规则,根据你的实际时间格式调整fmt参数
    let parse_options = StrpTimeOptions {
        fmt: Some("%-m/%-d/%Y %I:%M:%S %p".into()),
        date_dtype: DataType::Datetime(TimeUnit::Milliseconds, None),
        exact: true,
        ..Default::default()
    };
    
    // 向量化解析字符串为Datetime类型的ChunkedArray
    let datetime_ca = str_ca.strptime(parse_options)?;
    
    // 将ChunkedArray转为Series并添加到DataFrame
    let new_series = datetime_ca.into_series().rename("newTime");
    df = df.with_column(new_series)?;
    
    Ok(df)
}

方式二:Lazy API(延迟执行)

先构建查询计划,最后通过collect()执行,适合复杂查询场景(Polars会自动做查询优化,比如列裁剪、谓词下推):

use polars::prelude::*;

fn with_timestamps_lazy(df: DataFrame) -> Result<DataFrame, PolarsError> {
    let parse_options = StrpTimeOptions {
        fmt: Some("%-m/%-d/%Y %I:%M:%S %p".into()),
        date_dtype: DataType::Datetime(TimeUnit::Milliseconds, None),
        exact: true,
        ..Default::default()
    };
    
    let result_df = df
        .lazy()
        .with_columns([
            col("myTime")
                .str()
                .strptime(parse_options)
                .alias("newTime")
        ])
        .collect()?;
    
    Ok(result_df)
}

关于Lazy vs Eager的区别

  • Eager API:操作的是实际存在的Series/ChunkedArray,调用方法时立即执行计算,适合简单、直观的操作。
  • Lazy API:操作的是Expr表达式,它代表要执行的计算逻辑,但不会立即执行。直到调用collect()时,Polars才会优化查询计划并执行计算,适合复杂查询,性能更优。

你之前困惑的“为什么eager不能用Expr”,是因为Expr是Lazy API的专属抽象,它描述的是要做什么,而非直接操作数据;而Eager API需要直接操作具体的数据实例(Series/ChunkedArray),所以要用对应的方法(比如ChunkedArray::strptime)而非Expr。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 10:30:08