RのS3メソッドディスパッチを確認します。
本ポストでは関数 breakpoints {strucchange}
を例として、S3メソッドディスパッチ を確認します。
breakpoints {strucchange}
については、こちらのポストを確認してください。

1. ジェネリック関数とメソッド
始めに関数breakpoints
のメソッドを確認します。
library(strucchange)
breakpoints
function (obj, ...)
{
UseMethod("breakpoints")
}
<bytecode: 0x0000023a0e537760>
<environment: namespace:strucchange>
methods(breakpoints)
[1] breakpoints.breakpointsfull* breakpoints.formula* breakpoints.Fstats*
see '?methods' for accessing help and source code
Rにおいて、breakpoints
のような関数は「ジェネリック関数 (Generic Function)」と呼ばれます。ジェネリック関数は、それ自体が具体的な計算処理を持つわけではなく、いわば司令塔のような役割を果たします。
ジェネリック関数の主な仕事は、渡された引数(特に第一引数)のclass
属性を調べ、そのクラスに対応する具体的な処理関数(メソッド)を探し出して実行を委任することです。
メソッドは、通常「ジェネリック関数名.クラス名
」という命名規則に従っています。上記の methods(breakpoints)
で表示された3つの関数が、breakpoints
というジェネリック関数に対応するメソッド群です。
breakpoints.formula
breakpoints.breakpointsfull
breakpoints.Fstats
2. メソッドディスパッチの流れ
例えば、breakpoints
関数を以下のように呼び出すとします。
# サンプルデータの作成
<- 200
n <- 80
true_bp1 <- 140
true_bp2 <- 1:n
x <- rnorm(n, mean = 0, sd = 1)
error <- numeric(n)
y 1:true_bp1] <- 10 + 0.5 * x[1:true_bp1] + error[1:true_bp1]
y[<- 10 + 0.5 * true_bp1
start_val_regime2 + 1):true_bp2] <- start_val_regime2 - 1.5 * (x[(true_bp1 + 1):true_bp2] - true_bp1) + error[(true_bp1 + 1):true_bp2]
y[(true_bp1 <- start_val_regime2 - 1.5 * (true_bp2 - true_bp1)
start_val_regime3 + 1):n] <- start_val_regime3 + 1.0 * (x[(true_bp2 + 1):n] - true_bp2) + error[(true_bp2 + 1):n]
y[(true_bp2 <- data.frame(x = x, y = y)
sim_data
# 関数breakpoints の実行
<- breakpoints(y ~ x, data = sim_data, breaks = 2, h = 0.1)
bp_model print(bp_model)
Optimal 3-segment partition:
Call:
breakpoints.formula(formula = y ~ x, h = 0.1, breaks = 2, data = sim_data)
Breakpoints at observation number:
80 139
Corresponding to breakdates:
0.4 0.695
このとき、Rの内部では次のような処理(メソッドディスパッチ)が行われています。
- ジェネリック関数
breakpoints()
が呼び出されます。 -
breakpoints()
は、最初の引数であるy ~ x
のクラスを確認します。y ~ x
のようにチルダ(~
)を使って記述されたオブジェクトのクラスはformula
です。
# y ~ x のクラスを確認
class(y ~ x)
[1] "formula"
- Rは、「
breakpoints
というジェネリック関数」と「formula
というクラス」を組み合わせた名前のメソッド、つまりbreakpoints.formula
を探します。 -
breakpoints.formula
メソッドが見つかったため、Rはこのメソッドに引数を渡して実行します。
3. 他のメソッドが実行されるケース
ちなみに、他の2つのメソッドは以下のような場合に実行されます。
-
breakpoints.breakpointsfull
:breakpoints()
関数の実行結果(bp_model
など)は、breakpointsfull
というクラスを持っています。このオブジェクトに対して再度breakpoints()
を適用した場合に、このメソッドが呼び出されます。これは、一度計算したRSS.table
を再利用して、異なる数の変化点を抽出したい場合などに利用されます。
# 関数breakpointsの実行結果のクラスを確認
class(bp_model)
[1] "breakpointsfull" "breakpoints"
# breakpoints.breakpointsfullが呼び出されているか確認
# さらに、モデルを再利用し、今回は4つの構造変化点を抽出
breakpoints(bp_model, breaks = 4)
Optimal 5-segment partition:
Call:
breakpoints.breakpointsfull(obj = bp_model, breaks = 4)
Breakpoints at observation number:
23 46 79 139
Corresponding to breakdates:
0.115 0.23 0.395 0.695
breakpoints.Fstats
:strucchange
パッケージには、F検定統計量を計算するFstats()
という関数があります。このFstats()
の実行結果(Fstats
クラスのオブジェクト)をbreakpoints()
関数に渡すと、breakpoints.Fstats
メソッドが呼び出されます。なお、関数
Fstats()
の目的は、「データに構造変化が本当に存在するのか?」を検定することです。具体的には、「回帰モデルのパラメータは期間を通じて一定である(構造変化は無い)」という帰無仮説を、「パラメータがどこか1つの時点で変化した(構造変化が1つある)」という対立仮説に対して検定します。つまり、変化の有無を判断するためのツールです。関数の引数は以下の通りです。
# 関数Fstatsの引数を確認
args(Fstats)
function (formula, from = 0.15, to = NULL, data = list(), vcov. = NULL)
NULL
formula
:検定したいモデルの構造を
y ~ x
のようにformulaオブジェクトで指定します。breakpoints
関数と同じです。from
:変化点を探索する期間の開始点を、データ全体の割合(0から1)で指定します。デフォルトは
0.15
です。これは、データ全体の最初の15%を探索範囲から除外することを意味します。なぜなら、あまりに端に近い点を変化点として検定しようとすると、分割された一方のセグメントのデータ数が少なすぎて、意味のある回帰分析や比較ができないためです。この除外する期間を「トリミング (trimming)」と呼びます。to
:変化点を探索する期間の終了点を、データ全体の割合で指定します。この引数が
NULL
(デフォルト)の場合、自動的に1 - from
の値が設定されます。つまり、from = 0.15
の場合、to
は0.85
となり、データの真ん中の70%(15%地点から85%地点まで)を対象に変化点を探索します。data
:formula
で使用する変数が含まれるデータフレームを指定します。vcov.
:残差の自己相関や不均一分散を考慮した頑健な分散共分散行列を計算するための関数を指定できます(例:
sandwich::NeweyWest
)。
# 関数Fstatsの実行結果のクラスを確認
<- Fstats(formula = y ~ x, data = sim_data, from = 0.15)
fstats_obj class(fstats_obj)
[1] "Fstats"
# breakpoints.Fstatsが呼び出されているか確認
breakpoints(fstats_obj)
Optimal 2-segment partition:
Call:
breakpoints.Fstats(obj = fstats_obj)
Breakpoints at observation number:
112
Corresponding to breakdates:
0.555
以上です。