Python時系列データ分析 実践ガイド:pandas・statsmodels・Prophetで予測モデルを構築する

pandas 3.0の時系列機能、statsmodelsのSARIMAXモデル、Prophet 1.3の予測手法を実践コード付きで解説。ADF検定、季節分解、グリッドサーチによるパラメータ最適化、交差検証まで網羅した2026年対応ガイドです。

はじめに:なぜ時系列データ分析が重要なのか

時系列データとは、時間の流れに沿って記録されたデータのことです。株価の変動、月次の売上、気温の推移、Webサイトのアクセス数——ちょっと考えてみると、ビジネスでも科学でも、時間軸を持つデータって本当に多いんですよね。

適切に時系列分析ができれば、将来の売上予測、需要予測、異常検知、トレンドの把握が可能になります。このガイドでは、2026年時点の最新バージョンであるpandas 3.0、statsmodels 0.14.6、Prophet 1.3.0を使い、時系列データの前処理から予測モデルの構築までを実践的なコードとともに解説していきます。

正直に言うと、筆者自身も最初は「SARIMAのパラメータって何?」「ProphetとARIMAどっち使えばいいの?」と混乱しました。そのあたりの迷いやすいポイントも含めて、できるだけ丁寧に説明しますね。

環境構築と必要なライブラリのインストール

まずは必要なライブラリをインストールしましょう。Python 3.11以上の環境を推奨します。

pip install pandas==3.0.0 numpy matplotlib statsmodels==0.14.6 prophet==1.3.0

インストールが終わったら、基本的なインポートを済ませておきます。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.seasonal import seasonal_decompose
from statsmodels.tsa.statespace.sarimax import SARIMAX
from statsmodels.tsa.stattools import adfuller
from prophet import Prophet

pandasによる時系列データの基本操作

DatetimeIndexの作成と活用

pandasで時系列データを扱うなら、まず日付列をDatetimeIndexに変換するのが鉄板です。これだけで、日付ベースのスライスやリサンプリング、ローリング計算といった強力な時系列操作が一気に使えるようになります。

# CSVファイルの読み込みと日付インデックスの設定
df = pd.read_csv("sales_data.csv", parse_dates=["date"], index_col="date")

# 期間を指定したスライス
df_2025 = df["2025"]              # 2025年のデータだけ取得
df_q1 = df["2025-01":"2025-03"]   # 2025年Q1のデータ

# DatetimeIndexの情報を活用
df["month"] = df.index.month
df["day_of_week"] = df.index.dayofweek
df["is_weekend"] = df.index.dayofweek >= 5

年や四半期の指定だけでスライスできるのは、地味に便利です。

リサンプリング(resample)で時間粒度を変換する

resample()は、時系列データの粒度を変更するためのメソッドです。日次データを月次に集計したり、逆に低頻度のデータを高頻度に補間したりできます。

# 日次データを月次に集計
monthly_sales = df["sales"].resample("ME").sum()

# 週次の平均を算出
weekly_avg = df["sales"].resample("W").mean()

# 四半期ごとの集計
quarterly = df["sales"].resample("QE").agg(["sum", "mean", "std"])

ちなみに、pandas 3.0では従来の"M""ME"(month end)に変わっているので注意してください。古い記事のコードをそのままコピペするとエラーになることがあります。

移動平均とローリング計算

rolling()を使えば、移動平均や移動標準偏差の計算が簡単にできます。トレンドの把握やノイズ除去に欠かせない手法ですね。

# 7日間移動平均
df["ma_7"] = df["sales"].rolling(window=7).mean()

# 30日間移動平均
df["ma_30"] = df["sales"].rolling(window=30).mean()

# 移動標準偏差(ボラティリティの把握)
df["rolling_std"] = df["sales"].rolling(window=30).std()

# 可視化
fig, ax = plt.subplots(figsize=(12, 5))
df["sales"].plot(ax=ax, alpha=0.4, label="実績値")
df["ma_7"].plot(ax=ax, label="7日移動平均")
df["ma_30"].plot(ax=ax, label="30日移動平均")
ax.legend()
ax.set_title("売上推移と移動平均")
plt.tight_layout()
plt.show()

7日と30日の移動平均を重ねてプロットすると、短期トレンドと長期トレンドの違いが一目でわかります。個人的には、まずこの可視化から始めるのがおすすめです。

差分化(diff)で定常性を確認する

多くの時系列モデルは「定常性」——つまり時間に依存しない統計的な性質——を前提としています。diff()で差分を取ることで、トレンドや季節性を除去して定常化を目指します。

# 1期前との差分(トレンド除去)
df["diff_1"] = df["sales"].diff(1)

# 12期前との差分(年次季節性の除去)
df["diff_12"] = df["sales"].diff(12)

