📑
polarsで曜日操作を行う際の落とし穴
はじめに
polarsで曜日を用いた特徴量を作成して出力を検証したところ、Python標準のdatetime
とpolarsで曜日の扱いに違いがあることに気づきました。
本記事では、曜日の扱いに関する各ライブラリの仕様の違いと、polars特有の落とし穴、そしてその具体的な解決策について整理します。
この記事でわかること
- Python / pandas / polars における曜日の扱いの違い
- polarsで曜日を使う際の注意点と回避策
検証環境
- Python: 3.11.11
- pandas: 2.2.3
- polars: 1.27.0
- OS: macOS
結論
各ライブラリでの曜日の返り値は以下の通り:
ライブラリ | 関数 | 返り値 | 備考 |
---|---|---|---|
Python | datetime.weekday() |
0 (月) 〜 6 (日) | - |
pandas | dt.dayofweek |
0 (月) 〜 6 (日) | Pythonと同じ |
polars | dt.weekday() |
1 (月) 〜 7 (日) | 1始まりのインデックス |
検証
Pythonコード
from datetime import datetime
import pandas as pd
import polars as pl
def compare_weekday_calculations():
dates = [
datetime(2023, 1, 1), # 日曜日
datetime(2023, 1, 2), # 月曜日
datetime(2023, 1, 3), # 火曜日
datetime(2023, 1, 4), # 水曜日
datetime(2023, 1, 5), # 木曜日
datetime(2023, 1, 6), # 金曜日
datetime(2023, 1, 7), # 土曜日
]
df = pl.DataFrame({"date": dates})
pdf = pd.DataFrame({"date": dates})
df_polars_weekday = df.with_columns(pl.col("date").dt.weekday().alias("polars_weekday"))
pandas_weekdays = pdf["date"].dt.dayofweek.tolist()
print(f"{'日付':<12}{'曜日':<8}{'Python':<10}{'pandas':<10}{'Polars':<10}{'調整後':<10}")
day_names = ["月", "火", "水", "木", "金", "土", "日"]
for i, date in enumerate(dates):
python_wd = date.weekday()
pandas_wd = pandas_weekdays[i]
polars_wd = df_polars_weekday[i, "polars_weekday"]
day_name = day_names[python_wd] if python_wd < 6 else "日"
print(f"{date.strftime('%Y-%m-%d')} {day_name:<6}{python_wd:<10}{pandas_wd:<10}{polars_wd:<10}{adjusted_wd:<10}")
出力結果
日付 曜日 Python pandas polars
2023-01-01 日 6 6 7
2023-01-02 月 0 0 1
2023-01-03 火 1 1 2
2023-01-04 水 2 2 3
2023-01-05 木 3 3 4
2023-01-06 金 4 4 5
2023-01-07 土 5 5 6
polarsのみ出力結果が違う。
なぜpolarsだけ違うのか?
polarsはRustで実装されていて、日時処理にはRustの chrono ライブラリが使われている。
chrono の weekday().number_from_monday() は、1 = 月曜日, 7 = 日曜日になっていて、この仕様がpolarsにそのまま反映されている様子。
use chrono::{Datelike, NaiveDate};
fn main() {
let date = NaiveDate::from_ymd_opt(2023, 1, 1).unwrap();
println!("{}", date.weekday().number_from_monday()); // 7 (日曜日)
}
対処法
- 曜日をpolarsで処理した上でPython/pandasと互換性のある形式で使いたい場合は、返り値から -1 することで調整する。
df = df.with_columns((pl.col("date").dt.weekday() - 1).alias("weekday_fixed"))
- 曜日をLabelとして処理を行う。(scikit-learnのLabelEncoderを使って処理する。)
まとめ
- polarsの dt.weekday() は 1〜7(月〜日) を返すため、Pythonやpandasと異なる。
- 特徴量エンジニアリングや曜日ごとの集計処理においては インデックスの違いに注意 が必要。
- 以下の条件を両方満たす場合では明示的な調整が推奨される。
- 曜日を数値として扱う場合
- polars以外のパッケージ(NumPyなど)と併用する場合
Discussion