#' Create and return a list of output stats to attach to the main model
#' by looking in the model's path for the report files.
#'
#' @param model A model list as created by [load_ss_files()]
#' @param progress_n Report every time this many files are processed. Consider
#' how many posteriors there are, this should be a fairly large proportion of
#' that (around 1/8th) or there will be too much output and it will run slow
#' @param verbose Logical. If `TRUE`, show progress messages
#' @param first Load this many of the files. If a non-positive number, load
#' them all. Used for debugging purposes to cut down the size of the
#' lists used
#' @param ... Arguments passed to [extract_rep_table()]
#'
#' @return The extra MCMC list
#' @export
load_extra_mcmc <- function(model,
progress_n = 500,
verbose = TRUE,
first = 0,
...){
if(is.null(model$extra_mcmc_path) || is.na(model$extra_mcmc_path)){
if(verbose){
message("`extra_mcmc_path` is NA or NULL, so not attempting to load ",
"extra mcmc for the model in:\n`",
model$path, "`\n")
}
return(NA)
}
if(!dir.exists(model$extra_mcmc_path)){
if(verbose){
message("The `", model$extra_mcmc_path,
"` directory does not exist, so extra mcmc files were not ",
"loaded for model located in:\n", model$path, "\n\n")
}
return(NA)
}
if(first <= 0){
first <- model$nposts
if(is.null(first)){
first <- model$mcmc |> nrow()
}
}
if(!exists("reps") || (exists("reps") && length(reps) != first)){
if(verbose){
message("Loading Report files")
}
reps <-
load_extra_mcmc_repfiles(
model$extra_mcmc_path,
file_pat = regex_extra_mcmc_report,
progress_n = progress_n,
verbose = verbose,
first = first)
}
if(!exists("compreps") || (exists("compreps") && length(compreps) != first)){
if(verbose){
message("Loading CompReport files")
}
compreps <-
load_extra_mcmc_repfiles(
model$extra_mcmc_path,
file_pat = regex_extra_mcmc_compreport,
progress_n = progress_n,
verbose = verbose,
first = first)
}
# For debugging only, uncomment these lines to save these as global.
# If they exist, the loading above will not happen
reps <<- reps
compreps <<- compreps
extra_mcmc <- list()
# Age compositions -----------------------------------------------------------
# `extra_mcmc$age_comps` is a data frame with columns "iter", "yr", "fleet",
# and 15 columns for the age comps estimated in the model labeled as the age
extra_mcmc$age_comps <- load_extra_mcmc_age_comps(
compreps = compreps,
start_yr = model$startyr,
end_yr = model$endyr + 1,
progress_n = progress_n,
verbose = verbose,
beg_pat = "^Composition_Database",
end_pat = "End_comp_data",
...)
# Initial numbers-at-age -----------------------------------------------------
ages <- model$natage |> names() |> as.numeric() |> suppressWarnings()
ages <- ages[!is.na(ages)]
max_age <- max(ages)
extra_mcmc$init_natage <- load_extra_mcmc_init_nage(
reps = reps,
start_yr = model$startyr,
end_yr = model$endyr + 1,
progress_n = progress_n,
verbose = verbose,
beg_pat = paste0("^\\d+\\s*Early_InitAge_", max_age),
end_pat = "^\\d+\\s*Early_InitAge_1\\s+",
...)
# Recruitment deviatons ------------------------------------------------------
ages <- model$natage |> names() |> as.numeric() |> suppressWarnings()
ages <- ages[!is.na(ages)]
max_age <- max(ages)
extra_mcmc$recr_devs <- load_extra_mcmc_recr_devs(
reps = reps,
start_yr = model$startyr,
end_yr = model$endyr,
progress_n = progress_n,
verbose = verbose,
beg_pat = paste0("^\\d+\\s+Early_InitAge_", max_age),
end_pat = paste0("^\\d+\\s+Late_RecrDev_", model$endyr),
...)
# Cohort recruitments --------------------------------------------------------
extra_mcmc$recr_cohorts <- load_extra_mcmc_recr_cohorts(
reps = reps,
cohorts = large_cohorts,
start_yr = model$startyr,
end_yr = model$endyr + 1,
progress_n = progress_n,
verbose = verbose,
beg_pat = "^Recr_Initial",
end_pat = "^SPRratio_1966",
...)
# Biomass --------------------------------------------------------------------
biomass_lst <- load_extra_mcmc_biomass(
reps = reps,
start_yr = model$startyr,
end_yr = model$endyr + 1,
progress_n = progress_n,
verbose = verbose,
beg_pat = "^TIME_SERIES",
end_pat = "^SPR_SERIES",
...)
extra_mcmc$total_biomass_quants <-
biomass_lst$total_biomass_quants
extra_mcmc$total_age2_plus_biomass_quants <-
biomass_lst$total_age2_plus_biomass_quants
# Selectivity -------------------------------------------------------------
sel_fishery_lst <- load_extra_mcmc_sel(
reps = reps,
start_yr = model$startyr,
end_yr = model$endyr + 1,
progress_n = progress_n,
verbose = verbose,
beg_pat = "^COMBINED_ALK",
end_pat = "^Fecund +NA +1963",
model$endyr,
type = "fishery",
...)
extra_mcmc$sel_fishery_lo <- sel_fishery_lst$sel_lo
extra_mcmc$sel_fishery_med <- sel_fishery_lst$sel_med
extra_mcmc$sel_fishery_hi <- sel_fishery_lst$sel_hi
extra_mcmc$sel_fishery_end_yr <- sel_fishery_lst$sel_end_yr
sel_survey_lst <- load_extra_mcmc_sel(
reps = reps,
start_yr = model$startyr,
end_yr = model$endyr,
progress_n = progress_n,
verbose = verbose,
beg_pat = "^COMBINED_ALK",
end_pat = "^Fecund +NA +1963",
model$endyr,
type = "survey",
...)
extra_mcmc$sel_survey_lo <- sel_survey_lst$sel_lo
extra_mcmc$sel_survey_med <- sel_survey_lst$sel_med
extra_mcmc$sel_survey_hi <- sel_survey_lst$sel_hi
extra_mcmc$sel_survey_end_yr <- sel_survey_lst$sel_end_yr
# Selectivity * Weight ------------------------------------------------------
# AKA vulnerable biomass ----------------------------------------------------
selwt_pat <- paste0(model$endyr + 1, "_1_sel\\*wt")
selwt_lst <- load_extra_mcmc_selwt(
reps = reps,
verbose = verbose,
head_beg_pat = "^COMBINED_ALK",
head_end_pat = "^Fecund +NA +1963",
beg_pat = selwt_pat,
end_pat = selwt_pat,
...)
if(is.na(selwt_lst[1])){
extra_mcmc$selwt_med <- NA
}else{
extra_mcmc$selwt_med <- selwt_lst$selwt_med
}
# Numbers-at-age ------------------------------------------------------------
natage_lst <- load_extra_mcmc_atage(
reps = reps,
verbose = verbose,
start_yr = model$startyr,
end_yr = model$endyr + 1,
txt = "numbers-at-age",
beg_pat = "^NUMBERS_AT_AGE report",
end_pat = "^BIOMASS_AT_AGE",
scale = 1e3,
progress_n = progress_n,
...)
extra_mcmc$natage_med <- natage_lst$med
# Biomass-at-age ----------------------------------------------------------
batage_lst <- load_extra_mcmc_atage(
reps = reps,
verbose = verbose,
start_yr = model$startyr,
end_yr = model$endyr + 1,
txt = "biomass-at-age",
beg_pat = "^BIOMASS_AT_AGE",
end_pat = "^NUMBERS_AT_LENGTH",
scale = 1e3,
progress_n = progress_n,
...)
extra_mcmc$batage_med <- batage_lst$med
# Catch-at-age in numbers -------------------------------------------------
catage_lst <- load_extra_mcmc_atage(
reps = reps,
verbose = verbose,
start_yr = model$startyr,
end_yr = model$endyr,
txt = "catch-at-age",
beg_pat = "^CATCH_AT_AGE",
end_pat = "^DISCARD_AT_AGE",
scale = 1,
progress_n = progress_n,
...)
extra_mcmc$catage_med <- catage_lst$med
# Catch-at-age in biomass -------------------------------------------------
extra_mcmc$cbatage_med <- load_extra_mcmc_catage_biomass(
reps,
catage_lst$atage,
model$wtatage,
verbose)
# Exploitation-rate-at-age ------------------------------------------------
if(verbose){
message("Extracting exploitation-rate-at-age...")
}
# Divide each catage value by its corresponding batage value
batage_len_catage <- batage_lst$atage |>
filter(yr %in% catage_lst$atage$yr)
expatage <- (select(catage_lst$atage, -c(yr, iter)) /
as.vector(select(batage_len_catage, -c(yr, iter)))) |>
as_tibble() |>
# Divide every cell by 1,000 to get thousands of tonnes,
# then multiply by 100 for
mutate_all(~{.x / 1000 * 100}) |>
bind_cols(select(catage_lst$atage, yr)) |>
select(yr, everything())
extra_mcmc$expatage_med <- expatage |>
group_by(yr) |>
summarize_all(median)
# Apply selectivity to numbers-at-age ---------------------------------------
if(verbose){
message("Extracting selectivity with numbers-at-age...")
}
natage <- natage_lst$atage |>
filter(yr == model$endyr + 1) |>
select(-c(yr, iter))
sel <- sel_fishery_lst$sel |>
filter(yr == model$endyr + 1) |>
select(-c(yr))
natsel <- natage * sel
extra_mcmc$natsel_prop <- natsel %>%
mutate(rsum = rowSums(.)) |>
mutate_at(vars(-rsum), ~{.x / rsum}) |>
select(-rsum) |>
as_tibble()
if(is.na(selwt_lst[1])){
selwt <- NA
natselwt <- NA
extra_mcmc$natselwt_prop <- NA
}else{
selwt <- selwt_lst$selwt |>
select(-c(yr, iter))
if(nrow(natage) != nrow(selwt)){
# Can happen if not all posteriors complete, like in 2023 for
# sensitivity model 17 (hamel prior)
new_nrow <- min(nrow(natage), nrow(selwt))
diff <- abs(nrow(natage) - nrow(selwt))
if(new_nrow == nrow(natage)){
# Re-size selwt
selwt <- head(selwt, -diff)
}else{
# Re-size natage
natage <- head(selwt, -diff)
}
}
natselwt <- natage * selwt
extra_mcmc$natselwt_prop <- natselwt %>%
mutate(rsum = rowSums(.)) |>
mutate_at(vars(-rsum), ~{.x / rsum}) |>
select(-rsum) |>
as_tibble()
}
# Catchability --------------------------------------------------------------
if(verbose){
message("Extracting survey indices...")
}
x <- load_extra_mcmc_get_chunk(reps,
beg_pat = "^INDEX_2",
end_pat = "^INDEX_1")
# This is just to get the header, x$lst is not used in this function
# Remove survey name so that conversion to numeric does not produce a ton
# of warnings
x$header <- x$header[x$header != "Fleet_name"]
x$lst <- map(x$lst, ~{
gsub("(Age1|Acoustic)_Survey", "", .x)
})
ts_q <- extract_rep_table(reps_lst = x$lst,
header = x$header,
verbose = verbose,
...)
names(ts_q) <- tolower(names(ts_q))
extra_mcmc$num_posts <- length(unique(ts_q$iter))
# Index fits and catchability (q) -------------------------------------------------
if(verbose){
message("Extracting index fits and catchabilities...")
}
extra_mcmc$q_med <- ts_q |>
mutate(calc_q = as.numeric(calc_q)) |>
group_by(fleet, yr) |>
summarize(value = median(calc_q)) |>
ungroup()
extra_mcmc$q_lo <- ts_q |>
mutate(calc_q = as.numeric(calc_q)) |>
group_by(fleet, yr) |>
summarize(value = quantile(calc_q, probs = probs[1])) |>
ungroup()
extra_mcmc$q_hi <- ts_q |>
mutate(calc_q = as.numeric(calc_q)) |>
group_by(fleet, yr) |>
summarize(value = quantile(calc_q, probs = probs[3])) |>
ungroup()
extra_mcmc$index_lo <- ts_q |>
mutate(exp = as.numeric(exp)) |>
group_by(fleet, yr) |>
summarize(value = quantile(exp, probs[1])) |>
ungroup() |>
mutate(across(-c(yr, fleet), ~{.x <- .x / 1e6}))
extra_mcmc$index_med <- ts_q |>
mutate(exp = as.numeric(exp)) |>
group_by(fleet, yr) |>
summarize(value = median(exp)) |>
ungroup() |>
mutate(across(-c(yr, fleet), ~{.x <- .x / 1e6}))
extra_mcmc$index_hi <- ts_q |>
mutate(exp = as.numeric(exp)) |>
group_by(fleet, yr) |>
summarize(value = quantile(exp, probs[3])) |>
ungroup() |>
mutate(across(-c(yr, fleet), ~{.x <- .x / 1e6}))
extra_mcmc$q_vector <- ts_q |>
filter(fleet == 2) |>
select(iter, calc_q) |>
group_by(iter) |>
slice(1) |>
pull(calc_q) |>
as.numeric()
extra_mcmc$q_vector_age1 <- ts_q |>
filter(fleet == 3) |>
select(iter, calc_q) |>
group_by(iter) |>
slice(1) |>
pull(calc_q) |>
as.numeric()
# `extra_mcmc$index_fit_posts` is needed for the survey fit plot with
# many individual MCMC posteriors. It should be deleted after, inside the
# `create_rds_file()` function
iter <- unique(ts_q$iter)
fleets <- unique(ts_q$fleet)
index_fit_posts <- ts_q |>
select(iter, yr, fleet, exp)
yr_df_lst <- index_fit_posts |>
select(yr, fleet) |>
split(~fleet) |>
map(~{.x |>
select(-fleet) |>
distinct()})
index_fit_df_lst <- index_fit_posts |>
split(~fleet) |>
map(~{
.x <- .x |>
select(-c(yr, fleet)) |>
group_by(iter) |>
group_nest()
do.call(cbind, .x$data)
})
extra_mcmc$index_fit_posts <- map2(yr_df_lst, index_fit_df_lst, ~{
.y <- .y |>
set_names(iter)
# The `.name_repair` bit below silences the New names... messages
bind_cols_quiet(.x, .y)
}) |>
map2_df(fleets, ~{
.x |>
mutate(fleet = .y) |>
select(yr, fleet, everything())
}) |>
mutate(across(-c(yr, fleet), ~{.x <- .x / 1e6}))
# Median and quantiles of expected values and Pearson ---------------------
if(verbose){
message("Extracting age composition tables...")
}
x <- load_extra_mcmc_get_chunk(compreps,
beg_pat = "^Composition_Database",
end_pat = "End_comp_data")
# This is just to get the header, x$lst is not used in this function
ts <- extract_rep_table(reps_lst = x$lst,
header = x$header,
verbose = verbose,
...)
names(ts) <- tolower(names(ts))
if(verbose){
message("Calculating pearson residuals...")
}
comp <- ts |>
filter(!is.na(nsamp_adj), nsamp_adj > 0) |>
select(c(iter, yr, fleet, bin, obs, exp, pearson)) |>
rename(age = bin)
extra_mcmc$residuals_fishery <- comp |>
filter(fleet == 1) |>
select(-fleet) |>
group_by(yr, age) |>
summarize(exp_lo = quantile(exp, probs = probs[1]),
exp_med = quantile(exp, probs = probs[2]),
exp_hi = quantile(exp, probs = probs[3]),
obs_med = quantile(obs, probs = probs[2]),
pearson_lo = quantile(pearson, probs = probs[1]),
pearson_med = quantile(pearson, probs = probs[2]),
pearson_hi = quantile(pearson, probs = probs[3])) |>
ungroup()
extra_mcmc$residuals_survey <- comp |>
filter(fleet == 2) |>
select(-fleet) |>
group_by(yr, age) |>
summarize(exp_lo = quantile(exp, probs = probs[1]),
exp_med = quantile(exp, probs = probs[2]),
exp_hi = quantile(exp, probs = probs[3]),
obs_med = quantile(obs, probs = probs[2]),
pearson_lo = quantile(pearson, probs = probs[1]),
pearson_med = quantile(pearson, probs = probs[2]),
pearson_hi = quantile(pearson, probs = probs[3])) |>
ungroup()
if(verbose){
message("\nFinished loading Extra MCMC output")
}
if(verbose){
message("Extra MCMC size = ",
f(as.numeric(object.size(extra_mcmc) / 1e6), 2),
"MB\n")
}
extra_mcmc
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.