--- title: "Standardising a realistic logbook: two worked examples" author: "Henning Winker
FAO Fisheries and Aquaculture Division, Assessment and Management Team (NFIFM)
henning.winker@fao.org
" output: rmarkdown::html_document: toc: true toc_depth: 3 toc_float: true theme: readable vignette: > %\VignetteIndexEntry{Standardising a realistic logbook: two worked examples} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set(echo = TRUE, message = FALSE, warning = FALSE, fig.width = 8, fig.height = 4.5, dpi = 110, eval = requireNamespace("cluster", quietly = TRUE) && requireNamespace("mgcv", quietly = TRUE)) library(cpueTargeting) library(ggplot2) ``` This vignette takes a realistic multispecies logbook from the raw records to standardised abundance indices for two species. The logbook is simulated with `simulate_logbook()`, so it has the problems of real data (trips that combine two tactics, coarse and missing positions, rounding, under-reported bycatch, a species recorded under two names, fleet turnover, a closure), but the true tactics and the true biomass trends are known and every step can be checked. * **Part 1** prepares the logbook. These steps are the same for every species: format the data, explore the fleet, choose the species set, look at nominal CPUE and catch composition, and derive targeting covariates. * **Part 2** standardises **blackspot seabream**, a deep-reef demersal species caught by the reef handline. * **Part 3** standardises **dolphinfish**, a seasonal pelagic migrant caught by trolling. | Step | Question | Functions | Part | |---|---|---|---| | 1. Format | Which column is what? One row per operation? | `cpue_data()`, `subset()` | 1 | | 2. Fleet | Who fished when? Specialists or mixed vessels? | `plot_fleet()`, `vessel_summary()`, `plot_vessel_profile()`, `select_vessels()` | 1 | | 3. Species | Which species carry the targeting signal? | `species_curve()`, `plot_species_curve()`, `select_species()` | 1 | | 4. Nominal CPUE | What do the raw data say? | `plot_nominal()`, `plot_composition()`, `plot_cpue_map()` | 1 | | 5. Subsetting | Which trips could catch the species? | `select_tactics()`, `stephens_maccall()` | 2, 3 | | 6. Targeting covariates | How did fishing tactics change? | `add_targeting()`, `plot_oc()`, `plot_biplot()`, `tactic_table()`, `plot_targeting()` | 1, 2, 3 | | 7. Models | Base, spatial, Cluster, DPC, vessel effects, season | `fit_cpue()` | 2, 3 | | 8. Diagnostics | Which terms matter? Does the error distribution fit? | `cpue_diagnostics()`, `plot_index()`, `plot_step()`, `plot_influence()`, `plot_residuals()`, `ppc_cpue()`, `plot_effects()`, `plot_vessel_effects()` | 2, 3 | | 9. Index | The index for the assessment | `cpue_index()`, `export_index()` | 2, 3 | How the simulator works is described in *Setting up the logbook simulator: a user guide*; the general guidelines in *Analysing real catch and effort data: a step-by-step guide*. # The logbook ```{r simulate} sim <- simulate_logbook(seed = 1) sp_tab <- sim$settings$species knitr::kable(sp_tab[, c("species", "group", "habitat", "depth_mu", "price")], row.names = FALSE) ``` Prices favour demersal species in 2000-2007 and pelagic species in 2008-2015; from 2016 prices are neutral, the bottom longline is banned deeper than 300 m and effort is cut. Nominal CPUE (orange) departs from the true biomass (black) in different ways for each species: ```{r om-cpue, fig.height = 5} sim$Plots$cpue ``` This is what an analyst receives; the tactic is not in the logbook: ```{r logbook} head(sim$logbook, 8) ``` # Part 1. Preparing the logbook ## Step 1. Format the data `cpue_data()` turns the logbook (long format: one row per trip and species) into one row per operation with one column per species, merges species synonyms, and starts a processing log. ```{r format} x <- cpue_data(sim$logbook, year = "year", species = "species", catch = "kg", effort = "hours", id = "trip", vessel = "vessel", lon = "lon", lat = "lat", covariates = "month", synonyms = c("Blackspot seabream" = "Pagellus bogaraveo")) x <- subset(x, !is.na(lon), note = "positions reported") x ``` > **Guidelines** > > * Define the **operation**: the unit at which catch and effort are recorded > (set, haul, day or trip). Here it is the trip, and about a fifth of the > trips combine sets of two tactics; their catch composition is a mixture. > * Choose an **effort** measure that scales catch (here hours fished). > * Merge **synonyms** first (here blackspot seabream recorded under its common > name in some trips); they change the species set. > * Every selection with `subset()` is written to the log (`x$log`). ## Step 2. Explore the fleet ```{r fleet, fig.height = 5} fl <- plot_fleet(x) fl$records fl$vessels fl$vessel_years ``` Vessels enter and leave over the years. Each vessel's catch profile shows how far the fleet is split into specialists: ```{r vessel-profile, fig.height = 4} groups <- list(demersal = sp_tab$species[sp_tab$group == "demersal"], pelagic = sp_tab$species[sp_tab$group == "pelagic"]) plot_vessel_profile(x, groups = groups) head(vessel_summary(x), 8) ``` Since the data are simulated, we can label the vessels with their true type: ```{r vessel-type, fig.height = 4} type <- tapply(sim$trips$vessel_type, sprintf("V%02d", sim$trips$vessel), function(z) z[1]) plot_vessel_profile(x, groups = groups, by = type) ``` > **Guidelines** > > * Strong **fleet turnover** confounds vessel efficiency with year; random > vessel effects help if vessels overlap in time. > * **Specialists** (vessels at either end of the profile plot) target the > same species throughout; their targeting is absorbed by a vessel effect. > **Mixed vessels** switch tactics with prices; that is the targeting that > the catch composition has to capture. > * An **indicator fleet** (`select_vessels()`) keeps vessels that fished > consistently, optionally with rules on the target species > (`min_years_target`, `min_share_target`). Compare the index with and > without the selection. ## Step 3. Choose the species set ```{r species} plot_species_curve(x, threshold = 0.95) targets <- c("Pagellus bogaraveo", "Coryphaena hippurus") x <- select_species(x, threshold = 0.95, keep = targets) x$species_labels ``` ```{r groups} # the species groups that are still in the data, for the selections below demersal <- intersect(groups$demersal, x$species_labels) pelagic <- intersect(groups$pelagic, x$species_labels) ``` > **Guidelines** > > * Rank species by **occurrence** (default) and check by weight. > * Always **keep the species of interest**, even if rare. > * Species characteristic of other tactics are valuable even if not > abundant: they let the PCA separate the tactics. ## Step 4. Nominal CPUE and catch composition ```{r nominal, fig.height = 4} nom <- plot_nominal(x, targets) nom$index nom$positive nom$distribution ``` ```{r composition, fig.height = 4.5} plot_composition(x) plot_cpue_map(x, res = 0.5, by_period = 8) ``` Expect the catch composition to shift towards pelagic species in 2008-2015, at the same time as the prices: a first sign of targeting. > **Guidelines** > > * The **share of positive trips** and the **distribution of positive > CPUE** guide the choice of error distribution (Tweedie or delta). > * Abrupt changes in composition at known changes in prices, regulations or > gear point to targeting; gradual drifts may be abundance. ## Step 6. Targeting covariates for the whole fleet ```{r targeting} x <- add_targeting(x) x$targeting$pca plot_oc(x) ``` ```{r targeting-plots, fig.height = 5} plot_biplot(x) tactic_table(x) pt <- plot_targeting(x, res = 0.5) pt$time pt$pcs_time pt$map_tactic ``` Check the clusters against the true tactic of each trip (not possible with real data): ```{r truth-tactics} truth <- sim$trips[match(x$data$trip, sim$trips$trip), ] round(prop.table(table(cluster = x$data$FT, true_tactic = truth$tactic), 1), 2) ``` Across the whole fleet, the first axis is typically dominated by the contrast between pelagic and demersal trips (check the loadings). For a demersal species that contrast is mostly irrelevant: what matters is the targeting *within* the demersal trips. The worked examples therefore start by selecting the relevant tactics and recomputing the PCA. > **Guidelines** > > * **Name the tactics** after their dominant species and check that they > make sense to people who know the fishery. > * **Number of PCs**: Optimal Coordinates test, not AIC; the composition is > not independent of the response, so AIC selects too many PCs. > * **Continuous or discrete?** PCs represent gradual changes and mixed trips; > clusters assume distinct tactics. > * Run `add_targeting()` again **after every selection**; PCs and clusters > depend on the operations included (`select_tactics()` does this). ```{r helper} # relative indices of several fits against the true biomass compare_indices <- function(fits, true) { idx <- do.call(rbind, lapply(names(fits), function(n) { i <- cpue_index(fits[[n]]$fit, fits[[n]]$model) data.frame(year = i$year, relative = i$relative, lower = i$lower, upper = i$upper, index = n) })) true$true <- true$true / mean(true$true) ggplot(idx, aes(x = year, y = relative, colour = index, fill = index)) + geom_ribbon(aes(ymin = lower, ymax = upper), alpha = 0.12, colour = NA) + geom_line(linewidth = 0.8) + geom_line(data = true, aes(x = year, y = true), inherit.aes = FALSE, colour = "black", linewidth = 1.1) + geom_hline(yintercept = 1, linetype = 3) + labs(x = "Year", y = "Relative CPUE", colour = NULL, fill = NULL, subtitle = "black: true biomass") + theme_bw() } ``` # Part 2. Worked example: blackspot seabream Blackspot seabream (*Pagellus bogaraveo*) lives on deep reefs and is caught mainly by the reef handline. Its price is high in 2000-2007; during the pelagic boom (2008-2015) much of the fleet trolls for pelagic species instead, and in 2016 the closure moves effort. ```{r sbr-truth} true_sbr <- logbook_truth(sim, "Pagellus bogaraveo") ``` ## Step 5. Remove the trips that could not catch seabream `select_tactics()` keeps the clusters whose catch is at least 50% demersal species and recomputes the PCA and clusters on the remaining trips. ```{r sbr-select} xd <- select_tactics(x, species = demersal, min_share = 0.5) round(xd$targeting$previous$share, 2) # demersal share of each previous cluster tail(xd$log, 3) ``` An alternative is the habitat-based selection of Stephens and MacCall (2004), which predicts the presence of seabream from the other species in each trip: ```{r sbr-sm, fig.height = 4} sm <- stephens_maccall(x, target = "Pagellus bogaraveo") sm pl <- plot_stephens_maccall(sm) pl$coefficients pl$kept ``` > **Guidelines** > > * `select_tactics()`: select on a **group of species** (here all demersal > species), not on the target alone. Clusters come from the catch > composition; keeping only clusters rich in the target would select trips > by their catch of the target, and part of its abundance signal with them. > * The removed clusters should be clearly different (here almost no > demersal catch), not borderline. > * Stephens and MacCall: check the share kept by year (`pl$kept`); a trend > means the selection itself follows the targeting. > * Compare the index with and without the selection (below). ## Step 6. Targeting covariates of the demersal trips ```{r sbr-targeting, fig.height = 5} plot_oc(xd) plot_biplot(xd) tactic_table(xd) pd <- plot_targeting(xd, res = 0.5) pd$time pd$pcs_time ``` The new PCs describe the targeting within the demersal fishery, e.g. reef against sediment species and shallow against deep (check the loadings). ## Step 7. Fit the models Terms are added in a meaningful order: year only, then space, then the targeting covariates; all models have a random vessel effect. ```{r sbr-fit} fit_sbr <- fit_cpue(xd, species = "Pagellus bogaraveo", models = c("Base", "Base+S", "Cluster+S", "DPC+S"), vessel = TRUE, spatial_k = 40, engine = "bam") fit_sbr ``` ## Step 8. Diagnostics ### Model comparison, indices and steps ```{r sbr-diag} cpue_diagnostics(fit_sbr) ``` ```{r sbr-index, fig.height = 5} plot_index(fit_sbr, true = true_sbr) ``` ```{r sbr-step, fig.height = 8} plot_step(fit_sbr) ``` ### Influence ```{r sbr-influence} plot_influence(fit_sbr, "DPC+S") ``` The influence of the targeting term should change over time where targeting changed, i.e. around the start and end of the pelagic boom (2008, 2016) and the closure (2016). ### Residuals ```{r sbr-residuals, fig.height = 4} res <- plot_residuals(fit_sbr, "DPC+S", by = "FT") res$qq res$year res$by ``` ### Predictive check of the error distribution Residuals check each trip against its own predicted distribution. `ppc_cpue()` asks whether the fitted Tweedie distribution reproduces the data **as a whole**: it simulates 50 replicate data sets from each model (with parameter uncertainty) and compares the share of zeros, the upper tail and the histogram of CPUE with the observed data. ```{r sbr-ppc} pp_sbr <- ppc_cpue(fit_sbr, models = c("Base", "DPC+S")) pp_sbr ``` ```{r sbr-ppc-plots, fig.height = 5} pp_sbr$plots$distribution pp_sbr$plots$zeros pp_sbr$plots$tails ``` > **Guidelines: reading a predictive check** > > * **Histogram** (bars: observed, zeros leftmost; points: predicted with 95% > ranges): bars outside the ranges show where the error model fails, e.g. > too many observed zeros next to too few small catches. > * **Zeros by year**: the year effect fixes the mean, not the zeros. A model > without targeting covariates gives every trip in a year the same expected > catch and misses the zeros in years when much of the fleet fished other > habitats; a good targeting term brings them inside the envelope. > * **Upper quantiles**: observed below the envelope means the model's tail > is too heavy, above too light. > * Pooled checks can hide errors that cancel across years; look at the > by-year plots (or `by = "FT"`). ### Effects and vessels ```{r sbr-effects, fig.height = 4} eff <- plot_effects(fit_sbr, "DPC+S") eff$numeric eff$space plot_vessel_effects(fit_sbr, "DPC+S") ``` > **Guidelines** > > * A targeting term that matters shows **influence that changes over time**. > * Residuals should show **no trend by year** and no pattern by tactic. > * The error distribution should reproduce the **zeros and the upper tail > by year**. > * Compare **DPC and Cluster**; if they disagree, look at the PCs over time > and ask whether relative abundances may have diverged. ### Sensitivity to the data selection ```{r sbr-sensitivity, fig.height = 4} fit_sbr_all <- fit_cpue(x, species = "Pagellus bogaraveo", models = "DPC+S", vessel = TRUE, spatial_k = 40, engine = "bam") x_sm <- add_targeting(subset(x, sm$selected, note = "Stephens & MacCall, seabream")) fit_sbr_sm <- fit_cpue(x_sm, species = "Pagellus bogaraveo", models = "DPC+S", vessel = TRUE, spatial_k = 40, engine = "bam") compare_indices(list(`demersal tactics` = list(fit = fit_sbr, model = "DPC+S"), `Stephens & MacCall` = list(fit = fit_sbr_sm, model = "DPC+S"), `all trips` = list(fit = fit_sbr_all, model = "DPC+S")), true_sbr) ``` ## Step 9. The index ```{r sbr-index-table} idx_sbr <- cpue_index(fit_sbr, "DPC+S") knitr::kable(idx_sbr, digits = 3) ``` ```{r sbr-export, eval = FALSE} export_index(fit_sbr, "seabream_index_DPC.csv", model = "DPC+S") ``` # Part 3. Worked example: dolphinfish Dolphinfish (*Coryphaena hippurus*) is caught by trolling and is only present in summer and autumn. Its price booms in 2008-2015, when much of the fleet switches to pelagic fishing. ```{r dol-truth} true_dol <- logbook_truth(sim, "Coryphaena hippurus") ``` ## Step 5. Keep the pelagic trips ```{r dol-select} xp <- select_tactics(x, species = pelagic, min_share = 0.5) round(xp$targeting$previous$share, 2) tail(xp$log, 3) ``` Outside its habitat (demersal trips) a zero says nothing about dolphinfish abundance, and the PCA of the remaining trips can now describe the targeting among pelagic species. ## Step 6. Targeting covariates of the pelagic trips ```{r dol-targeting, fig.height = 5} plot_oc(xp) plot_biplot(xp) tactic_table(xp) pp_t <- plot_targeting(xp, by = "month", res = 0.5) pp_t$time pp_t$by ``` The PCs should now separate offshore species (albacore, dolphinfish) from coastal ones (amberjack, mackerel); check the loadings. ## Step 7. Fit the models Month enters as a cyclic smoother in every model except `Base`; the index is averaged over the observed months. ```{r dol-fit} fit_dol <- fit_cpue(xp, species = "Coryphaena hippurus", models = c("Base", "Covariates", "Cluster+S", "DPC+S"), terms = 's(month, bs = "cc", k = 6)', knots = list(month = c(0.5, 12.5)), vessel = TRUE, spatial_k = 40, engine = "bam") fit_dol ``` ## Step 8. Diagnostics ### Model comparison, indices and steps ```{r dol-diag} cpue_diagnostics(fit_dol) ``` ```{r dol-index, fig.height = 5} plot_index(fit_dol, true = true_dol) ``` ```{r dol-step, fig.height = 8} plot_step(fit_dol) ``` ### Influence and residuals ```{r dol-influence} plot_influence(fit_dol, "DPC+S") ``` ```{r dol-residuals, fig.height = 4} res_dol <- plot_residuals(fit_dol, "DPC+S", by = "month") res_dol$qq res_dol$year res_dol$by ``` ### Predictive check by month and by year For a migrant the share of zeros changes strongly over the year. A model without the seasonal term predicts the same share of zeros in every month: ```{r dol-ppc-month, fig.height = 4.5} pp_dol_m <- ppc_cpue(fit_dol, models = c("Base", "DPC+S"), by = "month") pp_dol_m$plots$zeros ``` ```{r dol-ppc-year, fig.height = 5} pp_dol <- ppc_cpue(fit_dol, models = c("Base", "DPC+S"), by = "year") pp_dol pp_dol$plots$distribution pp_dol$plots$zeros pp_dol$plots$tails ``` ### Error distribution: Tweedie or delta-lognormal? A delta model lets the probability of a catch and the catch rate of positive trips change separately, e.g. when the season shortens. AIC cannot compare a Tweedie with a delta model, but a predictive check can: ```{r dol-delta, fig.height = 5} fit_dol_dl <- fit_cpue(xp, species = "Coryphaena hippurus", models = "DPC+S", family = "delta_lognormal", terms = 's(month, bs = "cc", k = 6)', knots = list(month = c(0.5, 12.5)), vessel = TRUE, spatial_k = 40, engine = "bam") pp_dl <- ppc_cpue(fit_dol_dl, by = "month") rbind(data.frame(family = "Tweedie", pp_dol_m$overall[pp_dol_m$overall$model == "DPC+S", ]), data.frame(family = "delta-lognormal", pp_dl$overall)) pp_dl$plots$distribution pp_dl$plots$zeros ``` ### Effects and vessels ```{r dol-effects, fig.height = 4} eff_dol <- plot_effects(fit_dol, "DPC+S") eff_dol$numeric plot_vessel_effects(fit_dol, "DPC+S") ``` ### Sensitivity ```{r dol-sensitivity, fig.height = 4} fit_dol_all <- fit_cpue(x, species = "Coryphaena hippurus", models = "DPC+S", terms = 's(month, bs = "cc", k = 6)', knots = list(month = c(0.5, 12.5)), vessel = TRUE, spatial_k = 40, engine = "bam") compare_indices(list(`Tweedie, pelagic tactics` = list(fit = fit_dol, model = "DPC+S"), `delta-lognormal, pelagic tactics` = list(fit = fit_dol_dl, model = "DPC+S"), `Tweedie, all trips` = list(fit = fit_dol_all, model = "DPC+S")), true_dol) ``` > **Guidelines: seasonal species** > > * Include the season (cyclic smoother) in all models except `Base`, and > check the zeros by month with `ppc_cpue(..., by = "month")`. > * Keep only trips that could catch the species; outside its season or > habitat, zeros carry no information on abundance. > * Choose the error distribution with predictive checks and residuals, not > AIC across families; report the other family as a sensitivity. > * Seasonal availability can shift between years with temperature; in real > data a `month x year` interaction or a temperature covariate may be > needed. ## Step 9. The index ```{r dol-index-table} idx_dol <- cpue_index(fit_dol, "DPC+S") knitr::kable(idx_dol, digits = 3) ``` # Processing logs Every selection step is recorded, so the final data of each index can be reported exactly. ```{r logs} xd$log xp$log ``` # Reporting checklist | Item | Blackspot seabream | Dolphinfish | |---|---|---| | Operation and effort | trip; hours fished | trip; hours fished | | Data selection | positions reported; demersal tactics (`select_tactics`, >= 50% demersal catch) | positions reported; pelagic tactics (>= 50% pelagic catch) | | Species set | 95% of occurrences, target kept | 95% of occurrences, target kept | | Targeting | PCA of demersal trips, OC test, `n_pc + 1` clusters | PCA of pelagic trips, OC test, `n_pc + 1` clusters | | Models | Base, Base+S, Cluster+S, DPC+S; random vessel effect; Tweedie | Base, Covariates (month), Cluster+S, DPC+S; random vessel effect; Tweedie and delta-lognormal | | Diagnostics | model table, step, influence, residuals, predictive check (zeros by year) | as left, predictive check by month and year, family comparison | | Sensitivity | all trips; Stephens & MacCall | all trips; delta-lognormal | | Index | DPC+S with log SE and CV | DPC+S with log SE and CV | # Things to try * `logbook_settings()` lists every setting (see *Setting up the logbook simulator*). Change the prices, the closure, the share of two-set trips (`logbook$two_sets`) and how often they switch tactic (`logbook$switch_tactic`), the reef patches (`reef_share`, `reef_scale`), or the loyalty of specialists (`loyalty`), and see how each affects the targeting covariates and the indices. * Compare `n_pc` and the number of clusters, and the species set (`select_species(..., pool_other = TRUE)`). * Define an indicator fleet with `select_vessels(..., target = ...)` and compare the indices. * Give the species diverging trends (`B_start`, `r`) and check whether the PCs start absorbing them. # References Bentley, N., Kendrick, T.H., Starr, P.J. and Breen, P.A. (2012) Influence plots and metrics: tools for better understanding fisheries catch-per-unit-effort standardizations. ICES Journal of Marine Science 69: 84-88. Dunn, P.K. and Smyth, G.K. (1996) Randomized quantile residuals. Journal of Computational and Graphical Statistics 5: 236-244. Gelman, A., Meng, X.-L. and Stern, H. (1996) Posterior predictive assessment of model fitness via realized discrepancies. Statistica Sinica 6: 733-807. Hoyle, S.D., Langley, A.D. and Campbell, R.A. (2014) Recommended approaches for standardizing CPUE data from pelagic fisheries. WCPFC-SC10-2014/SA-IP-10. Stephens, A. and MacCall, A. (2004) A multispecies approach to subsetting logbook data for purposes of estimating CPUE. Fisheries Research 70: 299-310. Winker, H., Kerwath, S.E. and Attwood, C.G. (2013) Comparison of two approaches to standardize catch-per-unit-effort for targeting behaviour in a multispecies hand-line fishery. Fisheries Research 139: 118-131. Winker, H., Kerwath, S.E. and Attwood, C.G. (2014) Proof of concept for a novel procedure to standardize multispecies catch and effort data. Fisheries Research 155: 149-159.