# 差分後のデータを可視化して定常性を確認
df["diff_1"].dropna().plot(figsize=(12, 4), title="1階差分")
plt.show()

statsmodelsによる時系列分析

ADF検定で定常性を統計的に判定する

グラフを見て「なんとなく定常っぽい」と感じるだけでは不十分です。拡張ディッキー・フラー(ADF)検定を使って、統計的に定常性を確認しましょう。帰無仮説は「単位根が存在する(=非定常である)」なので、p値が0.05未満であれば定常と判断できます。

def check_stationarity(series, name=""):
    result = adfuller(series.dropna())
    print(f"--- ADF検定結果: {name} ---")
    print(f"ADF統計量:   {result[0]:.4f}")
    print(f"p値:         {result[1]:.4f}")
    print(f"使用ラグ数:  {result[2]}")
    for key, val in result[4].items():
        print(f"臨界値 ({key}): {val:.4f}")
    if result[1] < 0.05:
        print("→ 帰無仮説を棄却:このデータは定常です")
    else:
        print("→ 帰無仮説を棄却できず:このデータは非定常です")

# 元データと差分データで検定
check_stationarity(df["sales"], "元データ")
check_stationarity(df["sales"].diff(1).dropna(), "1階差分")

この関数は何度も使い回せるので、スニペットとして保存しておくと便利ですよ。

季節分解(seasonal_decompose)でトレンド・季節性・残差を分離する

seasonal_decomposeは、時系列データをトレンド成分、季節成分、残差の3つに分解してくれます。加法モデル(additive)と乗法モデル(multiplicative)のどちらかを選択できますが、データの値が常に正で、季節変動の振幅がトレンドに比例して大きくなる場合は乗法モデルを選ぶのがセオリーです。

# 加法モデルによる季節分解(周期=12ヶ月)
decomposition = seasonal_decompose(
    df["sales"].dropna(),
    model="additive",
    period=12
)

fig = decomposition.plot()
fig.set_size_inches(12, 8)
fig.suptitle("時系列データの季節分解", y=1.02)
plt.tight_layout()
plt.show()

# 各成分の取得
trend = decomposition.trend
seasonal = decomposition.seasonal
residual = decomposition.resid

SARIMAXモデルで季節性を含む予測を行う

さて、ここからが本番です。SARIMAXは、ARIMAモデルに季節成分と外生変数を加えた強力な時系列予測モデルです。パラメータ(p, d, q)が非季節成分、(P, D, Q, s)が季節成分を表します。

最初はパラメータの意味がピンとこないかもしれませんが、ざっくり言うと、pは過去何期分のデータを参照するか、dは何回差分を取るか、qは過去の予測誤差を何期分使うか、を指定するものです。

# SARIMAXモデルの構築
# (p,d,q) = (1,1,1), (P,D,Q,s) = (1,1,1,12)
model = SARIMAX(
    df["sales"],
    order=(1, 1, 1),
    seasonal_order=(1, 1, 1, 12),
    enforce_stationarity=False,
    enforce_invertibility=False
)
results = model.fit(disp=False)

# モデルの要約を表示
print(results.summary())

# AIC/BICでモデル比較
print(f"\nAIC: {results.aic:.2f}")
print(f"BIC: {results.bic:.2f}")

SARIMAXで将来を予測して可視化する

モデルが構築できたら、いよいよ将来の予測です。信頼区間も一緒にプロットすると、予測の不確実性が視覚的に把握できます。

# 12ヶ月先まで予測
forecast = results.get_forecast(steps=12)
forecast_mean = forecast.predicted_mean
confidence_int = forecast.conf_int()

# 予測結果の可視化
fig, ax = plt.subplots(figsize=(12, 5))
df["sales"].plot(ax=ax, label="実績値")
forecast_mean.plot(ax=ax, label="予測値", color="red")
ax.fill_between(
    confidence_int.index,
    confidence_int.iloc[:, 0],
    confidence_int.iloc[:, 1],
    color="red",
    alpha=0.15,
    label="95%信頼区間"
)
ax.legend()
ax.set_title("SARIMAXモデルによる売上予測")
plt.tight_layout()
plt.show()

パラメータの自動選定(グリッドサーチ)

正直なところ、SARIMAXの(p, d, q)(P, D, Q, s)を手動で決めるのはかなり大変です。AICを基準にしたグリッドサーチで、最適なパラメータの組み合わせを探索するのが現実的でしょう。

import itertools
import warnings
warnings.filterwarnings("ignore")

p = d = q = range(0, 2)
seasonal_pdq = [(x[0], x[1], x[2], 12)
                for x in itertools.product(range(0, 2), repeat=3)]

