Rの関数:decompose {stats}

Rの関数から decompose {stats} を確認します。

関数 decompose とは

decompose は、時系列データを「トレンド成分」「季節成分」「不規則成分(ランダムノイズ)」の3つの要素に分解するための関数です。本手法は「古典的分解法(Classical Decomposition)」と呼ばれ、時系列分析の基礎的なアプローチとして利用されています。

分解のアプローチには以下の2種類が存在します。

  1. 加法モデル (Additive Model):

    • 観測値 \(=\) トレンド \(+\) 季節成分 \(+\) 不規則成分
    • データの水準が変化しても、季節変動の振幅が一定である場合に適しています。
  2. 乗法モデル (Multiplicative Model):

    • 観測値 \(=\) トレンド \(\times\) 季節成分 \(\times\) 不規則成分
    • データの水準が上昇するにつれて、季節変動の振幅も拡大していくようなデータ構造に適しています。

本関数は、まず移動平均を用いてデータ全体からトレンドを抽出します。次に、トレンドを除去したデータから周期ごとの平均を計算して季節成分を抽出し、最後に残った部分を不規則成分として出力します。

関数 decompose の引数

args(decompose)
function (x, type = c("additive", "multiplicative"), filter = NULL) 
NULL
  1. x

    • 分解対象となる単変量の時系列データオブジェクト(ts クラス)。
    • データの周期(frequency)が2以上であり、かつ少なくとも2周期分以上のデータ長を持つ必要があります。当該条件を満たさない場合、エラーが発生します。
  2. type

    • 適用する分解モデルの種類を指定する文字列。
    • "additive" (デフォルト): 加法モデルを適用します。
    • "multiplicative": 乗法モデルを適用します。
  3. filter

    • トレンド成分を推定する際に使用する、移動平均の重み(フィルタ係数)を表す数値ベクトル。
    • デフォルト: NULL
    • 指定が省略された場合、関数の内部でデータの周期(f)に基づいた中心化移動平均フィルタが自動生成されます。周期が偶数の場合、両端の重みが 0.5/f、間の重みが 1/f となるフィルタが作られます(例:12ヶ月周期の場合、長さ13のフィルタ)。周期が奇数の場合は、すべての重みが均等な 1/f となります。

シミュレーションコード

以下に、decompose 関数の機能を確認するためのサンプルデータを用いたシミュレーションコードを示します。

本シミュレーションでは、明確なトレンドと季節性、そしてノイズを意図的に合成したデータを作成し、関数が元の成分をどれほど正確に抽出できるかを可視化して確認します。

時系列データの生成

  • データの構成
    • 観測値 = トレンド + 季節成分 + 不規則成分
    • トレンド: 右肩上がりの直線
    • 季節成分: 12期サイクルの波
    • 不規則成分: ランダムなノイズ
# パッケージの読み込み
library(ggplot2)
library(tidyr)

# 乱数シードの固定
seed <- 20260315
set.seed(seed)

# 加法モデルに適したデータを生成
# 周期12(月次データ相当)、5周期分の長さを設定
freq <- 12
n_periods <- 5
n_obs <- freq * n_periods

time_index <- 1:n_obs

# 各成分の真の値を設定
# 1. トレンド: 直線的な上昇
true_trend <- 50 + 0.8 * time_index
# 2. 季節成分: 一定の振幅を持つサインカーブ
true_season <- 15 * sin(2 * pi * time_index / freq)
# 3. 不規則成分: 正規乱数
true_random <- rnorm(n_obs, mean = 0, sd = 3)

# 観測データの合成(加法モデル)
obs_data <- true_trend + true_season + true_random
# 時系列オブジェクト(ts)に変換
ts_data <- ts(obs_data, frequency = freq)

# サンプルデータの可視化
plot(ts_data)
Figure 1

decompose 関数による分解

# 加法モデルを指定して分解を実行
dec_res <- decompose(ts_data, type = "additive")

# 結果の要素を確認
cat("--- decompose関数の出力リストの要素 ---\n")
print(names(dec_res))

