Standardising a realistic logbook: two worked examples

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

sim <- simulate_logbook(seed = 1)
sp_tab <- sim$settings$species
knitr::kable(sp_tab[, c("species", "group", "habitat", "depth_mu", "price")],
             row.names = FALSE)
species group habitat depth_mu price
Dentex dentex demersal shallow reef 60 12
Pagellus erythrinus demersal shallow sediment 50 8
Pagellus bogaraveo demersal deep reef 350 15
Merluccius merluccius demersal deep sediment 400 5
Conger conger demersal reef, all depths 150 3
Phycis blennoides demersal deep, mixed 500 2
Thunnus alalunga pelagic offshore 700 6
Coryphaena hippurus pelagic warm, offshore 300 7
Seriola dumerili pelagic coastal, reef-associated 100 9
Scomber colias pelagic coastal 60 2

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:

sim$Plots$cpue

This is what an analyst receives; the tactic is not in the logbook:

head(sim$logbook, 8)
##   trip year month vessel hours  lon   lat               species    kg
## 1    1 2000     1    V04   7.3 3.85 36.85         Dentex dentex 256.5
## 2    1 2000     1    V04   7.3 3.85 36.85        Scomber colias  23.5
## 3    1 2000     1    V04   7.3 3.85 36.85      Seriola dumerili  14.0
## 4    2 2000     1    V12   9.7 3.85 36.85         Conger conger  83.5
## 5    2 2000     1    V12   9.7 3.85 36.85 Merluccius merluccius  24.5
## 6    2 2000     1    V12   9.7 3.85 36.85        Scomber colias   3.5
## 7    2 2000     1    V12   9.7 3.85 36.85      Seriola dumerili   9.5
## 8    3 2000     1    V10   7.8 4.85 39.35    Pagellus bogaraveo  30.5

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.

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
## <cpue_data> 19218 operations, 10 species, years 2000 - 2024 
##   vessels: 40 
##   effort: hours 
##   position: lon / lat 
##   covariates: month 
##   main species: Scomber colias, Merluccius merluccius, Thunnus alalunga, Pagellus erythrinus, Pagellus bogaraveo ... 
## Processing log:
##                 step operations                             note
##  input (long format)      19574        46775 records, 10 species
##               subset      19218 positions reported (356 removed)

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

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:

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)
##   vessel records years years_qualified first last
## 1    V10     599    25              25  2000 2024
## 2    V16     599    25              25  2000 2024
## 3    V39     587    25              25  2000 2024
## 4    V38     585    25              25  2000 2024
## 5    V18     581    25              25  2000 2024
## 6    V01     578    25              25  2000 2024
## 7    V27     576    25              25  2000 2024
## 8    V05     575    25              25  2000 2024

Since the data are simulated, we can label the vessels with their true type:

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

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
##          Scomber.colias        Seriola.dumerili           Conger.conger 
##        "Scomber colias"      "Seriola dumerili"         "Conger conger" 
##        Thunnus.alalunga     Coryphaena.hippurus     Pagellus.erythrinus 
##      "Thunnus alalunga"   "Coryphaena hippurus"   "Pagellus erythrinus" 
##      Pagellus.bogaraveo   Merluccius.merluccius       Phycis.blennoides 
##    "Pagellus bogaraveo" "Merluccius merluccius"     "Phycis blennoides" 
##           Dentex.dentex 
##         "Dentex dentex"
# 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

nom <- plot_nominal(x, targets)
nom$index

nom$positive

nom$distribution

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

x <- add_targeting(x)
x$targeting$pca
## <cpue_pca> 19218 operations, 10 species, transform: fourth_root 
##  PC eigenvalue share cumulative
##   1      2.192 0.219      0.219
##   2      1.707 0.171      0.390
##   3      1.340 0.134      0.524
##   4      1.108 0.111      0.635
##   5      1.056 0.106      0.740
##   6      0.792 0.079      0.820
##   7      0.569 0.057      0.876
##   8      0.530 0.053      0.929
## Optimal Coordinates: 3 non-trivial PC(s); 4 clusters by the npc + 1 rule
plot_oc(x)

plot_biplot(x)