best_aic = float("inf")
best_params = None

for param in itertools.product(p, d, q):
    for seasonal_param in seasonal_pdq:
        try:
            mod = SARIMAX(
                df["sales"],
                order=param,
                seasonal_order=seasonal_param,
                enforce_stationarity=False,
                enforce_invertibility=False
            )
            res = mod.fit(disp=False)
            if res.aic < best_aic:
                best_aic = res.aic
                best_params = (param, seasonal_param)
        except Exception:
            continue

print(f"最適パラメータ: ARIMA{best_params[0]} x {best_params[1]}")
print(f"AIC: {best_aic:.2f}")

探索範囲を広げすぎると計算時間が爆発するので、まずはrange(0, 2)で試して、ある程度の方向性が見えたら範囲を絞り込む、というやり方がおすすめです。

Prophet 1.3で手軽に高精度な予測を実現する

Prophetの特徴と最新バージョンの改善点

ProphetはMeta社が開発した時系列予測ライブラリで、トレンド・季節性・祝日効果を自動的にモデル化してくれます。統計の専門知識がそこまでなくても使えるのが最大の強みです。

2026年1月にリリースされたProphet 1.3.0では、いくつかの嬉しい改善が入りました。

  • preprocess()calculate_initial_params()メソッドの追加で、前処理の中身が可視化できるように
  • cross_validation()extra_output_columns引数が追加
  • holidays_mode引数の追加で、祝日と季節性のモードを独立して制御可能に
  • scaling引数の追加(absmaxとminmaxから選択可能)
  • pandas 3.0およびNumPy 2.x系との完全互換

Prophetの基本的な使い方

Prophetでは、データをds(日付)とy(目的変数)の2列を持つDataFrameで渡します。このシンプルなインターフェースが、Prophetの使いやすさの理由でもあります。

# データの準備
prophet_df = df.reset_index().rename(
    columns={"date": "ds", "sales": "y"}
)[["ds", "y"]]

# モデルの構築と学習
model = Prophet(
    yearly_seasonality=True,
    weekly_seasonality=True,
    daily_seasonality=False,
    seasonality_mode="additive"
)
model.fit(prophet_df)

# 12ヶ月先の日付フレームを生成
future = model.make_future_dataframe(periods=365)

# 予測
forecast = model.predict(future)

# 主要カラム
print(forecast[["ds", "yhat", "yhat_lower", "yhat_upper"]].tail(10))

予測結果とトレンド成分の可視化

Prophetには予測結果や成分分解をワンライナーで可視化できるメソッドが用意されています。

# 予測結果のプロット
fig1 = model.plot(forecast)
plt.title("Prophet予測結果")
plt.xlabel("日付")
plt.ylabel("売上")
plt.tight_layout()
plt.show()

# トレンド・季節性成分の分解プロット
fig2 = model.plot_components(forecast)
plt.tight_layout()
plt.show()

祝日・イベント効果を追加する

ビジネスデータの場合、祝日やセール期間が売上に大きく影響しますよね。Prophetなら祝日情報をDataFrameで渡すだけで、これらの効果を自動的にモデルへ組み込んでくれます。

# 日本の祝日・イベントを定義
holidays = pd.DataFrame({
    "holiday": [
        "new_year", "golden_week", "obon",
        "christmas_sale", "year_end_sale"
    ],
    "ds": pd.to_datetime([
        "2025-01-01", "2025-05-03", "2025-08-13",
        "2025-12-20", "2025-12-28"
    ]),
    "lower_window": [0, -1, -2, -3, -2],
    "upper_window": [3, 3, 3, 5, 3],
})

# 祝日効果付きモデル(holidays_modeで独立制御)
model_holidays = Prophet(
    holidays=holidays,
    holidays_mode="additive",
    seasonality_mode="multiplicative"
)
model_holidays.fit(prophet_df)

forecast_h = model_holidays.predict(future)
fig = model_holidays.plot_components(forecast_h)
plt.tight_layout()
plt.show()

lower_windowupper_windowで、祝日の前後何日間に影響があるかを指定できるのがポイントです。たとえばクリスマスセールなら開始の3日前から効果が出始める、といった設定が可能です。

交差検証でモデル精度を評価する

Prophet 1.3のcross_validation()を使えば、時系列特有のウォークフォワード検証でモデルの予測精度を評価できます。通常のランダム分割だと未来の情報が漏れてしまうため、時系列では必ずこの方式を使いましょう。

from prophet.diagnostics import cross_validation, performance_metrics

# 交差検証(初期学習365日、予測期間90日、30日ごとにカットオフ)
df_cv = cross_validation(
    model,
    initial="365 days",
    period="30 days",
    horizon="90 days",
    extra_output_columns=["trend"]  # Prophet 1.3新機能
)

