概要(時系列の“型”を整えて、時間軸で正しく集計・解析する)
時系列処理の基礎は、日付や時刻をdatetime型へ変換し、時間順に並べてから「期間集計」「移動統計」「シフトや差分」などの操作を行うことです。pandasはDateTimeIndex(またはon=列指定)で時系列を強力に扱えます。最初の一歩で型と順序を整えるだけで、後の集計・可視化・異常検知が安全に回ります。
datetime化と順序固定(to_datetime→並べ替え→インデックス設定)
文字列を日時へ変換して安全運転に
import pandas as pd
df = pd.DataFrame({
"date": ["2025-01-02", "2024-12-31", "2025/01/01", "bad"],
"sales": [100, 120, 200, 150]
})
df["date"] = pd.to_datetime(df["date"], errors="coerce") # 変換不可はNaT
df = df.dropna(subset=["date"]).sort_values("date")
print(df.dtypes)
# date datetime64[ns]
Python日時型へ変換してから並べ替えるのが鉄則です。errors=”coerce”で壊れた値をNaTにでき、後で対処しやすくなります。
インデックスに載せると時系列APIが使いやすい
df = df.set_index("date")
# df.resample(...), df.rolling(...), df.loc["2025-01"] などが直感的に書ける
Pythonインデックスを日時にすることで、期間指定やリサンプリングが一気にシンプルになります。
期間集計(resample)と移動統計(rolling)
日次→月次へまとめる(ダウンサンプリング)
monthly = df.resample("M")["sales"].agg(["sum", "mean", "count"])
print(monthly)
Python頻度文字列(”D”,”W”,”M”,”MS”,”H”,”T”など)を指定し、期間ごとの合計・平均・件数を計算できます。
過去7日移動平均で平滑化(期間幅のrolling)
ma7d = df["sales"].rolling("7D").mean() # 直近7日間の平均
ma7d_center = df["sales"].rolling("7D", center=True).mean() # 表示上のズレを減らす
Python期間幅の窓は不等間隔にも自然に対応します。center=Trueは折れ線の視覚的整合に便利です。
固定幅N点の移動統計(ノイズ除去や変動把握)
roll = df["sales"].rolling(window=3, min_periods=2)
smooth = roll.mean() # 移動平均
vol = roll.std() # 移動標準偏差(揺らぎ)
Pythonmin_periodsで初期のNaNを抑え、レポートや異常検知の指標へ繋げます。
差分・シフト・期間抽出(時間軸で“変化”と“範囲”を扱う)
前日比・前週比などの差分
df["diff_1d"] = df["sales"].diff(1) # 1ステップ差分(例:前日比)
df["pct_change"] = df["sales"].pct_change() # 前ステップ比率
Pythondiffやpct_changeで変化量・変化率を簡単に計算できます。
時系列シフト(ラグ特徴量)
df["lag_1"] = df["sales"].shift(1) # 1ステップ前の値
df["lag_7"] = df["sales"].shift(7) # 7ステップ前(例:週次ラグ)
Python予測や異常検知で「過去の値」を特徴量として持たせられます。
期間指定で一発抽出(文字列スライスが使える)
jan = df.loc["2025-01"] # 2025年1月の範囲
range_df = df.loc["2024-12-31":"2025-01-02"] # 端点を含む範囲抽出
PythonDateTimeIndexなら直感的な文字列で範囲を切れます。
タイムゾーンと等間隔化(UTC統一→ローカル、asfreqで穴を見える化)
タイムゾーンの基本設計
ts_utc = pd.to_datetime(["2024-12-31T15:00:00Z","2025-01-01T03:00:00Z"], utc=True)
ts_jst = ts_utc.tz_convert("Asia/Tokyo")
Python混在TZはまずUTCへ正規化し、表示や集計で必要なローカルTZへ変換すると安全です。
等間隔へ再インデックス(補間前の下ごしらえ)
s = pd.Series([100, 120], index=pd.to_datetime(["2025-01-01 10:00","2025-01-01 12:00"]))
eq = s.asfreq("H") # 1時間間隔に再インデックス(欠損はNaN)
filled = eq.ffill() # 前方補完で穴埋め
Pythonasfreqで“穴”を可視化し、ffill/bfill/interpolateで文脈に合う補間を選びます。
例題(実務で通る“型”をひとまとめ)
売上ログをJSTへ揃え、日次レポートを作る
log = pd.DataFrame({
"ts": ["2024-12-31T15:00:00Z","2025-01-01T02:00:00Z","2025-01-02T01:00:00Z"],
"sales": [10, 20, 15]
})
ts_utc = pd.to_datetime(log["ts"], utc=True).dt.tz_convert("Asia/Tokyo")
daily = pd.DataFrame({"date": ts_utc.dt.date, "sales": log["sales"]})
report = daily.groupby("date", as_index=False)["sales"].sum().sort_values("date")
print(report)
Python時差を正しく処理してからグルーピングすると、日付またぎのズレがなく信頼できるレポートになります。
不規則な観測を7日移動平均で平滑化
df = pd.DataFrame({
"date": pd.to_datetime(["2025-01-01","2025-01-02","2025-01-05","2025-01-10","2025-01-11"]),
"value": [100, 120, 80, 200, 150]
}).set_index("date")
smooth = df["value"].rolling("7D").mean()
print(smooth)
Python期間窓のrollingは不等間隔データでも自然に使えます。グラフ化するとトレンドが一目で掴めます。
月次集計と異常候補の検出(短期平均からの乖離)
monthly = df.resample("M")["value"].sum()
ma3 = df["value"].rolling(3, min_periods=2).mean()
sd3 = df["value"].rolling(3, min_periods=2).std()
z = (df["value"] - ma3) / sd3
anomaly = df[z.abs() >= 2]
Python期間集計で俯瞰しつつ、短期の標準化乖離で“飛び値”を候補として抽出します。
よくあるつまずきと対策(型・順序・欠損・境界の意味)
datetime型にしてから操作する
文字列のままでは時系列APIが動きません。to_datetimeで型を揃え、NaTはdropnaやfillnaの方針を決めてから前へ進みます。
並べ替えは先に
sort_values(“date”)(またはインデックス順)で昇順を固定してからresampleやrollingを使うと、窓や区間の挙動が安定します。
欠損(NaT/NaN)は“意味のある穴”
アップサンプルや壊れた日付で欠損は必ず出ます。補間の前提(保持・前方補完・後方補完・線形)を最初に決め、dtypeの変化に注意します。
期間の境界とラベル位置
週次や月次レポートではclosed/labelの指定で「どの日に属するか」「どこにラベルを置くか」を明示します。仕様に合わせて統一すると誤解が減ります。
まとめ(「datetimeへ→順序固定→期間集計・移動統計・差分」。TZと補間は設計で決める)
時系列処理の土台は、日時型への変換と昇順固定です。その上でresampleで期間集計、rollingで移動統計、diff/pct_change/shiftで変化とラグを扱う。タイムゾーンはUTC統一→必要に応じてローカルへ、アップサンプリングの欠損は補間戦略を先に決める。この型を身につければ、初心者でも“正確で説得力のある”時系列前処理とレポートを安定して作れます。
