はじめに
この記事は、polarsで日付データを日付型に変換する方法、日付の差分(日単位、月単位、年単位)の取得、および加算した日付を取得する方法の備忘録です。
以下はサンプルデータになります。
import polars as pl # ver. 1.35.2
df = pl.DataFrame({
"購入日_str": ["20001025", "19920221", "20251220"],
"購入日_int": [20001025, 19920221, 20251220],
"購入月_str": ["200010", "199202", "202512"],
"購入月_int": [200010, 199202, 202512],
"返品日_str": ["20011229", "19950430", None],
"返品日_int": [20011229, 19950430, None],
})
print(df)
# shape: (3, 6)
# ┌────────────┬────────────┬────────────┬────────────┬────────────┬────────────┐
# │ 購入日_str ┆ 購入日_int ┆ 購入月_str ┆ 購入月_int ┆ 返品日_str ┆ 返品日_int │
# │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
# │ str ┆ i64 ┆ str ┆ i64 ┆ str ┆ i64 │
# ╞════════════╪════════════╪════════════╪════════════╪════════════╪════════════╡
# │ 20001025 ┆ 20001025 ┆ 200010 ┆ 200010 ┆ 20011229 ┆ 20011229 │
# │ 19920221 ┆ 19920221 ┆ 199202 ┆ 199202 ┆ 19950430 ┆ 19950430 │
# │ 20251220 ┆ 20251220 ┆ 202512 ┆ 202512 ┆ null ┆ null │
# └────────────┴────────────┴────────────┴────────────┴────────────┴────────────┘
日付型への変換
日付がYYYYmmddやYYYYmmの形の文字列型で入っているとき、以下のように.str.to_date()を使うことでpl.Date型に変換できます。
nullが含まれていてもエラーにはならず、そのままnullが返されます。
なお、YYYYmmのフォーマットの日付を.to_date("%Y%m")でpl.Date型に変換したときは、YYYY-mm-ddのddは01になります。
df.select(
pl.col("購入日_str").str.to_date("%Y%m%d"),
pl.col("購入月_str").str.to_date("%Y%m"),
pl.col("返品日_str").str.to_date("%Y%m%d"),
)
# shape: (3, 3)
# ┌────────────┬────────────┬────────────┐
# │ 購入日_str ┆ 購入月_str ┆ 返品日_str │
# │ --- ┆ --- ┆ --- │
# │ date ┆ date ┆ date │
# ╞════════════╪════════════╪════════════╡
# │ 2000-10-25 ┆ 2000-10-01 ┆ 2001-12-29 │
# │ 1992-02-21 ┆ 1992-02-01 ┆ 1995-04-30 │
# │ 2025-12-20 ┆ 2025-12-01 ┆ null │
# └────────────┴────────────┴────────────┘
日付の型が整数などの数値型の場合は、.cast(str)で文字列型に変換してから.str.to_date()をします。
df.select(
pl.col("購入日_int").cast(str).str.to_date("%Y%m%d"),
pl.col("購入月_int").cast(str).str.to_date("%Y%m"),
pl.col("返品日_int").cast(str).str.to_date("%Y%m%d"),
)
# shape: (3, 3)
# ┌────────────┬────────────┬────────────┐
# │ 購入日_int ┆ 購入月_int ┆ 返品日_int │
# │ --- ┆ --- ┆ --- │
# │ date ┆ date ┆ date │
# ╞════════════╪════════════╪════════════╡
# │ 2000-10-25 ┆ 2000-10-01 ┆ 2001-12-29 │
# │ 1992-02-21 ┆ 1992-02-01 ┆ 1995-04-30 │
# │ 2025-12-20 ┆ 2025-12-01 ┆ null │
# └────────────┴────────────┴────────────┘
pl.Date型のYYYY-mm-ddのddにあたる部分を月初にしたいときは.dt.month_start()、月末にしたいときは.dt.month_end()を呼び出します
月末にするときは、うるう年も考慮されます。
df.select(
pl.col("購入日_str").str.to_date("%Y%m%d").alias("購入日"),
pl.col("返品日_str").str.to_date("%Y%m%d").alias("返品日"),
).with_columns(
pl.col("購入日").dt.month_start().alias("購入日_月初"),
pl.col("購入日").dt.month_end().alias("購入日_月末"),
pl.col("返品日").dt.month_start().alias("返品日_月初"),
pl.col("返品日").dt.month_end().alias("返品日_月末"),
)
# shape: (3, 6)
# ┌────────────┬────────────┬─────────────┬─────────────┬─────────────┬─────────────┐
# │ 購入日 ┆ 返品日 ┆ 購入日_月初 ┆ 購入日_月末 ┆ 返品日_月初 ┆ 返品日_月末 │
# │ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
# │ date ┆ date ┆ date ┆ date ┆ date ┆ date │
# ╞════════════╪════════════╪═════════════╪═════════════╪═════════════╪═════════════╡
# │ 2000-10-25 ┆ 2001-12-29 ┆ 2000-10-01 ┆ 2000-10-31 ┆ 2001-12-01 ┆ 2001-12-31 │
# │ 1992-02-21 ┆ 1995-04-30 ┆ 1992-02-01 ┆ 1992-02-29 ┆ 1995-04-01 ┆ 1995-04-30 │
# │ 2025-12-20 ┆ null ┆ 2025-12-01 ┆ 2025-12-31 ┆ null ┆ null │
# └────────────┴────────────┴─────────────┴─────────────┴─────────────┴─────────────┘
ddの箇所を特定の日で統一したいときには、.str.to_date()の前段階で文字列として希望する日を結合しておきます。
YYYYmmddの8桁のフォーマットなら、前から6文字分を抜き出して、例えば'15'を結合してから.str.to_date()をします。
mid_of_month = '15'
df.select(
(pl.col("購入日_str").str.head(6) + mid_of_month).str.to_date("%Y%m%d").alias("購入日_月中"),
(pl.col("返品日_str").str.head(6) + mid_of_month).str.to_date("%Y%m%d").alias("返品日_月中"),
)
# shape: (3, 2)
# ┌─────────────┬─────────────┐
# │ 購入日_月中 ┆ 返品日_月中 │
# │ --- ┆ --- │
# │ date ┆ date │
# ╞═════════════╪═════════════╡
# │ 2000-10-15 ┆ 2001-12-15 │
# │ 1992-02-15 ┆ 1995-04-15 │
# │ 2025-12-15 ┆ null │
# └─────────────┴─────────────┘
日付同士の差分の取得
以下に、pl.Date型同士の差を、年単位、月単位、日単位でそれぞれ取得する方法をまとめます。
-
年単位:比較する日付について、
.dt.year()で取得した年同士を引き算することで取得 -
月単位:比較する日付について、
.dt.year()で計算した年数差に12を掛けたのち、それに.dt.month()で取得した月同士の引き算の差分を足すことで取得 -
日単位:
pl.Date型同士をそのまま引き算したのち、.dt.total_days()で取得
df.select(
pl.col("購入日_str").str.to_date("%Y%m%d").alias("購入日"),
pl.col("返品日_str").str.to_date("%Y%m%d").alias("返品日"),
).with_columns(
(
pl.col("返品日").dt.year() - pl.col("購入日").dt.year()
).alias("年数差"),
(
(pl.col("返品日").dt.year() - pl.col("購入日").dt.year()) * 12
+ (pl.col("返品日").dt.month() - pl.col("購入日").dt.month())
).alias("月数差"),
(
(pl.col("返品日") - pl.col("購入日")).dt.total_days()
).alias("日数差"),
)
# shape: (3, 5)
# ┌────────────┬────────────┬────────┬────────┬────────┐
# │ 購入日 ┆ 返品日 ┆ 年数差 ┆ 月数差 ┆ 日数差 │
# │ --- ┆ --- ┆ --- ┆ --- ┆ --- │
# │ date ┆ date ┆ i32 ┆ i32 ┆ i64 │
# ╞════════════╪════════════╪════════╪════════╪════════╡
# │ 2000-10-25 ┆ 2001-12-29 ┆ 1 ┆ 14 ┆ 430 │
# │ 1992-02-21 ┆ 1995-04-30 ┆ 3 ┆ 38 ┆ 1164 │
# │ 2025-12-20 ┆ null ┆ null ┆ null ┆ null │
# └────────────┴────────────┴────────┴────────┴────────┘
月数差を求める計算が少し長いので、例えば次のような関数を実装しておくと便利です。
from typing import Literal
def get_diff_dates(
date_col1: str,
date_col2: str,
units: Literal["y", "m", "d"]
) -> pl.Expr:
if units == 'y':
return (
pl.col(date_col1).dt.year() - pl.col(date_col2).dt.year()
)
elif units == "m":
return (
(pl.col(date_col1).dt.year() - pl.col(date_col2).dt.year()) * 12
+ (pl.col(date_col1).dt.month() - pl.col(date_col2).dt.month())
)
elif units == "d":
return (
(pl.col(date_col1) - pl.col(date_col2)).dt.total_days()
)
else:
raise ValueError('units must be one of ["y", "m", "d"]')
上記の関数を用いると、年数差や日数差などを同じ使用感で計算できます。
df.select(
pl.col("購入日_str").str.to_date("%Y%m%d").alias("購入日"),
pl.col("返品日_str").str.to_date("%Y%m%d").alias("返品日"),
).with_columns(
get_diff_dates("返品日", "購入日", "y").alias("年数差"),
get_diff_dates("返品日", "購入日", "m").alias("月数差"),
get_diff_dates("返品日", "購入日", "d").alias("日数差"),
)
# shape: (3, 5)
# ┌────────────┬────────────┬────────┬────────┬────────┐
# │ 購入日 ┆ 返品日 ┆ 年数差 ┆ 月数差 ┆ 日数差 │
# │ --- ┆ --- ┆ --- ┆ --- ┆ --- │
# │ date ┆ date ┆ i32 ┆ i32 ┆ i64 │
# ╞════════════╪════════════╪════════╪════════╪════════╡
# │ 2000-10-25 ┆ 2001-12-29 ┆ 1 ┆ 14 ┆ 430 │
# │ 1992-02-21 ┆ 1995-04-30 ┆ 3 ┆ 38 ┆ 1164 │
# │ 2025-12-20 ┆ null ┆ null ┆ null ┆ null │
# └────────────┴────────────┴────────┴────────┴────────┘
日付への加算
.dt.offset_by()を使うことで、特定の値を加算した日付を取得できます。
引数に、年ならy、月ならmo、日ならdの前に加算したい数字を付けます。
このとき、-をつけると数字分を引いた日付になります。
なお、月を意図してmとするとminutesになるので、moにするのを忘れないようにご注意ください。
それぞれの単位は、4y6mo5dのように組み合わせて利用できます。
df.select(
pl.col("返品日_str").str.to_date("%Y%m%d").alias("返品日"),
).with_columns(
pl.col("返品日").dt.offset_by("-2y").alias("2年前"),
pl.col("返品日").dt.offset_by("3mo").alias("3ヶ月後"),
pl.col("返品日").dt.offset_by("4y6mo5d").alias("4年6ヶ月5日後"),
)
# shape: (3, 4)
# ┌────────────┬────────────┬────────────┬───────────────┐
# │ 返品日 ┆ 2年前 ┆ 3ヶ月後 ┆ 4年6ヶ月5日後 │
# │ --- ┆ --- ┆ --- ┆ --- │
# │ date ┆ date ┆ date ┆ date │
# ╞════════════╪════════════╪════════════╪═══════════════╡
# │ 2001-12-29 ┆ 1999-12-29 ┆ 2002-03-29 ┆ 2006-07-04 │
# │ 1995-04-30 ┆ 1993-04-30 ┆ 1995-07-30 ┆ 1999-11-04 │
# │ null ┆ null ┆ null ┆ null │
# └────────────┴────────────┴────────────┴───────────────┘
以上です。