Diagnostics-for-VPA.Rmd
※ tidyr, dplyr, purrr, ggplot2,
stringerの5つは”tidyverse”というパッケージの中にまとめてあるのでlibrary(tidyverse)
だけでも大丈夫です
感度分析はdo_sensitivity_vpa
関数を使えばできます。
引数で重要なものは、VPA計算の結果(vpa
関数の実行結果のオブジェクト)、感度分析を行いたい指標what_replace
、感度分析の値value
の3つです。また、結果のグラフについての引数はwhat_plot
とncol
があります。出力数を減らしたい場合はwhat_plot
に出力したい結果だけ入力してください。ncol
はグラフの列数です。適宜調整してください。
結果では、$result
にVPAの結果がリスト形式で、$graph
に複数のVPAの結果を重ね書きしたグラフが入っています。
関数の引数について(特にvalue
に与えるデータの型について)、不明点等ありましたら、以下のようにhelp
を使って確認してみてください。
help(do_sensitivity_vpa)
what_replace="M"
にすると自然死亡係数について感度分析を行えます。value
には、感度分析を行いたい割合をnumeric
型で入れてください。(以下の例では、もとの自然死亡係数を0.5倍、1.5倍、2倍にした感度分析結果を返す)
res_vpa_sensitivity <- do_sensitivity_vpa(res_vpa_estb,
what_replace = "M",
value = c(0.5, 1.5, 2))
$result
の中にはvpa計算の結果$graph
の中には結果の図 がそれぞれ、入っています。今回の例では、value
を3通り仮定したので、それぞれの結果がリスト型式で以下のように入っております。
names(res_vpa_sensitivity$result)
[1] "Sensitivity M= x0.5" "Sensitivity M= x1.5" "Sensitivity M= x2"
res_vpa_sensitivity$graph
自然死亡係数を大きくすると、資源量のスケールも併せて大きくなっています。
直接Mの行列を入力しても実行できます。 最近年のMの仮定について見たい、掛け算ではなく足し算で実行したい、等の場合は行列を与えてください
年齢別体重を置き換える場合は、what_replace = "waa"
とします。
自然死亡係数の場合と同様に、value
に入れた値がもとの年齢別体重にかけられます。
また、what_plot
に作図で出力したい結果を指定することができます。
この他、plot_year
に作図したい年(x軸の範囲)を指定することもできます
res_vpa_sensitivity <- do_sensitivity_vpa(res_vpa_estb,
what_replace = "waa",
value = c(0.8, 1.2),
what_plot=c("SSB","biomass","U","Recruitment",
"fish_number","fishing_mortality"),
plot_year = c(1997, 2000),
ncol=3
)
res_vpa_sensitivity$graph
また、解析に用いたwaaと同じ長さのmatrix
であれば、list
にまとめてvalue
に任意の値を与えることができます。
漁獲量計算と資源量計算で異なる年齢別体重を用いている場合には、漁獲物中の年齢別体重の設定についても感度分析ができます。この場合は、what_replace = "waa.catch"
とします。
value
に入れる値の型はindex = waa
の場合と同じく、numeric
またはlist
型です。
(この例の場合には、漁獲量用の年齢別体重が設定されていないため、エラーとなります。)
res_vpa_sensitivity <- do_sensitivity_vpa(res_vpa_estb,
what_replace = "waa.catch",
value = c(0.8, 1.2),
what_plot=c("SSB","biomass","U","Recruitment",
"fish_number","fishing_mortality"),
ncol=3)
αを置き換えたい場合は、what_replace = "alpha"
とします。
value
には直接仮定したい値を入れてください。
res_vpa_sensitivity <- do_sensitivity_vpa(res_vpa_estb,
what_replace = "alpha",
value = c(0.9,0.8),
what_plot=c("SSB","biomass","U","Recruitment",
"fish_number","fishing_mortality"),
ncol=3
)
[1] "Warning! The estimated F for the older ages may not be accurate if C<<N is not satisfied for the older ages."
[1] "Warning! The estimated F for the older ages may not be accurate if C<<N is not satisfied for the older ages."
res_vpa_sensitivity$graph
チューニングの有無で行える感度分析が大きく変わります。
tune=FALSE
で解析している) :
tf.year
について感度分析を行いますtune=TRUE
で解析している)
sel.update=TRUE
) :
選択率更新法の代わりに全F推定法を行いますterm.f="all"
) :
全F推定の代わりに選択率更新法を行いますチューニングなしVPAの場合、最終年のFが過去何年分の平均になるかという仮定の部分の値を変えて感度分析を行います。
# チューニングなしVPAの場合
res_tmp <- vpa(vpadat_estb, tf.year=1996:1999, last.catch.zero = FALSE,
Pope = TRUE, p.init = 0.8, tune=FALSE)
tmp <- do_sensitivity_vpa(res_tmp,what_replace = "tuning", value = list(1995:1999, 1998:1999))
# 2つの数列をlist型にしてvalueを与えています
今回の例では選択率更新法で推定されているため、全F推定で感度分析が行われます。
これまでと同様に、what_replace = "tuning"
といれれば大丈夫です。
選択率更新法の代わりに感度分析で全F推定を行う場合、value
に値を入れる必要はありません
res_tmp <- vpa(vpadat_estb, tf.year=1998:2000, last.catch.zero = FALSE,
Pope = TRUE, p.init = 0.5, tune=TRUE, sel.update=TRUE)
res_vpa_sensitivity <- do_sensitivity_vpa(res_tmp,
what_replace = "tuning",
what_plot=c("SSB","biomass","U","Recruitment",
"fish_number","fishing_mortality"),
ncol=3
)
res_vpa_sensitivity$graph
全F推定の場合は、代わりに選択率更新法で感度分析が行われます。 また先のチューニングなしVPAのように、最終年Fの仮定についても値を変えて、感度分析することができます。
# 全F推定法の場合
res_tmp <- vpa(vpadat_estb, tf.year=1997:1999, last.catch.zero = FALSE,
Pope = TRUE, p.init = 0.5, tune=TRUE, sel.update=FALSE, term.F = "all")
tmp <- do_sensitivity_vpa(res_tmp, what_replace = "tuning", value = list(1995:2000))
# 数列をlist型にしてvalueを与えています
# チューニングなしVPAと同じです
全F推定でリッジVPAを行っている場合(λ>0)にはλの値についての感度分析も実施してみてください。
# リッジVPAの場合
res_tmp <- vpa(vpadat_estb, tf.year=1997:1999, last.catch.zero = FALSE,
Pope = TRUE, p.init = 0.5, tune=TRUE, term.F = "all", lambda = 0.2)
tmp <- do_sensitivity_vpa(res_tmp, what_replace = "lambda", value = c(0,0.1,0.5,0.7),
what_plot=c("SSB","biomass","U","Recruitment",
"fish_number","fishing_mortality"),
ncol=3)
# lambdaの値を"numeric型"で与えています
# list型ではないので気を付けてください
bを推定した場合、bを推定しない(b=1)場合について、感度分析を実施してください。what_replace = "b"
とするとbについて感度分析できます。また、bを推定していない場合も適宜bを推定(あるいは固定)してみて、感度分析を行ってみてください。
numeric
型、またはlist
型(複数与えたい場合)で値を入れる(ベースケースでbを推定しているが、bを推定しない感度分析を実施する場合はvalue=1
を与える)"b.est"
と入れる
# b推定しているが、bを固定する場合
tmp <- do_sensitivity_vpa(res_vpa_estb, what_replace = "b", value = list(c(1,1),c(1.2,1.2)))
# bを固定しているが、bを推定する場合
tmp <- do_sensitivity_vpa(res_vpa_estb, what_replace = "b", value = c("b.est"))
vpa(rec)
でrec引数を指定していも利用可能ですrec.year
より遡ると、効果が消えますレトロスペクティブ解析はdo_retrospective_vpa
関数を用いることで、
までを一貫して行えます。
引数として、
res
: vpa計算の結果のオブジェクトの他にn_retro
:
レトロスペクティブ解析を何年さかのぼって実行するか(デフォルトで5年)b_reest
:
b(hyperstability/depletion)をレトロスペクティブ解析内で再推定するかwhat_plot
: 作図で何を出力するかplot_year
:
作図したい年(作図のx軸の範囲)ncol
: 作図の列数があります。
今回の解析結果res_vpa_estb
を使って、7年分をさかのぼってレトロスペクティブ解析する場合、以下のようにコードします。
res_vpa_retrospective <- do_retrospective_vpa(res_vpa_estb,
n_retro = 7,
b_reest = FALSE)
res_vpa_retrospective$mohn_rho
N B SSB R F
-1.353966e-01 -1.019165e-01 -9.331419e-02 -1.829777e-01 1.626003e+05
結果はlist型式でまとめてあり、$
を使って呼び出すことが可能です。resultはVPAの推定結果、mohn_rhoはmorhのrhoの値、graphはグラフ、b_resはbの再推定結果です。またMohn’s
rhoは、グラフ中にも併記してあります。 ここで、漁獲係数のMohn’s
rhoは年齢別漁獲係数を全て足し合わせた値となっています。
res_vpa_retrospective$graph
また、bをレトロスペクティブ解析内で再推定したい場合は以下のようにコードしてください。
res_tmp <- vpa(vpadat_estb, tf.year=1997:1999, last.catch.zero = FALSE, Pope = TRUE,
p.init = 0.5, tune=TRUE, sel.update=FALSE, term.F = "all", b.est = TRUE)
# まずbを推定するvpaを実行しています
res_vpa_retrospective <- do_retrospective_vpa(res_tmp,
n_retro = 4,
b_reest = TRUE # ここをTRUEにしてください!!
)
res_vpa_retrospective$b_res
[38;5;246m# A tibble: 6 × 4
[39m
`2000` `1999` `1998` `1997`
[3m
[38;5;246m<dbl>
[39m
[23m
[3m
[38;5;246m<dbl>
[39m
[23m
[3m
[38;5;246m<dbl>
[39m
[23m
[3m
[38;5;246m<dbl>
[39m
[23m
[38;5;250m1
[39m 1.11 1.04 0.792 0.603
[38;5;250m2
[39m 0.564 0.736 0.826 0.612
[38;5;250m3
[39m 0.854 0.679 0.356 0.257
[38;5;250m4
[39m 0.578 0.523 0.603 0.383
[38;5;250m5
[39m 0.657 0.892 0.628 0.480
[38;5;250m6
[39m 1.14 1.33 1.47 1.11
このように各レトロスペクティブ解析ごとに、bを推定していることが確認できました。(ダミーデータなので変な値となっています)
VPAを2段階法で行っている場合は、res_step1
引数に1段階目のVPA結果のオブジェクトを与えてください。
res_tmp1 <- vpa(vpadat_estb, tf.year=1997:1999, last.catch.zero = FALSE, Pope = TRUE,
p.init = 0.5, tune=FALSE, term.F = "max", b.est = FALSE,fc.year=1998:2000)
res_tmp2 <- vpa(vpadat_estb, tf.year=1997:1999, last.catch.zero = FALSE, Pope = TRUE,
p.init = 0.5, tune=TRUE, term.F = "max", b.est = TRUE, fc.year=1998:2000,
sel.f = res_tmp1$saa$`2000`) # sel.fで2段階目の選択率の初期値を与えます
res_vpa_retrospective <- do_retrospective_vpa(res_tmp2,
n_retro = 3,
plot_year = c(1995, 2000),
b_reest = TRUE,
res_step1 = res_tmp1
# ここに1段階目のチューニングなしVPAを与えてください
)
res_vpa_retrospective$graph
Fの推定結果が初期値に依存せずに、安定した結果となっているかを診断するために、jitter
analysis(初期値を乱数で生成し、vpa計算を繰り返し実施する)をおこないます。vpa
関数では推定パラメータの初期値は引数p.init
で与えられますが、全F推定法の場合、最終年の各年齢のFの初期値になります。
Fの結果が初期値に依存する場合、初期値に応じて推定結果も変わることが考えられます。このような場合、パラメータの初期値への依存が高く、局所的な最適解に引っ張られている可能性などが考えられます。すなわち、データの持つ情報量が低いことが考えられるため注意が必要です。jitter analysisはこのようなことがおこっていないか確認するための解析です。
初期値は対数を取った0.001から2の間を10刻みにした後、指数を取ってます。刻みの数はn_ite
で指定できます。
do_estcheck_vpa
関数で、自動的にjitter
analysisとその結果の作図が行われます。
基本的に、vpa計算の結果のオブジェクトのみを与えれば計算が行われます。 その他に引数として、
what_plot
: 作図したい年齢TMB
: TMBで解析する(
始めて実行する場合はuse_rvpa_tmb()
を先に実行
)を指定します。コマンドは以下の通りです。
res_vpa_jitter <- do_estcheck_vpa(res_vpa_estb, n_ite=10)
Maximum likelihood in jitter analysis is: 23.60018
Likelihood with estimated parameters is: 23.60018
最初に引数で与えたVPA結果について、
を知らせてくれます。ヘッセ行列の対角成分に負がある(本来正になる値であり、パラメータ推定が失敗してる)、あるいは収束していない場合、ここで関数が止まります。
再度、VPA
関数を使って解析してください。(初期値を変える、全F推定の場合パラメータを減らす、資源量指数の非線形性を考慮している場合はbを与えるあるいは1にする、などの工夫が必要となります)
次に、関数内でjitter
analysisが行われます。n_ite数分のVPA計算を行います。
生成する初期値の数は 10個(n_ite = 10
)
で十分です。推定値と初期値の大体の関係が見れれば問題ありませんが、初期値に応じて、Fが異なる値で推定される(大きな値に発散せずに)場合には注意が必要です。
その結果がres_vpa_jitter
内にあり、それぞれ、
$initial_value
に初期値$p_name
推定したパラメータ(漁獲係数)の年齢$value
推定値や尤度、収束の値を返す$graph
初期値と推定値or尤度の関係の図が入っています。
res_vpa_jitter$graph$estimated # 推定されたF
res_vpa_jitter$graph$likelihood # 対数尤度(大きいほど良い)
ここで赤い横線は与えた結果の推定値と尤度を表しています。
結果の図を見ると、いくつかの初期値のセットではFが0におちたり、Fが高い値になってしまっていることがわかります。これらのときの尤度は、推定値が得られたときの対数尤度(赤線)よりも小さい値になっていることから、推定パラメータから計算される尤度はほかの初期値を与えたときの推定値から得られる尤度と比較しても最大になっている(=最尤推定値が得られている)ことが確かめられました。
またVPAは、最終年のFでしか指標値とのフィットをコントロールできないことから、FがInfなどの大きな値になることもあります。このような場合に、推定結果の最大対数尤度より、Fが発散した場合の最大対数尤度の方が大きくなることがあります。これはFが収束していないだけですので、こういった場合は推定と初期値の関係に問題ありません。 (逆にこういったFが大きく発散しないFを探すという方が実際の資源に適用する場合には多いかと思います)
関数を実施したとき、“Maximum likelihood in jitter analysis is: , Likelihood with estimated parameters is :” という表示が出ますので、上下の値が一致している、または上の値よりも下の値のほうが大きければOKです。逆に、上の値(Maximum likelihood in jitter analysis)のほうが大きい場合には、上述のようにFが発散しているため問題ないか、最尤推定値は他にある可能性がある場合の2通り考えられます。後者の場合は初期値を変えて計算しなおしてください。
残差プロットはモデル診断の中でも、最も重要なパートの1つです。残差とは、CPUEとその予測値(qN^b)の差のことを指し、これが小さいほうがよりフィッティングの良いモデルとなります。残差の対数(対数残差)プロットは0を平均とした分布になります。また、 残差プロットが時系列的に一貫して増える(または減る)傾向(自己相関)がみられる場合、モデルとデータの間に何らかの齟齬がある可能性があり、注意が必要です。この他、 他の残差に比べて著しく大きい残差は、そのデータ(CPUE)が外れ値である場合があります。外れ値はパラメータ推定に大きな影響を及ぼすので、外れ値と思われるデータを解析に含めるか再考する必要があります。 ただし、いずれも時系列データを扱っていると往々に起こりうることですので、外れ値がある、あるいは自己相関係数が有意であっても、結果が妥当そうであれば問題ありません。 残差プロットを行うことでこれらの傾向を一目にして把握することができます。
残差プロットはplot_residual_vpa
関数で実行できます。大きく3種類のプロットを書くことができます。
$year_resid
:
対数残差を時系列にプロットしたもの。先述の残差の傾向と外れ値の把握に利用。標準化残差のプロットについては$year_sd_resid
中にある。$fitting_Index
:
CPUEと予測CPUEをそれぞれ重ねてプロットしたもの。データと推定結果のフィッティングを見るもの。$abund_Index
:
資源量/資源重量/親魚重量とその予測CPUEとの関係。赤い線が推定した資源量とその指数の線形/非線形性を表す。またindex_name
引数中に、各指標の名前を与えるとプロットに反映されます(ない場合はIndex01、Index02・・・となります)。与える場合、用いた指標の数分引数に入れてください。
plot_example <- plot_residual_vpa(res_vpa_estb, index_name = c("CPUE1", "CPUE2","CPUE3","CPUE4","CPUE5","CPUE6"),plot_smooth=TRUE)
plot_example$year_resid
plot_example$abund_Index
また、plot_year
引数でプロットしたい年を指定することも可能です。
plot_example <- plot_residual_vpa(res_vpa_estb, plot_year = 1995:2000)
plot_example$fitting_Index
これらの残差プロットは今回のバージョンから、vpa
関数実行時に引数plot = T
にすると自動生成されるようになっています。モデル診断の最も基本パートとなるので、是非活用ください。
先程の図の通り、残差プロットに信頼区間が明記されるようになりました。 信頼区間は濃い色が80%、薄い色が95%のそれぞれ信頼区間となっております。
これによって、外れ値の検出ができます。 特に、最近年に外れ値がある場合、その値が資源量推定結果に与える影響は大きいことが想定されるので、後述のジャックナイフ法などを通して結果の妥当性を評価してください。
plot_example$year_resid
もし信頼区間の影が邪魔で消したい場合は、resid_CI=FALSE
としてください。
plot_residual_vpa(res_vpa_estb,
index_name = c("CPUE1", "CPUE2","CPUE3",
"CPUE4","CPUE5","CPUE6"),
plot_smooth=TRUE,
resid_CI = FALSE)$year_resid
また、自己相関係数も明記されるようになりました。
もしこの自己相関係数が有意に大きいあるいは小さい場合、rho=0.6*
のように、*印が付きます。
この場合は、残差の独立の仮定が満たされていません。
ただし、満たされていないから、一概にモデルとしての妥当性が不十分で、棄却されるというわけではありません。(主観的で恐縮ですが)
ジャックナイフ法によるデータの影響力の検証等を通して、結果の妥当性が確認されれば、問題がないと考えます。
こういった妥当性の検証のプロセスを説明することが大切だと考えます。
観測値(資源量指数)を1つずつ抜いて解析を行うことで、影響力の強いデータや外れ値を検出する方法です。
do_jackknife_vpa
関数で自動で観測値を抜いて再解析を行います。
plot_year
に作図したい年(x軸の範囲)を指定することもできます
scale_value
に任意の値を与えることで、プロットの点の形を任意で指定できます(ベースモデル+ジャックナイフの数分与えてください)
=> ジャックナイフ法の他に、感度分析、レトロスペクティブ解析でも指定できます
引数method
はデフォルトで”index”となっており、資源量指数の種類ごとに取り除いて解析を行います(つまり、資源量指数の種類分、結果が返ってきます)。一方、それぞれの種類を年ごとに取り除きたい場合は”all”を選択してください。この場合、資源量指数の種類×年数分、取り除いて計算するためデータ数によっては多くの計算時間を必要とする点に気を付けてください。
res_vpa_jackknife <- do_jackknife_vpa(res_vpa_estb,
what_plot = c("SSB", "biomass", "U", "Recruitment",
"fish_number", "fishing_mortality"),
ncol = 3, plot_year = c(1995, 2000),
scale_value = 2:8)
res_vpa_jackknife$JKplot_vpa # 全体の結果
res_vpa_jackknife$JKplot_par + ylim(0,1) # 最終年のFの比較 (ここではylimで縦軸を調整しています)
ブートストラップ法は乱数でデータを再生成し、信頼区間等を推定する方法です。 ここでは
それぞれについて、新規関数とともに紹介します。
またパッケージの更新速度の都合上、ブートストラップ標本数は10個としていますが、95%信頼区間などを正確に計算したい場合は1000回(デフォルト)ほど必要です。
ただし、データの特性や推定されたsigma
の大きさにも依存するので、200回や500回でも、1000回と信頼区間がそれほど変わらない場合は、少なくても構いません。
一方で大まかな幅や各パラメータの相関関係だけを知りたい場合には、100回ほどでも問題ありません。
ブートストラップ法ではVPA計算を繰り返し行うことから時間がかかるので、TMBの仕様を推奨します。最初にvpa計算する時にTMB=T
にしておくと、以下のブートストラップ計算もTMBで行われます。
先述の通り、ブートストラップ法はデータをリサンプリングする方法ですが、VPAでは残差をリサンプリングすることで、データ(CPUE)を再生成します。
frasyrの中にはブートストラップ法を自動で行うboo.vpa
関数がありますが、今回作成したplot_resboot_vpa
関数内でもboo.vpa
関数を使っています。したがって、plot_resboot_vpa
関数に直接vpa計算結果のオブジェクトを入れてください。
引数B_method
でブートストラップ法の方法を変えることができます。基本はパラメトリックブートストラップで問題ありません。ノンパラメトリックブートストラップ法では残差を重複ありでランダムにデータに足し合わせて再生成します。一方、パラメトリックブートストラップは平均0、分散が残差の分散となるような正規分布から乱数でデータを生成して、それぞれ解析する方法です。
"p"
:
パラメトリックブートストラップ法(デフォルト)"n"
: ノンパラメトリックブートストラップ法"r"
:
残差のスムージング後にBootstrap-t法を行う方法。引数ci_range
で信頼区間の値を指定できます。デフォルトは0.95です(95%信頼区間の表記)。
res_vpa_bootstrap <- plot_resboot_vpa(res_vpa_estb, B_ite = 10)
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
library(patchwork)
res_vpa_bootstrap$plot_ssb/res_vpa_bootstrap$plot_rec/res_vpa_bootstrap$plot_biomass
このようにSSB、加入量、バイオマスそれぞれについて95%信頼区間とともに資源動態の作図ができます。ここで使っているpatchwork
パッケージは、ggplot2
の作図オブジェクトを後からくっつけることが可能なパッケージです。frasyrインストール時に一緒にインストールされます。
ブートストラップ法は資源量の信頼区間だけでなく、パラメータの信頼区間も算出出来ます。 ここでは最終年最高齢のFの信頼区間を出しています。
res_tmp <- numeric()
for(i in 1:10) res_tmp[i] <- as.numeric(res_vpa_bootstrap$res_boot[[i]]$Fc.at.age[4])
quantile(res_tmp, probs = c(0.025,0.5,0.975))
2.5% 50% 97.5%
0.4392048 0.7634732 7.0830143
この他、各パラメータの相関関係について、ブートストラップ標本を基にプロットが得られます。
res_vpa_bootstrap$plot_cor
VPAは年齢別漁獲尾数(catch at
age)が真であると仮定して推定するため、catch at
ageの不確実性を評価することが出来ないモデルです。
ここで紹介するdo_caaboot_vpa
関数は、引数として与えている年齢別漁獲尾数($input$dat$caa
)の対数を取った後、正規分布乱数で生成した年齢別漁獲尾数を用いて、再度VPA計算を行います。
引数B_cv
で乱数生成の変動係数を決めることができます。デフォルトで0.2となっていますが、年齢別漁獲尾数データの不確実性が大きいことが想定される場合、この値を適宜大きくして検討してみてください。
res_vpa_caaboot <- do_caaboot_vpa(res_vpa_estb, B_ite = 10)
res_vpa_caaboot$plot_ssb
res_vpa_caaboot$plot_rec
res_vpa_caaboot$plot_biomass
一般に、VPAの資源量推定結果を信頼区間と重ねてプロットすると、先程のres_vpa_bootstrap$plot_ssb
のように最近年で信頼区間が広く、過去にさかのぼるほど信頼区間は狭くなっています。
これは決して過去の推定結果が正しいからではなく、確率変数として考えている資源量指数の不確実性を考慮しただけでは、最近の資源量の不確実性しか考慮できず、それより過去は正しいと仮定されている年齢別漁獲尾数データによって、決定的に求まっているだけなのです。 年齢別漁獲尾数の不確実性を考慮すると、時系列に関係なく資源量推定結果に不確実性があることが明らかになるかと思います。