使用Rust Polars自定义函数将&str转换为f64

3phpmpom  于 2023-04-30  发布在  其他
关注(0)|答案(1)|浏览(399)

我的问题可能可以被描述为对Rust和Polars来说都是非常新的。对我宽容点。:)
我尝试使用自定义函数建立一个模式,基于以下文档:https://pola-rs.github.io/polars-book/user-guide/dsl/custom_functions.html,但到目前为止我不成功。
在我的代码中,我有一个函数声明如下:

pub fn convert_str_to_tb(value: &str) -> f64 {
    let value = value.replace(",", "");
    let mut parts = value.split_whitespace();
    let num = parts.next().unwrap().parse::<f64>().unwrap();
    let unit = parts.next().unwrap();

    match unit {
        "KB" => num / (1000.0 * 1000.0 * 1000.0),
        "MB" => num / (1000.0 * 1000.0),
        "GB" => num / 1000.0,
        "TB" => num,
        _ => panic!("Unsupported unit: {}", unit),
    }
}

我相信我应该可以这样调用这个函数:

df.with_columns([
    col("value").map(|s| Ok(convert_str_to_tb(s))).alias("value_tb");
])

我的第一个问题是with_columns方法似乎不存在-我必须使用with_column。如果我使用with_column,我会收到以下错误:

the trait bound `Expr: IntoSeries` is not satisfied
the following other types implement trait `IntoSeries`:
  Arc<(dyn polars::prelude::SeriesTrait + 'static)>
  ChunkedArray<T>
  Logical<DateType, Int32Type>
  Logical<DatetimeType, Int64Type>
  Logical<DurationType, Int64Type>
  Logical<TimeType, Int64Type>
  polars::prelude::SeriesrustcClick for full compiler diagnostic

我尝试转换的DataFrame:

let mut df = df!("volume" => &["volume01", "volume02", "volume03"],
                 "value" => &["1,000 GB", "2,000,000 MB", "3 TB"]).unwrap();

也许有一种方法可以在没有自定义函数的情况下做到这一点?

7eumitmz

7eumitmz1#

问题1,with_columns

关于文档,有一个令人困惑的注意事项--示例中的df是一个惰性 Dataframe 。您可以在使用自定义函数的完整代码片段中看到它们调用.lazy().with_columns()是惰性 Dataframe 上的可用方法。

问题二,自定义函数

在自定义函数中预期的内容和您定义的内容之间存在一些类型问题。您期望输入str并输出f64。然而,由于错误意味着s参数实际上是Series,并且期望返回值是Option<Series>
这是怎么回事.map()函数为您提供了自定义函数需要迭代的序列。
更新您的自定义函数以具有适当的arg和返回类型:

pub fn convert_str_to_tb(value: Series) -> Option<Series> {
    Some(value.iter().map(|v| {
        let value = v.get_str().unwrap().replace(",", "");
        let mut parts = value.split_whitespace();
        let num = parts.next().unwrap().parse::<f64>().unwrap();
        let unit = parts.next().unwrap();

        match unit {
            "KB" => num / (1000.0 * 1000.0 * 1000.0),
            "MB" => num / (1000.0 * 1000.0),
            "GB" => num / 1000.0,
            "TB" => num,
            _ => panic!("Unsupported unit: {}", unit),
        }
    }).collect())
}

并称之为利用

df.lazy().with_columns([
    col("value").map(|s| Ok(convert_str_to_tb(s)), GetOutput::default()).alias("value_tb")
]).collect().unwrap();

给出输出:

shape: (3, 3)
┌──────────┬──────────────┬──────────┐
│ volume   ┆ value        ┆ value_tb │
│ ---      ┆ ---          ┆ ---      │
│ str      ┆ str          ┆ f64      │
╞══════════╪══════════════╪══════════╡
│ volume01 ┆ 1,000 GB     ┆ 1.0      │
│ volume02 ┆ 2,000,000 MB ┆ 2.0      │
│ volume03 ┆ 3 TB         ┆ 3.0      │
└──────────┴──────────────┴──────────┘

相关问题