Skip to content

类型转换

类型转换将列的基础数据类型转换为新的类型。类型转换可以通过cast函数实现。

函数cast包含一个参数strict,该参数决定了Polars在遇到无法从源数据类型转换为目标数据类型时的行为。默认行为是strict=True,这意味着Polars将抛出一个错误来通知用户转换失败,同时提供无法转换的值的详细信息。另一方面,如果strict=False,任何无法转换为目标数据类型的值将静默转换为null

基本示例

让我们看一下以下包含整数和浮点数的数据框:

import polars as pl

df = pl.DataFrame(
    {
        "integers": [1, 2, 3],
        "big_integers": [10000002, 2, 30000003],
        "floats": [4.0, 5.8, -6.3],
    }
)

print(df)
use polars::prelude::*;

let df = df! (
    "integers"=> [1, 2, 3],
    "big_integers"=> [10000002, 2, 30000003],
    "floats"=> [4.0, 5.8, -6.3],
)?;

println!("{}", df);
shape: (3, 3)
┌──────────┬──────────────┬────────┐
│ integers ┆ big_integers ┆ floats │
│ ---      ┆ ---          ┆ ---    │
│ i64      ┆ i64          ┆ f64    │
╞══════════╪══════════════╪════════╡
│ 1        ┆ 10000002     ┆ 4.0    │
│ 2        ┆ 2            ┆ 5.8    │
│ 3        ┆ 30000003     ┆ -6.3   │
└──────────┴──────────────┴────────┘

要在浮点数和整数之间执行类型转换操作,或者反之,我们使用函数 cast

cast

result = df.select(
    pl.col("integers").cast(pl.Float32).alias("integers_as_floats"),
    pl.col("floats").cast(pl.Int32).alias("floats_as_integers"),
)
print(result)

cast

let result = df
    .clone()
    .lazy()
    .select([
        col("integers")
            .cast(DataType::Float32)
            .alias("integers_as_floats"),
        col("floats")
            .cast(DataType::Int32)
            .alias("floats_as_integers"),
    ])
    .collect()?;
println!("{}", result);

shape: (3, 2)
┌────────────────────┬────────────────────┐
│ integers_as_floats ┆ floats_as_integers │
│ ---                ┆ ---                │
│ f32                ┆ i32                │
╞════════════════════╪════════════════════╡
│ 1.0                ┆ 4                  │
│ 2.0                ┆ 5                  │
│ 3.0                ┆ -6                 │
└────────────────────┴────────────────────┘

请注意,浮点数在转换为整数数据类型时会被截断。

降级数值数据类型

您可以通过更改与列的数字数据类型相关的精度来减少其内存占用。例如,下面的代码演示了如何通过从Int64转换为Int16以及从Float64转换为Float32来降低内存使用量:

cast · estimated_size

print(f"降级前: {df.estimated_size()} 字节")
result = df.with_columns(
    pl.col("integers").cast(pl.Int16),
    pl.col("floats").cast(pl.Float32),
)
print(f"降级后: {result.estimated_size()} 字节")

cast · estimated_size

println!("Before downcasting: {} bytes", df.estimated_size());
let result = df
    .clone()
    .lazy()
    .with_columns([
        col("integers").cast(DataType::Int16),
        col("floats").cast(DataType::Float32),
    ])
    .collect()?;
println!("After downcasting: {} bytes", result.estimated_size());

Before downcasting: 72 bytes
After downcasting: 42 bytes

在执行向下转换时,确保选择的位数(如64、32或16)足以容纳列中的最大和最小数字至关重要。例如,32位有符号整数(Int32)表示介于-2147483648和2147483647之间的整数,而8位有符号整数仅表示介于-128和127之间的整数。尝试向下转换为精度不足的数据类型会导致Polars抛出错误:

cast

from polars.exceptions import InvalidOperationError

try:
    result = df.select(pl.col("big_integers").cast(pl.Int8))
    print(result)
except InvalidOperationError as err:
    print(err)

cast

let result = df
    .clone()
    .lazy()
    .select([col("big_integers").strict_cast(DataType::Int8)])
    .collect();
if let Err(e) = result {
    println!("{}", e)
};

conversion from `i64` to `i8` failed in column 'big_integers' for 2 out of 3 values: [10000002, 30000003]

如果将参数strict设置为False,则溢出/下溢的值将转换为null

cast

result = df.select(pl.col("big_integers").cast(pl.Int8, strict=False))
print(result)

cast

let result = df
    .clone()
    .lazy()
    .select([col("big_integers").cast(DataType::Int8)])
    .collect()?;
