Skip to content

キャスティング(Casting)

キャスティングは、カラムの基本となる DataType を新しいものに変換します。Polars は Arrow を使用してメモリ内のデータを管理し、変換を行うために Rust 実装 の計算カーネルに依存しています。キャスティングは cast() メソッドで利用可能です。

cast メソッドには strict パラメータが含まれており、これは Polars がソース DataType からターゲット DataType に変換できない値に遭遇したときの動作を決定します。デフォルトでは strict=True で、これは Polars が変換に失敗したことをユーザーに通知し、キャストできなかった値の詳細を提供するエラーを投げることを意味します。一方、strict=False の場合、ターゲット DataType に変換できない値は暗黙に null に変換されます。

数値

以下の DataFrame は、整数と浮動小数点数の両方を含んでいます。

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)

DataFrame

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() 関数を呼び出せます。

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)

cast

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 へのキャスティングによってメモリ使用量を低減させる方法を示しています。

cast

out = df.select(
    pl.col("integers").cast(pl.Int16).alias("integers_smallfootprint"),
    pl.col("floats").cast(pl.Float32).alias("floats_smallfootprint"),
)
print(out)

cast

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 が投げられます。

cast

try:
    out = df.select(pl.col("big_integers").cast(pl.Int8))
    print(out)
except Exception as e:
    print(e)

cast

let out = df
    .clone()
    .lazy()
    .select([col("big_integers").strict_cast(DataType::Int8)])
    .collect();
match out {
    Ok(out) => println!("{}", &out),
    Err(e) => println!("{:?}", e),
};

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

strict パラメータを False に設定することで、オーバーフローするような値を null 値に変換します。

cast

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

cast

let out = df
    .clone()
    .lazy()
    .select([col("big_integers").cast(DataType::Int8)])
    .collect();
match out {
    Ok(out) => println!("{}", &out),
    Err(e) => println!("{:?}", e),
};

```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 に変換します。

cast

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)

cast

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) からブール値へのキャスティングは許可されていません。

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],
    }
)

out = df.select(pl.col("integers").cast(pl.Boolean), pl.col("floats").cast(pl.Boolean))
print(out)

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 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   │
└──────────┴────────┘

日付

DateDatetime などの時間データ型は、エポックからの日数(Date)およびマイクロ秒(Datetime)、すなわち UNIX 時間として表されます。したがって、数値型と時間データ型の間でのキャスティングが許可されています。

cast

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)

cast

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_stringstr.to_datetime が使用されます。Polars はフォーマットに chrono format syntax を採用しています。str.to_datetime にはタイムゾーン機能をサポートする追加オプションがあります。さらなる情報については、API ドキュメントを参照してください。

dt.to_string · str.to_date

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 │
└────────────┴─────────────────────┘