类型转换
类型转换将列的底层数据类型转换为新的数据类型。类型转换可以通过函数 cast
实现。
函数 cast
包含一个参数 strict
,它决定了 Polars 在遇到无法从源数据类型转换为目标数据类型的值时的行为。默认行为是 strict=True
,这意味着 Polars 将抛出错误,通知用户转换失败,同时提供无法转换的值的详细信息。另一方面,如果 strict=False
,任何无法转换为目标数据类型的值将被静默转换为 null
。
基本示例
让我们来看看下面包含整数和浮点数的 DataFrame
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
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)
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
来降低内存使用量
print(f"Before downcasting: {df.estimated_size()} bytes")
result = df.with_columns(
pl.col("integers").cast(pl.Int16),
pl.col("floats").cast(pl.Float32),
)
print(f"After downcasting: {result.estimated_size()} bytes")
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 抛出错误。
from polars.exceptions import InvalidOperationError
try:
result = df.select(pl.col("big_integers").cast(pl.Int8))
print(result)
except InvalidOperationError as err:
print(err)
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
shape: (3, 1)
┌──────────────┐
│ big_integers │
│ --- │
│ i8 │
╞══════════════╡
│ null │
│ 2 │
│ null │
└──────────────┘
将字符串转换为数值数据类型
表示数字的字符串可以通过类型转换转换为相应的数据类型。反向转换也是可行的。
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)
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
值。
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)
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 中数字的 Truthy 和 Falsy 值保持一致。
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)
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)。
数值类型和时间数据类型之间允许进行类型转换,这揭示了它们之间的关系。
from datetime import date, datetime, time
df = pl.DataFrame(
{
"date": [
date(1970, 1, 1), # epoch
date(1970, 1, 10), # 9 days later
],
"datetime": [
datetime(1970, 1, 1, 0, 0, 0), # epoch
datetime(1970, 1, 1, 0, 1, 0), # 1 minute later
],
"time": [
time(0, 0, 0), # reference time
time(0, 0, 1), # 1 second later
],
}
)
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)
use chrono::prelude::*;
let df = df!(
"date" => [
NaiveDate::from_ymd_opt(1970, 1, 1).unwrap(), // epoch
NaiveDate::from_ymd_opt(1970, 1, 10).unwrap(), // 9 days later
],
"datetime" => [
NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(), // epoch
NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_opt(0, 1, 0).unwrap(), // 1 minute later
],
"time" => [
NaiveTime::from_hms_opt(0, 0, 0).unwrap(), // reference time
NaiveTime::from_hms_opt(0, 0, 1).unwrap(), // 1 second later
]
)
.unwrap()
.lazy()
// Make the time unit match that of Python's for the same results.
.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 格式语法进行格式化。
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
· 在功能 `dtype-date` 中可用 · 在功能 `temporal` 中可用
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 文档了解更多信息。