# 抽出された各成分をデータフレームにまとめる
# グラフ描画用に整形します
df_plot <- data.frame(
  時点 = time_index,
  観測値 = as.numeric(dec_res$x),
  トレンド成分 = as.numeric(dec_res$trend),
  季節成分 = as.numeric(dec_res$seasonal),
  不規則成分 = as.numeric(dec_res$random)
)

# 縦持ちデータに変換
df_long <- pivot_longer(df_plot, cols = -時点, names_to = "成分", values_to = "値")

# グラフの並び順を論理的な順番に固定
df_long$成分 <- factor(df_long$成分, levels = c("観測値", "トレンド成分", "季節成分", "不規則成分"))

# プロットの作成
p1 <- ggplot(df_long, aes(x = 時点, y = 値)) +
  geom_line(color = "darkblue", linewidth = 0.5) +
  facet_wrap(~成分, ncol = 1, scales = "free_y") +
  labs(
    title = "decompose関数による古典的時系列分解(加法モデル)",
    x = "時点",
    y = "値"
  ) +
  theme_minimal() +
  theme(strip.text = element_text(size = 12))

print(p1)
--- decompose関数の出力リストの要素 ---
[1] "x"        "seasonal" "trend"    "random"   "figure"   "type"    
Figure 2
  1. 観測値 : 入力した元の時系列データです。
  2. トレンド成分 : 移動平均によって平滑化された線です。グラフの両端が途切れている(欠損値NAとなっている)のは、中心化移動平均を使用するため、データの端では計算が行えないからです。
  3. 季節成分 : トレンドを除去した後のデータから抽出された、固定的なパターンです。シミュレーションで設定したサインカーブの山と谷をほぼ追随できています。
  4. 不規則成分 : 観測値からトレンド成分と季節成分を差し引いた残りの部分です。こちらも、トレンド成分の計算に伴い両端が欠損値となります。

真の成分と推定成分の比較

# パッケージの読み込み
library(gridExtra)

# 比較用データフレームの作成
df_compare <- data.frame(
  Time = time_index,
  # トレンドの比較
  True_Trend = true_trend,
  Est_Trend = as.numeric(dec_res$trend),
  # 季節成分の比較
  True_Season = true_season,
  Est_Season = as.numeric(dec_res$seasonal)
)

# 可視化 (トレンドの比較)
p_trend <- ggplot(df_compare, aes(x = Time)) +
  # 真のトレンド(黒い破線)
  geom_line(aes(y = True_Trend, color = "真のトレンド (True)"),
    linewidth = 0.5, linetype = "dashed"
  ) +
  # 推定されたトレンド(青い実線)
  geom_line(aes(y = Est_Trend, color = "推定トレンド (Estimated)"),
    linewidth = 1.2, alpha = 0.8
  ) +
  labs(
    title = "トレンド成分の比較",
    y = "トレンド値"
  ) +
  scale_color_manual(name = "", values = c("真のトレンド (True)" = "black", "推定トレンド (Estimated)" = "blue")) +
  theme_minimal() +
  theme(legend.position = "bottom")


# 可視化 (季節成分の比較)
p_season <- ggplot(df_compare, aes(x = Time)) +
  # 真の季節成分(黒い破線)
  geom_line(aes(y = True_Season, color = "真の季節成分 (True)"),
    linewidth = 0.5, linetype = "dashed"
  ) +
  # 推定された季節成分(赤い実線)
  geom_line(aes(y = Est_Season, color = "推定季節成分 (Estimated)"),
    linewidth = 1.2, alpha = 0.8
  ) +
  labs(
    title = "季節成分の比較",
    y = "季節変動値"
  ) +
  scale_color_manual(name = "", values = c("真の季節成分 (True)" = "black", "推定季節成分 (Estimated)" = "red")) +
  theme_minimal() +
  theme(legend.position = "bottom")

# 2つのグラフを並べて表示
grid.arrange(p_trend, p_season, ncol = 1)
Figure 3
  1. トレンド成分(上図) : 推定された青い線は、真の黒い破線をほぼ追随しています。
  2. 季節成分(下図) : 赤い実線と黒い破線の山と谷はぼ一致しています。decompose 関数は、トレンドを除去した後のデータから『各月の平均値』を計算することで季節性を抽出します。この単純なアプローチでも、固定的な季節パターンをほぼ復元できていることが確認できます。

以上です。