tactic_table(x)
##   FT operations share              dominant Scomber colias Seriola dumerili
## 1  1       9936 0.517        Scomber colias           38.3             11.6
## 2  2       3620 0.188      Thunnus alalunga            2.2              1.9
## 3  3       2849 0.148    Pagellus bogaraveo            1.6              1.6
## 4  4       2813 0.146 Merluccius merluccius            2.4              1.6
##   Conger conger Thunnus alalunga Coryphaena hippurus Pagellus erythrinus
## 1          10.8              0.7                 8.8                15.4
## 2           1.1             79.6                10.1                 2.2
## 3           3.2              0.7                 2.0                 9.0
## 4           2.4              1.0                 1.1                 2.6
##   Pagellus bogaraveo Merluccius merluccius Phycis blennoides Dentex dentex
## 1                0.5                   0.5               0.2          13.1
## 2                0.6                   0.7               0.4           1.2
## 3               75.5                   1.6               0.7           4.0
## 4                2.1                  73.2              10.9           2.7
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):

truth <- sim$trips[match(x$data$trip, sim$trips$trip), ]
round(prop.table(table(cluster = x$data$FT, true_tactic = truth$tactic), 1), 2)
##        true_tactic
## cluster Pelagic Reef Sediment
##       1    0.43 0.33     0.24
##       2    0.96 0.02     0.02
##       3    0.05 0.85     0.10
##       4    0.07 0.16     0.78

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).
# 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.

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.

xd <- select_tactics(x, species = demersal, min_share = 0.5)
round(xd$targeting$previous$share, 2)    # demersal share of each previous cluster
##    1    2    3    4 
## 0.41 0.06 0.94 0.94
tail(xd$log, 3)
##             step operations
## 4  add targeting      19218
## 5 select tactics       5662
## 6  add targeting       5662
##                                                                                note
## 4                      3 PC(s) used (OC test: 3), 4 clusters (clara on composition)
## 5 clusters 3, 4 kept (clusters with >= 50% of catch from 6 species) (13556 removed)
## 6                      4 PC(s) used (OC test: 4), 5 clusters (clara on composition)

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:

sm <- stephens_maccall(x, target = "Pagellus bogaraveo")
sm
## <stephens_maccall> target: Pagellus bogaraveo 
##   critical probability: 0.35
##   operations kept: 3208 of 19218 (17%)
##   target present in 63% of kept and 8% of dropped operations
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

plot_oc(xd)

plot_biplot(xd)

tactic_table(xd)
##   FT operations share              dominant Scomber colias Seriola dumerili
## 1  1       1585 0.280 Merluccius merluccius            2.8              1.5
## 2  2       1581 0.279    Pagellus bogaraveo            2.2              1.3
## 3  3       1245 0.220    Pagellus bogaraveo            1.0              1.9
## 4  4        839 0.148 Merluccius merluccius            1.5              1.9
## 5  5        412 0.073     Phycis blennoides            0.0              1.1
##   Conger conger Thunnus alalunga Coryphaena hippurus Pagellus erythrinus
## 1           2.1              1.2                 0.9                 2.0
## 2           0.0              1.0                 2.9                 3.9
## 3           6.2              0.5                 1.1                14.0
## 4           2.9              0.6                 1.5                 3.5
## 5           2.6              0.0                 1.7                 7.4
##   Pagellus bogaraveo Merluccius merluccius Phycis blennoides Dentex dentex
## 1                2.4                  71.4              13.0           2.6
## 2               83.4                   1.6               0.7           3.0
## 3               68.8                   0.9               0.4           5.1
## 4                1.7                  83.5               0.1           2.8
## 5                3.7                   0.4              77.3           5.7
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.

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
## <cpue_fit> Pagellus bogaraveo - tweedie with random vessel effects 
##   operations: 5662 ; years: 2000 - 2024 
##   index averaged over: FT (and positions in spatial models) 
## Models:
##   Base         y ~ fyear + s(vessel_re, bs = "re")
##   Base+S       y ~ fyear + s(pos_x, pos_y, k = 40) + s(vessel_re, bs = "re")
##   Cluster+S    y ~ fyear + s(pos_x, pos_y, k = 40) + FT + s(vessel_re, bs = "re")
##   DPC+S        y ~ fyear + s(pos_x, pos_y, k = 40) + s(PC1, k = 4) + s(PC2, k = 4) + s(PC3, k = 4) + s(PC4, k = 4) + s(vessel_re, bs = "re")

Step 8. Diagnostics

Model comparison, indices and steps

