MATLABで『時系列解析入門』8章
最終更新:2021/08/05
局所定常ARモデル.
非専門家が書いています. 十分に批判的に読んで頂くようお願いいたします. 間違い・疑問点などあれば, ぜひコンタクトフォームへ連絡いただけると幸いです.
任意個の区間への自動分割
アルゴリズム
教科書に詳しいアルゴリズムはあるので, そちらを見てもらいたい. コードと地震波データ(MYE1F)に適用した結果を示す.
簡単に地震波データで説明する.
- 地震波データ ( 長さ2600 ) を各々長さ150の少区間 $A_1, A_2, \ldots$ に分ける.
- 区間 $A_1$ と $A_2$ にARモデルを適用 ( 最大次数は10とした ) し, 各々の区間の最小AICの和を分割モデルのAIC:$\mathrm{AIC}_D$ とする.
- 併合区間 $[ A_1, A_2 ]$ にARモデルを適用し, 最小AIC ( $\mathrm{AIC}_P$ ) を計算する.
- $\mathrm{AIC}_P$ と $\mathrm{AIC}_D$ を比較し, $\mathrm{AIC}_P$のほうが小さければ区間を併合し, $\mathrm{AIC}_D$ が小さければ区間は分割したままにする.
- これを繰り返す.
コード
正直効率的でない部分もあるが, そんなに重い計算でもないので.
function [ns, span, ms, aicDP, spec, f] = lsar(d, maxArOrder, ns0, method) % LSAR % % INPUTS % maxArOrder : ARモデルの最大次数 % ns0 : 基本小区間の長さ % method: 1: levinson % 2: Leastsquare % 3: Normal Equation % % OUTPUTS % ns : 各局所定常スパンのデータ数 % span : 局所定常スパンの始点と終点 % ms : 分割モデルの次数 % aicDP: 分割モデルのAICと併合モデルのAIC % spec : 各局所定常スパンのパワースペクトル % f : パワースペクトルの周波数 % preprocessings d = d - mean(d); L = length(d); span = zeros(ceil(L/ns0), 2); ns = zeros(ceil(L/ns0), 1); aicDP = zeros(floor(ceil(L/ns0))-2, 2); % step 1 [aic, ~, ~, maic] = myAr(d(1:ns0), maxArOrder, method); aic0 = aic(maic+1); k = 1; span(1, 1) = maxArOrder + 1; span(1, 2) = maxArOrder + ns0; ns(1) = ns0 - maxArOrder; % step 2- count = 1; while L - span(k, 2) + 1 > ns0 % divided model [aic, ~, ~, maic] = myAr(d(span(k, 2):span(k, 2)+ns0), maxArOrder, method); aic1 = aic(maic+1); aicD = aic0 + aic1; % pooled model [aic, ~, ~, maic] = myAr(d(span(k, 1):span(k, 2)+ns0), maxArOrder, method); aicP = aic(maic+1); aicDP(count, :) = [aicD aicP]; if aicD < aicP k = k+1; span(k, 1) = span(k-1, 2) + 1; span(k, 2) = span(k-1, 2) + ns0; ns(k) = ns0; aic0 = aicD; else span(k, 2) = span(k, 2) + ns0; ns(k) = ns(k) + ns0; aic0 = aicP; end count = count + 1; end span = span(span(:, 1)~=0, :); ns = ns(ns ~= 0); % plot t = tiledlayout(3, 1); t.Padding = 'compact'; t.TileSpacing = 'compact'; nexttile(1, [2, 1]); for I = 1:size(span, 1) plot(span(I, 1):span(I, 2), d(span(I, 1):span(I, 2))); hold on end temp = maxArOrder:ns0:length(d); xline(temp(2:end-1)); ylabel('Data'); xticks({}); xlim([1 L]); nexttile bar(temp(2:end-1), aicDP(:, 1)-aicDP(:, 2), 0.5, 'LineStyle', 'none') xlim([1 L]); ylabel('AIC_{D} - AIC_{P}') % calculate local stationary spectrum nd = length(ns); ms = zeros(1, nd); spec = zeros(nd, length(0:0.001:0.5)); for I = 1:nd [~, Arcoef, s2, maic] = arNormaleq(d(span(I, 1):span(I, 2)), maxArOrder); a = Arcoef.(['order', num2str(maic)]); [s, f] = spectrumARMA(a, 0, s2(maic+1)); spec(I, :) = s; ms(I) = maic; end end
なお, 函数myAR
は, 今までに作ったYule-Walker法, Householder法, 正規方程式を用いたARモデル推定の函数をまとめたものである.
Householder法, 正規方程式は最小二乗法なので結果はおなじになる.
検算用である.
MATLABコード
MATLABfunction [aic, Arcoef, s2, maic] = myAr(d, M, method) %MYAR % % INPUT % d:data % M:maximum AR degree (lag) % method: 1: levinson % 2: Leastsquare % 3: Normal Equation % % OUTPUT % aic : AIC % Arcoef : AR coefficient (structure) % s2 : sigma^2 % maic : The order which provides the minimum AIC switch method case 1 [aic, Arcoef, s2, maic] = arLevinson(d, M); case 2 [aic, Arcoef, s2, maic] = arLeastsquares(d, M); case 3 [aic, Arcoef, s2, maic] = arNormaleq(d, M); otherwise warning('Unexpected method type.') end end
地震波へ適用
Yule-Walker法で行った結果はよく分割してくれている. $\mathrm{AIC}_D - \mathrm{AIC}_P$ は, 正の値だと併合モデルが選択されたことを示す.
MATLABコード
MATLABd = mye1f; maxArOrder = 10; ns0 = 150; [ns, span, ms, aicDP, spec, f] = lsar(d, maxArOrder, ns0, 1);
各局所定常スペクトルもみておく. 横軸 $f$, 縦軸 $\log_{10}{p(f)}$ である.
MATLABコード
MATLABfigure; nTile = size(spec, 1); t = tiledlayout(ceil(sqrt(nTile)), ceil(sqrt(nTile))); t.Padding = 'compact'; t.TileSpacing = 'compact'; for I = 1:nTile nexttile area(f, log10(spec(I, :)), 'BaseValue', -1, 'LineStyle', 'none'); ylim([-1 4]) xlim([0 0.5]) title([num2str(span(I, 1)) ' - ' num2str(span(I, 2))]) end
白色雑音へ適用
このモデルを白色雑音に適用したらどうなるのだろう? AICも確率的に動くので, 分割モデルは次数が大きい ( 罰金が大きい ) とはいえ一定の確率で分割モデルが選ばれてしまうはずである.
ほら!
ただ, 局所定常スペクトラムをみれば, 意味のある分割かはそこそこわかると思う.
変化時点の精密な推定
アルゴリズム
理屈はシンプル. 信号を分ける点を一つずつずらしていき, 前半のAICと後半のAICの和をもとめ, それが最小になる点を変化時点とする.
ただ, 膨大な量の計算量になるので, 教科書通りハウスホルダー法で行っている. 教科書には前半にハウスホルダー法を適用するための行列 ( 式8.22 ) が載っているが, 後半に対しては載っていないのでココに記しておく. もちろん, 載せるまでもなく自明なのだが, 結構添字が複雑になるので.
まず $\vecseq{y}{n_1+1}{N}$ にARモデルを当てはめる. 前半を計算するための行列 ( 式8.22 ) に対応する後半の行列は,
である. $p$ 番目に上三角行列の下に追加すべき行ベクトル ( 式8.17の下段 ) は,
となる.
コード
k
やc
など変な変数があるのは, 最初 $p$ を増やす間隔も設定しようと思ったから.
でもめんどくさくなってやめた.
function [aic, aicmin, changePoint] = lsarChgpt(d, maxArOrder, subinterval, candidate) % LSARCHGPT % INPUTS % d : 1 dim. data % maxArOrder : ARモデルの最大次数 % subinterval : モデリングに使用するデータ区間の始点と終点 [n0, ne] % candidate : 変化点の候補点の最小値と最大値 [n1, n2] 十分に[n0, ne]に含まれること. % % OUTPUTS % aic : 区間[n1, n2]で当てはめられたARモデルのAIC % aicmin : 最小AIC % changePoint : 推定された変化点 M = maxArOrder; d = reshape(d, [], 1); % d = d - mean(d); d = d(subinterval(1):subinterval(2)); n0 = candidate(1) - subinterval(1) + 1; n1 = candidate(2) - subinterval(1); L = candidate(2) - candidate(1) + 1; k = 1; l = floor(L/k); p = 1; % Former part XF = zeros(n0-M, M+1); aicF = zeros(1, l); for col = 1:M begin = M - col + 1; XF(:, col) = d(begin:n0-col); end XF(:, end) = d(M+1:n0); [~, SF] = qr(XF); % householder SF = SF(1:M+1, 1:M+1); s2F = flipud(cumsum(flipud(SF(:, end).^2))) / (n0-M); aicFTmp = (n0 - M) * log(s2F) + 2*((0:M)'+1); aicF(p) = min(aicFTmp); % Latter part N = length(d); XL = zeros(N-n1-M, M+1); aicL = zeros(1, l); for col = 1:M begin = n1 - col + M + 1; XL(:, col) = d(begin:N-col); end XL(:, end) = d(n1+M+1:N); [~, SL] = qr(XL); % householder SL = SL(1:M+1, 1:M+1); s2L = flipud(cumsum(flipud(SL(:, end).^2))) / (N-n1-M); aicLTmp = (N-n1-M) * log(s2L) + 2*((0:M)'+1); aicL(p) = min(aicLTmp); for I = 1:l-1 c = p*k; % Former part [~, SF] = qr([SF; [d(n0+c-1:-1:n0-M+c)' d(n0+c)]]); SF = SF(1:M+1, 1:M+1); s2F = flipud(cumsum(flipud(SF(:, end).^2))) / (n0-M+c); aicFTmp = (n0-M+c) * log(s2F) + 2*((0:M)'+1); % Latter part [~, SL] = qr([[d(n1+M-c:-1:n1-c+1)' d(n1+M-c+1)]; SL]); SL = SL(1:M+1, 1:M+1); s2L = flipud(cumsum(flipud(SL(:, end).^2))) / (N-n1-M+c); aicLTmp = (N-n1-M+c) * log(s2L) + 2*((0:M)'+1); p = p + 1; aicF(p) = min(aicFTmp); aicL(p) = min(aicLTmp); end aic = fliplr(aicL) + aicF; [aicmin, changePoint] = min(aic); changePoint = candidate(1) + changePoint*k; end
地震波への適用
再び地震波のデータへ適用する. これは完璧でしょう. 久々に教科書とほぼ完全に一致した. これは, 比較的変化が明確な常微動→P波への変化
MATLABコード
MATLABt = tiledlayout(2, 1); t.Padding = 'compact'; t.TileSpacing = 'compact'; nexttile plot(candidate(1):candidate(2), d(candidate(1):candidate(2))) title('常微動からP波への遷移区間') xlim([candidate(1), candidate(2)]) xline(changePoint) nexttile plot(candidate(1):candidate(2), aic) xline(changePoint) title('AICの変化')
これは, P波→S波への変化. 教科書と若干形が違うが, 変化点はおなじになった. 一点ずつ変化点をずらしながら愚直に両側のAICを計算してみても同じ形になったので, とりあえずよしとする.
おまけ
変化点をずらしながら, 愚直に両側のAICを計算する函数を作った. 非効率なので計算速度は落ちるが, 以下を目的とした.
- 検算用
- 任意の固定変化点間隔
- 最小二乗法以外の適用
function [aic, aicmin, changePoint] = lsarChgpt2(d, maxArOrder, subinterval, candidate, interval, method) % LSARCHGPT2 % INPUTS % d : 1 dim. data % maxArOrder : ARモデルの最大次数 % subinterval : モデリングに使用するデータ区間の始点と終点 [n0, ne] % candidate : 変化点の候補点の最小値と最大値 [n1, n2] 十分に[n0, ne]に含まれること. % interval : 変化点候補の間隔 % method: 1: levinson % 2: Leastsquare % 3: Normal Equation % % OUTPUTS % aic : 区間[n1, n2]で当てはめられたARモデルのAIC % aicmin : 最小AIC % changePoint : 推定された変化点 M = maxArOrder; chgPtCandidate = candidate(1):interval:candidate(2); L = length(chgPtCandidate); aic = zeros(1, L); for I = 1:L d1 = d(subinterval(1):chgPtCandidate(I)); d2 = d(chgPtCandidate(I)+1:subinterval(2)); [aict, ~, ~, maic] = myAr(d1, M, method); aic1 = aict(maic+1); [aict, ~, ~, maic] = myAr(d2, M, method); aic2 = aict(maic+1); aic(I) = aic1+aic2; end [aicmin, changePoint] = min(aic); changePoint = chgPtCandidate(changePoint); end
変化時点の事後確率
地震波
「変化時点の精密な推定」のモデルで, AICが対数尤度と考えれば, 変化点を $j$ と考えたときの尤度は,
と考えられる. AICがバイアス補正された対数尤度という認識はあったが, AICから尤度を計算するという発想はなかった. 新鮮で面白い. よって, 事前分布として一様分布を想定すれば, 到着時刻の事後分布が,
をみたす. 確率密度にするならば, 正規化すればよい. 教科書の例を再現する.
MATLABコード
MATLABd = mye1f; maxArOrder = 10; subinterval = [400, 800]; candidate = [600, 700]; [aic, aicmin, changePoint] = lsarChgpt(d, maxArOrder, subinterval, candidate); t = tiledlayout(3, 1); t.Padding = 'compact'; t.TileSpacing = 'compact'; nexttile plot(candidate(1):candidate(2), d(candidate(1):candidate(2))) title('常微動からP波への遷移区間') xlim([candidate(1), candidate(2)]) xline(changePoint) nexttile plot(candidate(1):candidate(2), aic) xline(changePoint) title('AICの変化') nexttile post = exp(-0.5 * (aic - min(aic))); post = post / sum(post); plot(post) title('変化点の事後確率')