🈷️
月や曜日データを機械学習に使うときの「落とし穴」と「sin/cos変換」のすごい力
~時系列データ前処理の最適解を数学的にやさしく解説~
はじめに
株価や売上など、時系列データの分析・予測では「月(1月, 12月)」や「曜日(日~土)」など周期的なカレンダー情報を特徴量として使うことが多いです。
でも、「1月=1」「12月=12」とそのまま数値で入れるのはNGって知っていましたか?
本記事では、その理由とsin/cos関数を用いた変換(円環特徴量エンコーディング)の本質的なメリットを、初心者にも分かるように数式・図解・コード付きで解説します。
1. 月や曜日を「そのまま数値」で使う落とし穴
1-1. 危険性その1「数値の距離が意味を持たない」
例えば、「月」データ。
- 1月 →
1
- 12月 →
12
と入れてしまうと、1月と12月の距離が「11」もあることになる。
でも本当は、カレンダー上は「1月⇔12月」は1ヶ月しか離れていません(12月→1月はすぐ隣)。
つまり「数字の差=現実の距離」にならず、機械学習モデルが誤解してしまう。
1-2. 危険性その2「順序の誤認識」
また、「12月」→「1月」とカレンダーが循環的に繰り返されることも、そのままでは伝わりません。
曜日も同様で、「日曜=0」「月曜=1」…「土曜=6」としても、「土曜」と「日曜」が「6」も離れてしまう。
2. 周期データを正しく表現する「sin/cos変換」
2-1. 数学的なアイデア:「円(単位円)」に乗せる
周期性(循環性)があるデータは、「直線」上ではなく「円(単位円)」上にマッピングするのが直感的。
1年=12ヶ月、1週間=7日と考えて、それぞれ0~2πの角度に対応させます。
-
月データの場合
\text{θ(月)} = 2π \times \frac{\text{月番号}}{12} -
曜日データの場合(週始めを0とした場合)
\text{θ(曜日)} = 2π \times \frac{\text{曜日番号}}{7}
2-2. sin/cos変換の式と図解
-
sin変換:
\text{sin}(θ) = \text{sin}\left(2π \times \frac{\text{値}}{周期}\right) -
cos変換:
\text{cos}(θ) = \text{cos}\left(2π \times \frac{\text{値}}{周期}\right)
(※単位円上のsin/cosのイメージ)
2-3. Pythonでの実装例
import numpy as np
# 月をsin/cosに変換
df["Month_sin"] = np.sin(2 * np.pi * df["Date"].dt.month / 12)
df["Month_cos"] = np.cos(2 * np.pi * df["Date"].dt.month / 12)
# 曜日(例:月〜金のみ稼働の株価の場合は周期5)をsin/cosに変換
df["DOW_sin"] = np.sin(2 * np.pi * df["Date"].dt.dayofweek / 5)
df["DOW_cos"] = np.cos(2 * np.pi * df["Date"].dt.dayofweek / 5)
※曜日は一般的には
/7
ですが、株式市場など「週5日」稼働の場合は/5
に調整します。
3. sin/cos変換の直感的な効果と機械学習へのメリット
3-1. 「循環性」を適切に表現
- 1月と12月、日曜と土曜など、円の上で近い値として表現される
- 距離感が現実と一致する
3-2. 「同じ周期上でパターン」を学習しやすい
- XGBoostなどのツリー系モデルでも、連続的な値の組み合わせパターンを拾える
- 「季節性」や「曜日パターン」の検出が容易に
3-3. 実際のモデル精度の向上も期待
- 実務でも、周期データのsin/cos変換で予測精度・解釈性が改善する事例多数
4. おまけ:sin/cos変換のペアは必ずセットで!
- sinだけ、cosだけでは「どの位置にいるか」を完全には表現できません。
- **「sinとcosをセットで」**使うことで、円周上の任意の1点(角度)を一意に表現できます。
まとめ
- 月や曜日などの周期データを「そのまま数値」で入れるのはNG
- sin/cos変換を使えば、周期性・循環性を正しくモデルに伝えられる
- 数学的な裏付けと実装も簡単
- 時系列予測や分類の精度・安定性向上に必須のテクニック
Discussion