cpue_diagnostics(fit_sbr)
##       model component    n     AIC     BIC dev_explained  edf tweedie_p
## 1      Base      main 5662 23850.1 24218.9        0.0427 53.1     1.435
## 2    Base+S      main 5662 22713.8 23332.0        0.2251 90.2     1.392
## 3 Cluster+S      main 5662 18824.1 19423.9        0.6409 87.6     1.314
## 4     DPC+S      main 5662 17529.8 18195.4        0.7321 95.4     1.336
##   vessel_sd   dAIC
## 1     0.253 6320.4
## 2     0.254 5184.0
## 3     0.271 1294.4
## 4     0.278    0.0
plot_index(fit_sbr, true = true_sbr)

plot_step(fit_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

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.

pp_sbr <- ppc_cpue(fit_sbr, models = c("Base", "DPC+S"))
pp_sbr
## Predictive check: 50 replicates, with parameter uncertainty, tweedie 
## 
##  model  stat observed median  lower  upper tail_prob
##   Base zeros    0.473  0.472  0.456  0.486      0.48
##   Base  mean    2.950  2.950  2.820  3.140      0.52
##   Base   q90   12.000 12.300 11.600 12.800      0.82
##   Base   q99   24.300 24.000 22.200 25.700      0.38
##  DPC+S zeros    0.473  0.477  0.470  0.487      0.80
##  DPC+S  mean    2.950  2.990  2.890  3.110      0.86
##  DPC+S   q90   12.000 12.800 12.400 13.400      1.00
##  DPC+S   q99   24.300 23.500 22.000 24.800      0.12
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

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

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

idx_sbr <- cpue_index(fit_sbr, "DPC+S")
knitr::kable(idx_sbr, digits = 3)
year n nominal index relative se_log cv lower upper
2000 130 3.859 2.105 1.090 0.118 0.119 0.864 1.375
2001 188 3.607 2.164 1.121 0.109 0.110 0.904 1.389
2002 168 3.587 1.960 1.015 0.113 0.114 0.813 1.267
2003 200 3.736 2.467 1.277 0.107 0.107 1.036 1.575
2004 222 3.153 2.155 1.116 0.108 0.108 0.904 1.378
2005 257 2.513 2.100 1.087 0.109 0.110 0.877 1.347
2006 325 2.766 2.401 1.244 0.103 0.103 1.016 1.522
2007 299 2.767 2.073 1.073 0.104 0.104 0.876 1.316
2008 205 2.499 1.941 1.005 0.114 0.114 0.804 1.257
2009 220 2.373 2.058 1.066 0.115 0.116 0.850 1.336
2010 214 2.944 2.143 1.110 0.108 0.109 0.897 1.372
2011 241 2.898 2.254 1.167 0.107 0.107 0.946 1.440
2012 258 1.990 1.774 0.919 0.111 0.111 0.739 1.142
2013 318 2.181 1.916 0.992 0.106 0.107 0.805 1.222
2014 364 2.099 1.875 0.971 0.104 0.105 0.791 1.191
2015 197 2.255 2.074 1.074 0.118 0.118 0.852 1.354
2016 214 3.713 1.814 0.939 0.104 0.104 0.766 1.152
2017 239 3.863 1.844 0.955 0.103 0.103 0.781 1.168
2018 207 3.927 1.777 0.920 0.105 0.106 0.749 1.131
2019 210 3.439 1.612 0.835 0.106 0.106 0.678 1.027
2020 260 3.077 1.508 0.781 0.104 0.104 0.638 0.957
2021 221 3.152 1.766 0.914 0.107 0.107 0.742 1.127
2022 192 2.998 1.465 0.759 0.112 0.112 0.609 0.945
2023 176 3.258 1.615 0.836 0.110 0.110 0.674 1.038
2024 137 2.908 1.418 0.734 0.121 0.121 0.579 0.931
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.

true_dol <- logbook_truth(sim, "Coryphaena hippurus")

Step 5. Keep the pelagic trips

xp <- select_tactics(x, species = pelagic, min_share = 0.5)
round(xp$targeting$previous$share, 2)
##    1    2    3    4 
## 0.59 0.94 0.06 0.06
tail(xp$log, 3)
##             step operations
## 4  add targeting      19218
## 5 select tactics      13556
## 6  add targeting      13556
##                                                                               note
## 4                     3 PC(s) used (OC test: 3), 4 clusters (clara on composition)
## 5 clusters 1, 2 kept (clusters with >= 50% of catch from 4 species) (5662 removed)
## 6                     2 PC(s) used (OC test: 2), 3 clusters (clara on composition)

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

plot_oc(xp)

plot_biplot(xp)

tactic_table(xp)
##   FT operations share            dominant Scomber colias Seriola dumerili
## 1  1       6558 0.484      Scomber colias           48.3             14.3
## 2  2       3507 0.259    Thunnus alalunga            2.0              1.5
## 3  3       3491 0.258 Pagellus erythrinus           10.1              4.5
##   Conger conger Thunnus alalunga Coryphaena hippurus Pagellus erythrinus
## 1           8.4              1.1                11.9                 0.4
## 2           1.2             82.3                 9.3                 0.8
## 3          16.8              0.7                 1.3                56.4
##   Pagellus bogaraveo Merluccius merluccius Phycis blennoides Dentex dentex
## 1                0.6                   0.6               0.2          14.4
## 2                0.6                   0.7               0.4           1.2
## 3                0.3                   0.4               0.3           9.3
pp_t <- plot_targeting(xp, by = "month", res = 0.5)
pp_t$time

pp_t$by
## $pc

## 
## $tactic

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.

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
## <cpue_fit> Coryphaena hippurus - tweedie with random vessel effects 
##   operations: 13556 ; years: 2000 - 2024 
##   index averaged over: FT, month (and positions in spatial models) 
## Models:
##   Base         y ~ fyear + s(vessel_re, bs = "re")
##   Covariates   y ~ fyear + s(month, bs = "cc", k = 6) + s(vessel_re, bs = "re")
##   Cluster+S    y ~ fyear + s(pos_x, pos_y, k = 40) + s(month, bs = "cc", k = 6) + FT + s(vessel_re, bs = "re")
##   DPC+S        y ~ fyear + s(pos_x, pos_y, k = 40) + s(month, bs = "cc", k = 6) + s(PC1, k = 4) + s(PC2, k = 4) + s(vessel_re, bs = "re")

Step 8. Diagnostics

Model comparison, indices and steps

cpue_diagnostics(fit_dol)
##        model component     n     AIC     BIC dev_explained  edf tweedie_p
## 1       Base      main 13556 30865.1 31348.0        0.0779 62.1     1.669
## 2 Covariates      main 13556 29756.3 30269.5        0.1919 66.1     1.644
## 3  Cluster+S      main 13556 25041.4 25801.8        0.5618 98.8     1.549
## 4      DPC+S      main 13556 19234.5 19953.3        0.8050 92.9     1.479
##   vessel_sd    dAIC
## 1     0.816 11630.5
## 2     0.791 10521.7
## 3     0.635  5806.9
## 4     0.261     0.0
plot_index(fit_dol, true = true_dol)

plot_step(fit_dol)

Influence and residuals

plot_influence(fit_dol, "DPC+S")

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:

pp_dol_m <- ppc_cpue(fit_dol, models = c("Base", "DPC+S"), by = "month")
pp_dol_m$plots$zeros

pp_dol <- ppc_cpue(fit_dol, models = c("Base", "DPC+S"), by = "year")
pp_dol
## Predictive check: 50 replicates, with parameter uncertainty, tweedie 
## 
##  model  stat observed median  lower  upper tail_prob
##   Base zeros    0.715  0.715  0.710  0.723      0.54
##   Base  mean    0.954  0.967  0.907  1.030      0.62
##   Base   q90    9.610  9.190  8.660  9.730      0.06
##   Base   q99   25.800 24.700 21.500 27.800      0.20
##  DPC+S zeros    0.715  0.716  0.711  0.721      0.68
##  DPC+S  mean    0.954  0.988  0.940  1.020      0.90
##  DPC+S   q90    9.610 10.200  9.570 10.800      0.94
##  DPC+S   q99   25.800 25.600 23.800 27.700      0.46
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:

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))
##            family model group  stat   observed     median      lower      upper
## 5         Tweedie DPC+S   All zeros  0.7148126  0.7162511  0.7110191  0.7211973
## 6         Tweedie DPC+S   All  mean  0.9539995  0.9877376  0.9401629  1.0223080
## 7         Tweedie DPC+S   All   q90  9.6052632 10.1706136  9.5709359 10.7872752
## 8         Tweedie DPC+S   All   q99 25.7524357 25.5919369 23.8012316 27.7413133
## 1 delta-lognormal DPC+S   All zeros  0.7148126  0.7152552  0.7085036  0.7207952
## 2 delta-lognormal DPC+S   All  mean  0.9539995  1.1408306  1.0670850  1.2713289
## 3 delta-lognormal DPC+S   All   q90  9.6052632 10.0403629  9.1416388 10.9862446
## 4 delta-lognormal DPC+S   All   q99 25.7524357 44.9223675 39.4275724 53.2216513
##   tail_prob
## 5      0.68
## 6      0.90
## 7      0.94
## 8      0.46
## 1      0.58
## 2      1.00
## 3      0.88
## 4      1.00
pp_dl$plots$distribution