println!("{}", result);

shape: (3, 1)
┌──────────────┐
│ big_integers │
│ ---          │
│ i8           │
╞══════════════╡
│ null         │
│ 2            │
│ null         │
└──────────────┘

将字符串转换为数值数据类型

表示数字的字符串可以通过类型转换转换为适当的数据类型。相反的转换也是可能的:

cast

df = pl.DataFrame(
    {
        "integers_as_strings": ["1", "2", "3"],
        "floats_as_strings": ["4.0", "5.8", "-6.3"],
        "floats": [4.0, 5.8, -6.3],
    }
)

result = df.select(
    pl.col("integers_as_strings").cast(pl.Int32),
    pl.col("floats_as_strings").cast(pl.Float64),
    pl.col("floats").cast(pl.String),
)
print(result)

cast

let df => df! (
    "integers_as_strings" => ["1", "2", "3"],
    "floats_as_strings" => ["4.0", "5.8", "-6.3"],
    "floats" => [4.0, 5.8, -6.3],
)?;

let result => df
    .clone()
    .lazy()
    .select([
        col("integers_as_strings").cast(DataType::Int32),
        col("floats_as_strings").cast(DataType::Float64),
        col("floats").cast(DataType::String),
    ])
    .collect()?;
println!("{}", result);

shape: (3, 3)
┌─────────────────────┬───────────────────┬────────┐
│ integers_as_strings ┆ floats_as_strings ┆ floats │
│ ---                 ┆ ---               ┆ ---    │
│ i32                 ┆ f64               ┆ str    │
╞═════════════════════╪═══════════════════╪════════╡
│ 1                   ┆ 4.0               ┆ 4.0    │
│ 2                   ┆ 5.8               ┆ 5.8    │
│ 3                   ┆ -6.3              ┆ -6.3   │
└─────────────────────┴───────────────────┴────────┘

如果列包含非数值或格式不良的值,Polars 将抛出一个错误,并提供有关转换错误的详细信息。您可以设置 strict=False 来绕过错误并获取 null 值。

cast

df = pl.DataFrame(
    {
        "floats": ["4.0", "5.8", "- 6 . 3"],
    }
)
try:
    result = df.select(pl.col("floats").cast(pl.Float64))
except InvalidOperationError as err:
    print(err)

cast

let df = df! ("floats" => ["4.0", "5.8", "- 6 . 3"])?;

let result = df
    .clone()
    .lazy()
    .select([col("floats").strict_cast(DataType::Float64)])
    .collect();
if let Err(e) = result {
    println!("{}", e)
};

conversion from `str` to `f64` failed in column 'floats' for 1 out of 3 values: ["- 6 . 3"]

布尔值

布尔值可以表示为1(True)或0(False)。可以在数值数据类型和布尔值之间执行类型转换操作,反之亦然。

将数字转换为布尔值时,数字0被转换为False,而所有其他数字被转换为True,这与Python中数字的真值和假值一致:

cast

df = pl.DataFrame(
    {
        "integers": [-1, 0, 2, 3, 4],
        "floats": [0.0, 1.0, 2.0, 3.0, 4.0],
        "bools": [True, False, True, False, True],
    }
)

result = df.select(
    pl.col("integers").cast(pl.Boolean),
    pl.col("floats").cast(pl.Boolean),
    pl.col("bools").cast(pl.Int8),
)
print(result)

cast

let df = df! (
        "integers"=> [-1, 0, 2, 3, 4],
        "floats"=> [0.0, 1.0, 2.0, 3.0, 4.0],
        "bools"=> [true, false, true, false, true],
)?;

let result = df
    .clone()
    .lazy()
    .select([
        col("integers").cast(DataType::Boolean),
        col("floats").cast(DataType::Boolean),
        col("bools").cast(DataType::UInt8),
    ])
    .collect()?;
println!("{}", result);

shape: (5, 3)
┌──────────┬────────┬───────┐
│ integers ┆ floats ┆ bools │
│ ---      ┆ ---    ┆ ---   │
│ bool     ┆ bool   ┆ i8    │
╞══════════╪════════╪═══════╡
│ true     ┆ false  ┆ 1     │
│ false    ┆ true   ┆ 0     │
│ true     ┆ true   ┆ 1     │
│ true     ┆ true   ┆ 0     │
│ true     ┆ true   ┆ 1     │
└──────────┴────────┴───────┘

解析 / 格式化时间数据类型

所有时间数据类型在内部都表示为自某个参考时刻(通常称为纪元)以来经过的时间单位数。例如,数据类型Date的值存储为自纪元以来的天数。对于数据类型Datetime,时间单位是微秒(us),而对于Time,时间单位是纳秒(ns)。

