キャスティング(Casting)
キャスティングは、カラムの基本となる DataType
を新しいものに変換します。Polars は Arrow を使用してメモリ内のデータを管理し、変換を行うために Rust 実装 の計算カーネルに依存しています。キャスティングは cast()
メソッドで利用可能です。
cast
メソッドには strict
パラメータが含まれており、これは Polars がソース DataType
からターゲット DataType
に変換できない値に遭遇したときの動作を決定します。デフォルトでは strict=True
で、これは Polars が変換に失敗したことをユーザーに通知し、キャストできなかった値の詳細を提供するエラーを投げることを意味します。一方、strict=False
の場合、ターゲット DataType
に変換できない値は暗黙に null
に変換されます。
数値
以下の DataFrame
は、整数と浮動小数点数の両方を含んでいます。
df = pl.DataFrame(
{
"integers": [1, 2, 3, 4, 5],
"big_integers": [1, 10000002, 3, 10000004, 10000005],
"floats": [4.0, 5.0, 6.0, 7.0, 8.0],
"floats_with_decimal": [4.532, 5.5, 6.5, 7.5, 8.5],
}
)
print(df)
let df = df! (
"integers"=> &[1, 2, 3, 4, 5],
"big_integers"=> &[1, 10000002, 3, 10000004, 10000005],
"floats"=> &[4.0, 5.0, 6.0, 7.0, 8.0],
"floats_with_decimal"=> &[4.532, 5.5, 6.5, 7.5, 8.5],
)?;
println!("{}", &df);
shape: (5, 4)
┌──────────┬──────────────┬────────┬─────────────────────┐
│ integers ┆ big_integers ┆ floats ┆ floats_with_decimal │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ i64 ┆ f64 ┆ f64 │
╞══════════╪══════════════╪════════╪═════════════════════╡
│ 1 ┆ 1 ┆ 4.0 ┆ 4.532 │
│ 2 ┆ 10000002 ┆ 5.0 ┆ 5.5 │
│ 3 ┆ 3 ┆ 6.0 ┆ 6.5 │
│ 4 ┆ 10000004 ┆ 7.0 ┆ 7.5 │
│ 5 ┆ 10000005 ┆ 8.0 ┆ 8.5 │
└──────────┴──────────────┴────────┴─────────────────────┘
浮動小数点数と整数の間、またはその逆のキャスティング操作を行うには、cast()
関数を呼び出せます。
out = df.select(
pl.col("integers").cast(pl.Float32).alias("integers_as_floats"),
pl.col("floats").cast(pl.Int32).alias("floats_as_integers"),
pl.col("floats_with_decimal")
.cast(pl.Int32)
.alias("floats_with_decimal_as_integers"),
)
print(out)
let out = df
.clone()
.lazy()
.select([
col("integers")
.cast(DataType::Float32)
.alias("integers_as_floats"),
col("floats")
.cast(DataType::Int32)
.alias("floats_as_integers"),
col("floats_with_decimal")
.cast(DataType::Int32)
.alias("floats_with_decimal_as_integers"),
])
.collect()?;
println!("{}", &out);
shape: (5, 3)
┌────────────────────┬────────────────────┬─────────────────────────────────┐
│ integers_as_floats ┆ floats_as_integers ┆ floats_with_decimal_as_integer… │
│ --- ┆ --- ┆ --- │
│ f32 ┆ i32 ┆ i32 │
╞════════════════════╪════════════════════╪═════════════════════════════════╡
│ 1.0 ┆ 4 ┆ 4 │
│ 2.0 ┆ 5 ┆ 5 │
│ 3.0 ┆ 6 ┆ 6 │
│ 4.0 ┆ 7 ┆ 7 │
│ 5.0 ┆ 8 ┆ 8 │
└────────────────────┴────────────────────┴─────────────────────────────────┘
小数値を整数にキャストする場合、これらは下向きに丸められることに注意してください。
ダウンキャスト
要素に割り当てられたビット数を変更することで、メモリフットプリントを削減することも可能です。例として、以下のコードは Int64
から Int16
へ、そして Float64
から Float32
へのキャスティングによってメモリ使用量を低減させる方法を示しています。
out = df.select(
pl.col("integers").cast(pl.Int16).alias("integers_smallfootprint"),
pl.col("floats").cast(pl.Float32).alias("floats_smallfootprint"),
)
print(out)
let out = df
.clone()
.lazy()
.select([
col("integers")
.cast(DataType::Int16)
.alias("integers_smallfootprint"),
col("floats")
.cast(DataType::Float32)
.alias("floats_smallfootprint"),
])
.collect();
match out {
Ok(out) => println!("{}", &out),
Err(e) => println!("{:?}", e),
};
shape: (5, 2)
┌─────────────────────────┬───────────────────────┐
│ integers_smallfootprint ┆ floats_smallfootprint │
│ --- ┆ --- │
│ i16 ┆ f32 │
╞═════════════════════════╪═══════════════════════╡
│ 1 ┆ 4.0 │
│ 2 ┆ 5.0 │
│ 3 ┆ 6.0 │
│ 4 ┆ 7.0 │
│ 5 ┆ 8.0 │
└─────────────────────────┴───────────────────────┘
オーバーフロー
ダウンキャストを行う際は、選択されたビット数(例えば 64、32、16)がカラムに含まれる最大および最小の数値を収容するのに十分であることを確認することが重要です。例えば、32ビット符号付き整数 (Int32
) を使用すると、-2147483648 から +2147483647 の範囲の整数を扱うことができますが、Int8
を使用すると -128 から 127 の整数しか扱うことができません。サイズが小さすぎる DataType
にキャストしようとすると、Polars によってサポートされていない操作として ComputeError
が投げられます。
conversion from `i64` to `i8` failed in column 'big_integers' for 3 out of 5 values: [10000002, 10000004, 10000005]
strict
パラメータを False
に設定することで、オーバーフローするような値を null 値に変換します。
```python exec="on" result="text" session="user-guide/cast out = df.select(pl.col("big_integers").cast(pl.Int8, strict=False)) print(out)
## 文字列
文字列は数値データ型にキャストでき、その逆も同様です:
=== ":fontawesome-brands-python: Python"
[:material-api: `cast`](https://docs.pola.rs/py-polars/html/reference/expressions/api/polars.Expr.cast.html)
```python
df = pl.DataFrame(
{
"integers": [1, 2, 3, 4, 5],
"float": [4.0, 5.03, 6.0, 7.0, 8.0],
"floats_as_string": ["4.0", "5.0", "6.0", "7.0", "8.0"],
}
)
out = df.select(
pl.col("integers").cast(pl.String),
pl.col("float").cast(pl.String),
pl.col("floats_as_string").cast(pl.Float64),
)
print(out)
```
=== ":fontawesome-brands-rust: Rust"
[:material-api: `cast`](https://docs.pola.rs/docs/rust/dev/polars_lazy/dsl/enum.Expr.html#method.cast)
```rust
let df = df! (
"integers" => &[1, 2, 3, 4, 5],
"float" => &[4.0, 5.03, 6.0, 7.0, 8.0],
"floats_as_string" => &["4.0", "5.0", "6.0", "7.0", "8.0"],
)?;
let out = df
.clone()
.lazy()
.select([
col("integers").cast(DataType::String),
col("float").cast(DataType::String),
col("floats_as_string").cast(DataType::Float64),
])
.collect()?;
println!("{}", &out);
```
```python exec="on" result="text" session="user-guide/cast"
df = pl.DataFrame(
{
"integers": [1, 2, 3, 4, 5],
"float": [4.0, 5.03, 6.0, 7.0, 8.0],
"floats_as_string": ["4.0", "5.0", "6.0", "7.0", "8.0"],
}
)
out = df.select(
pl.col("integers").cast(pl.String),
pl.col("float").cast(pl.String),
pl.col("floats_as_string").cast(pl.Float64),
)
print(out)
列に数値でない値が含まれている場合、Polars は変換エラーの詳細を示す ComputeError
を投げます。strict=False
を設定すると、数値でない値を null
に変換します。
df = pl.DataFrame({"strings_not_float": ["4.0", "not_a_number", "6.0", "7.0", "8.0"]})
try:
out = df.select(pl.col("strings_not_float").cast(pl.Float64))
print(out)
except Exception as e:
print(e)
let df = df! ("strings_not_float"=> ["4.0", "not_a_number", "6.0", "7.0", "8.0"])?;
let out = df
.clone()
.lazy()
.select([col("strings_not_float").cast(DataType::Float64)])
.collect();
match out {
Ok(out) => println!("{}", &out),
Err(e) => println!("{:?}", e),
};
conversion from `str` to `f64` failed in column 'strings_not_float' for 1 out of 5 values: ["not_a_number"]
ブール値
ブール値は 1 (True
) または 0 (False
) として表現されます。そのため、数値型の DataType
とブール値の間、またはその逆のキャスティング操作を行うことが可能です。ただし、文字列 (String
) からブール値へのキャスティングは許可されていません。
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],
}
)
out = df.select(pl.col("integers").cast(pl.Boolean), pl.col("floats").cast(pl.Boolean))
print(out)
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 out = df
.clone()
.lazy()
.select([
col("integers").cast(DataType::Boolean),
col("floats").cast(DataType::Boolean),
])
.collect()?;
println!("{}", &out);
shape: (5, 2)
┌──────────┬────────┐
│ integers ┆ floats │
│ --- ┆ --- │
│ bool ┆ bool │
╞══════════╪════════╡
│ true ┆ false │
│ false ┆ true │
│ true ┆ true │
│ true ┆ true │
│ true ┆ true │
└──────────┴────────┘
日付
Date
や Datetime
などの時間データ型は、エポックからの日数(Date
)およびマイクロ秒(Datetime
)、すなわち UNIX 時間として表されます。したがって、数値型と時間データ型の間でのキャスティングが許可されています。
from datetime import date, datetime
df = pl.DataFrame(
{
"date": pl.date_range(date(2022, 1, 1), date(2022, 1, 5), eager=True),
"datetime": pl.datetime_range(
datetime(2022, 1, 1), datetime(2022, 1, 5), eager=True
),
}
)
out = df.select(pl.col("date").cast(pl.Int64), pl.col("datetime").cast(pl.Int64))
print(out)
use chrono::prelude::*;
let date = polars::time::date_range(
"date",
NaiveDate::from_ymd_opt(2022, 1, 1)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap(),
NaiveDate::from_ymd_opt(2022, 1, 5)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap(),
Duration::parse("1d"),
ClosedWindow::Both,
TimeUnit::Milliseconds,
None,
)?
.cast(&DataType::Date)?;
let datetime = polars::time::date_range(
"datetime",
NaiveDate::from_ymd_opt(2022, 1, 1)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap(),
NaiveDate::from_ymd_opt(2022, 1, 5)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap(),
Duration::parse("1d"),
ClosedWindow::Both,
TimeUnit::Milliseconds,
None,
)?;
let df = df! (
"date" => date,
"datetime" => datetime,
)?;
let out = df
.clone()
.lazy()
.select([
col("date").cast(DataType::Int64),
col("datetime").cast(DataType::Int64),
])
.collect()?;
println!("{}", &out);
shape: (5, 2)
┌───────┬──────────────────┐
│ date ┆ datetime │
│ --- ┆ --- │
│ i64 ┆ i64 │
╞═══════╪══════════════════╡
│ 18993 ┆ 1640995200000000 │
│ 18994 ┆ 1641081600000000 │
│ 18995 ┆ 1641168000000000 │
│ 18996 ┆ 1641254400000000 │
│ 18997 ┆ 1641340800000000 │
└───────┴──────────────────┘
文字列と Dates
/Datetimes
の間で変換する場合、dt.to_string
と str.to_datetime
が使用されます。Polars はフォーマットに chrono format syntax を採用しています。str.to_datetime
にはタイムゾーン機能をサポートする追加オプションがあります。さらなる情報については、API ドキュメントを参照してください。
df = pl.DataFrame(
{
"date": pl.date_range(date(2022, 1, 1), date(2022, 1, 5), eager=True),
"string": [
"2022-01-01",
"2022-01-02",
"2022-01-03",
"2022-01-04",
"2022-01-05",
],
}
)
out = df.select(
pl.col("date").dt.to_string("%Y-%m-%d"),
pl.col("string").str.to_datetime("%Y-%m-%d"),
)
print(out)
dt.to_string
· str.replace_all
· Available on feature dtype-date · Available on feature temporal
let date = polars::time::date_range(
"date",
NaiveDate::from_ymd_opt(2022, 1, 1)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap(),
NaiveDate::from_ymd_opt(2022, 1, 5)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap(),
Duration::parse("1d"),
ClosedWindow::Both,
TimeUnit::Milliseconds,
None,
)?;
let df = df! (
"date" => date,
"string" => &[
"2022-01-01",
"2022-01-02",
"2022-01-03",
"2022-01-04",
"2022-01-05",
],
)?;
let out = 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!("{}", &out);
shape: (5, 2)
┌────────────┬─────────────────────┐
│ date ┆ string │
│ --- ┆ --- │
│ str ┆ datetime[μs] │
╞════════════╪═════════════════════╡
│ 2022-01-01 ┆ 2022-01-01 00:00:00 │
│ 2022-01-02 ┆ 2022-01-02 00:00:00 │
│ 2022-01-03 ┆ 2022-01-03 00:00:00 │
│ 2022-01-04 ┆ 2022-01-04 00:00:00 │
│ 2022-01-05 ┆ 2022-01-05 00:00:00 │
└────────────┴─────────────────────┘