pp_dl$plots$zeros

Effects and vessels

eff_dol <- plot_effects(fit_dol, "DPC+S")
eff_dol$numeric

plot_vessel_effects(fit_dol, "DPC+S")

Sensitivity

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

idx_dol <- cpue_index(fit_dol, "DPC+S")
knitr::kable(idx_dol, digits = 3)
year n nominal index relative se_log cv lower upper
2000 239 0.481 0.049 0.916 0.168 0.170 0.658 1.274
2001 274 0.538 0.051 0.952 0.155 0.156 0.703 1.289
2002 232 1.001 0.059 1.108 0.143 0.143 0.838 1.465
2003 275 0.655 0.043 0.797 0.145 0.146 0.600 1.060
2004 296 0.552 0.048 0.886 0.147 0.148 0.664 1.183
2005 326 0.770 0.057 1.058 0.136 0.137 0.810 1.382
2006 415 0.941 0.059 1.108 0.121 0.121 0.875 1.404
2007 354 0.498 0.056 1.046 0.136 0.136 0.802 1.365
2008 569 0.872 0.050 0.924 0.108 0.108 0.748 1.141
2009 575 1.084 0.044 0.825 0.105 0.106 0.671 1.014
2010 597 0.957 0.046 0.866 0.104 0.104 0.706 1.061
2011 649 1.131 0.057 1.069 0.099 0.099 0.881 1.298
2012 690 0.976 0.057 1.054 0.098 0.099 0.869 1.278
2013 811 1.182 0.056 1.040 0.095 0.095 0.863 1.252
2014 805 1.020 0.056 1.042 0.098 0.099 0.859 1.264
2015 562 1.011 0.055 1.023 0.104 0.104 0.835 1.254
2016 650 0.998 0.064 1.185 0.101 0.102 0.971 1.445
2017 653 1.173 0.058 1.081 0.101 0.101 0.887 1.317
2018 724 0.974 0.057 1.065 0.102 0.102 0.872 1.300
2019 680 1.143 0.067 1.253 0.099 0.100 1.031 1.522
2020 802 0.984 0.047 0.867 0.100 0.100 0.713 1.054
2021 585 0.816 0.048 0.893 0.109 0.110 0.721 1.107
2022 628 0.942 0.055 1.026 0.104 0.104 0.837 1.257
2023 593 0.916 0.053 0.983 0.105 0.105 0.800 1.208
2024 572 0.929 0.050 0.933 0.106 0.107 0.758 1.150