# 精度指標の算出
df_metrics = performance_metrics(df_cv)
print(df_metrics[["horizon", "mape", "rmse", "mae"]].head(10))

SARIMAXとProphetの使い分け

「結局どっちを使えばいいの?」という疑問は、時系列分析を始めたばかりの方なら誰でも持つと思います。答えは「データの特性とプロジェクトの要件による」なのですが、それだけだと身も蓋もないので、もう少し具体的に整理してみました。

観点SARIMAXProphet
学習コスト統計的知識が必要初心者にも使いやすい
季節性の扱いパラメータで手動設定自動検出・フーリエ級数で柔軟
祝日・イベント外生変数として手動追加組み込みサポート
欠損値への耐性弱い(補間が必要)強い(自動処理)
複数季節性1つの季節成分のみ年次・週次・日次を同時モデル化
解釈性統計的に厳密成分分解で直感的
外生変数SARIMAXで対応追加リグレッサーで対応
適したデータ量中規模(数百〜数千点)大規模にも対応

実務では、両方のモデルを作ってみて交差検証の結果を比較するのが一番確実です。筆者の経験上、祝日やイベントの影響が大きい小売系のデータではProphetが強く、比較的パターンが安定した製造業のデータではSARIMAXがうまくフィットすることが多いですね。

実践Tips:時系列分析で陥りやすい落とし穴

ここからは、実際に時系列分析をやる中でよくハマるポイントをまとめます。自分も何度か痛い目に遭ったので、ぜひ参考にしてください。

  • リーケージ(情報漏洩):未来の情報が訓練データに混入しないよう、必ず時間順にデータを分割しましょう。ランダム分割は絶対NGです。これを見落とすと、検証時の精度は良いのに本番で全然当たらない、という事態になります。
  • 季節周期の誤設定:月次データならs=12、週次データならs=52など、データの粒度に合った周期を設定してください。日次データにs=12を設定してしまうのは意外とありがちなミスです。
  • 過学習の兆候:訓練データへのフィットが完璧なのにテストデータで精度が劣化する場合は、パラメータを単純化するか正則化を強めましょう。
  • 外れ値の放置:異常値は予測精度に大きく影響します。前処理の段階でIQR法やZスコアで検出・処理しておくのが基本です。
  • 定常性の未確認:ADF検定をスキップしてARIMAモデルを適用すると、予測が大きくずれることがあります。面倒でも必ず検定を実施しましょう。

よくある質問(FAQ)

Q1. pandasのresampleとgroupbyの違いは何ですか?

resample()DatetimeIndexを持つ時系列データ専用の集計メソッドで、時間の粒度を変換できます。一方のgroupby()は任意のカラムでグループ化するための汎用メソッドです。時系列データの集計にはresample()を使う方がコードが簡潔になりますし、欠損期間の処理も自動で行ってくれるのでおすすめです。

Q2. SARIMAのパラメータ(p, d, q)はどうやって決めますか?

基本的なアプローチとしては、dはADF検定で定常になるまでの差分回数、pはPACF(偏自己相関)のカットオフ、qはACF(自己相関)のカットオフから決定します。ただ実務では、本記事で紹介したAICベースのグリッドサーチが最も確実でしょう。手作業で決めると見落としが出がちなので。pmdarimaライブラリのauto_arima()を使って自動選定する方法もあります。

Q3. ProphetとSARIMAXはどちらが精度が高いですか?

これは一概には言えません。祝日やイベントの影響が大きいビジネスデータではProphetが強い傾向がありますし、定常性の高い時系列ではSARIMAXが安定した結果を出すことが多いです。ベストプラクティスとしては、両方のモデルを構築して交差検証でMAPE・RMSEなどの指標を比較することです。

Q4. 時系列データの欠損値はどう処理すべきですか?

時系列データの欠損値処理には、前方補間(ffill)、線形補間(interpolate(method='linear'))、季節性を考慮した補間が有効です。ランダムに補間すると時間的な依存関係が壊れてしまうので、必ず時間の順序を尊重した方法を選んでください。なお、Prophetは欠損値を自動処理してくれますが、SARIMAXでは事前の補間が必要です。

Q5. リアルタイムの時系列予測にはどのライブラリが適していますか?

リアルタイム性が求められる場合は、学習済みモデルの推論速度がカギになります。statsmodelsのSARIMAXは軽量で推論が速く、バッチ予測にも向いています。大量の時系列を同時に処理するならStatsForecastライブラリが高速です。Prophetはモデル構築にやや時間がかかるため、定期的なバッチ予測向きと考えるのがよいでしょう。

著者について Editorial Team

Our team of expert writers and editors.