允许在数值类型和时间数据类型之间进行转换,并揭示了这种关系:

cast

from datetime import date, datetime, time

df = pl.DataFrame(
    {
        "date": [
            date(1970, 1, 1),  # 纪元
            date(1970, 1, 10),  # 9天后
        ],
        "datetime": [
            datetime(1970, 1, 1, 0, 0, 0),  # 纪元
            datetime(1970, 1, 1, 0, 1, 0),  # 1分钟后
        ],
        "time": [
            time(0, 0, 0),  # 参考时间
            time(0, 0, 1),  # 1秒后
        ],
    }
)

result = df.select(
    pl.col("date").cast(pl.Int64).alias("days_since_epoch"),
    pl.col("datetime").cast(pl.Int64).alias("us_since_epoch"),
    pl.col("time").cast(pl.Int64).alias("ns_since_midnight"),
)
print(result)

cast

use chrono::prelude::*;

let df = df!(
    "date" => [
        NaiveDate::from_ymd_opt(1970, 1, 1).unwrap(),  // 纪元
        NaiveDate::from_ymd_opt(1970, 1, 10).unwrap(),  // 9天后
    ],
    "datetime" => [
        NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(),  // 纪元
        NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_opt(0, 1, 0).unwrap(),  // 1分钟后
    ],
    "time" => [
        NaiveTime::from_hms_opt(0, 0, 0).unwrap(),  // 参考时间
        NaiveTime::from_hms_opt(0, 0, 1).unwrap(),  // 1秒后
    ]
)
.unwrap()
.lazy()
// 使时间单位与Python的匹配以获得相同的结果。
.with_column(col("datetime").cast(DataType::Datetime(TimeUnit::Microseconds, None)))
.collect()?;

let result = df
    .clone()
    .lazy()
    .select([
        col("date").cast(DataType::Int64).alias("days_since_epoch"),
        col("datetime")
            .cast(DataType::Int64)
            .alias("us_since_epoch"),
        col("time").cast(DataType::Int64).alias("ns_since_midnight"),
    ])
    .collect()?;
println!("{}", result);

shape: (2, 3)
┌──────────────────┬────────────────┬───────────────────┐
│ days_since_epoch ┆ us_since_epoch ┆ ns_since_midnight │
│ ---              ┆ ---            ┆ ---               │
│ i64              ┆ i64            ┆ i64               │
╞══════════════════╪════════════════╪═══════════════════╡
│ 0                ┆ 0              ┆ 0                 │
│ 9                ┆ 60000000       ┆ 1000000000        │
└──────────────────┴────────────────┴───────────────────┘

要将时间数据类型格式化为字符串,我们可以使用函数dt.to_string,要从字符串解析时间数据类型,我们可以使用函数str.to_datetime。这两个函数都采用chrono格式语法进行格式化。

dt.to_string · str.to_date

df = pl.DataFrame(
    {
        "date": [date(2022, 1, 1), date(2022, 1, 2)],
        "string": ["2022-01-01", "2022-01-02"],
    }
)

result = df.select(
    pl.col("date").dt.to_string("%Y-%m-%d"),
    pl.col("string").str.to_datetime("%Y-%m-%d"),
)
print(result)

dt.to_string · str.replace_all · 功能 temporal 可用 · 功能 dtype-date 可用

let df = df! (
        "date" => [
            NaiveDate::from_ymd_opt(2022, 1, 1).unwrap(),
            NaiveDate::from_ymd_opt(2022, 1, 2).unwrap(),
        ],
        "string" => [
            "2022-01-01",
            "2022-01-02",
        ],
)?;

let result = df
    .clone()
    .lazy()
    .select([
        col("date").dt().to_string("%Y-%m-%d"),
        col("string").str().to_datetime(
            Some(TimeUnit::Microseconds),
            None,
            StrptimeOptions::default(),
            lit("raise"),
        ),
    ])
    .collect()?;
println!("{}", result);

shape: (2, 2)
┌────────────┬─────────────────────┐
│ date       ┆ string              │
│ ---        ┆ ---                 │
│ str        ┆ datetime[μs]        │
╞════════════╪═════════════════════╡
│ 2022-01-01 ┆ 2022-01-01 00:00:00 │
│ 2022-01-02 ┆ 2022-01-02 00:00:00 │
└────────────┴─────────────────────┘

值得注意的是,str.to_datetime 功能提供了支持时区功能的额外选项。有关更多信息,请参阅API文档。