Processing logs

Every selection step is recorded, so the final data of each index can be reported exactly.

xd$log
##                  step operations
## 1 input (long format)      19574
## 2              subset      19218
## 3      select species      19218
## 4       add targeting      19218
## 5      select tactics       5662
## 6       add targeting       5662
##                                                                                note
## 1                                                         46775 records, 10 species
## 2                                                  positions reported (356 removed)
## 3                                        10 of 10 species kept (occurrence >= 0.95)
## 4                      3 PC(s) used (OC test: 3), 4 clusters (clara on composition)
## 5 clusters 3, 4 kept (clusters with >= 50% of catch from 6 species) (13556 removed)
## 6                      4 PC(s) used (OC test: 4), 5 clusters (clara on composition)
xp$log
##                  step operations
## 1 input (long format)      19574
## 2              subset      19218
## 3      select species      19218
## 4       add targeting      19218
## 5      select tactics      13556
## 6       add targeting      13556
##                                                                               note
## 1                                                        46775 records, 10 species
## 2                                                 positions reported (356 removed)
## 3                                       10 of 10 species kept (occurrence >= 0.95)
## 4                     3 PC(s) used (OC test: 3), 4 clusters (clara on composition)
## 5 clusters 1, 2 kept (clusters with >= 50% of catch from 4 species) (5662 removed)
## 6                     2 PC(s) used (OC test: 2), 3 clusters (clara on composition)

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.