Skip to contents

Computes CPUE estimates with standard errors and confidence intervals from a creel survey design with attached interview data. Supports both ratio-of-means (for complete trips) and mean-of-ratios (for incomplete trips) estimation methods.

Usage

estimate_catch_rate(
  design,
  by = NULL,
  variance = "taylor",
  conf_level = 0.95,
  estimator = NULL,
  use_trips = NULL,
  truncate_at = 0.5,
  targeted = TRUE,
  missing_sections = "warn",
  force_origin = TRUE
)

Arguments

design

A creel_design object with interviews attached via add_interviews. The design must have an interview survey object constructed with catch and effort columns.

by

Optional tidy selector for grouping variables. Accepts bare column names (e.g., by = day_type), multiple columns (e.g., by = c(day_type, location)), or tidyselect helpers (e.g., by = starts_with("day")). When NULL (default), computes a single CPUE estimate across all interviews.

Two kinds of column are not groupings and are refused: the interview id, as registered by add_catch(), add_lengths() or add_ages(), which holds one value per interview and so leaves no within-group variance to estimate; and columns the package derived rather than the user supplying, such as .angler_effort. A wildcard selector drops the derived columns silently; asking for one specifically is an error. A column of your own is never treated as derived, whatever it is called.

variance

Character string specifying variance estimation method. Options: "taylor" (default, Taylor linearization), "bootstrap" (bootstrap resampling with 500 replicates), or "jackknife" (jackknife resampling, automatic JKn/JK1 selection).

conf_level

Numeric confidence level for confidence intervals (default: 0.95 for 95% confidence intervals). Must be between 0 and 1.

estimator

Character string specifying estimation method. Options: "ratio-of-means" (default, for complete trips), "mor" (mean-of-ratios, for incomplete trips), or "mortr" (truncated mean-of-ratios — same as "mor" but truncate_at is mandatory and defaults to 0.5 h). MOR and MORtr require the trip_status field and error if no incomplete trips are available. See Details.

use_trips

Character string specifying which trip type to use when trip_status field is provided. Options: "complete" uses only complete trips with ratio-of-means estimator; "incomplete" uses only incomplete trips with MOR; "all" uses all interviews (complete + incomplete) with MOR — the correct choice for roving designs; "diagnostic" estimates CPUE using both trip types and returns a comparison table. Default is NULL. When NULL and interview_type = "roving" (set via add_interviews), automatically defaults to "all" + MOR. Otherwise defaults to "complete". Parameter is ignored when trip_status field is not provided (backward compatibility). For bus-route and ice designs the set is "complete" (default), "incomplete" or "diagnostic"; "all" is not an estimator there, and the roving auto-route to "all" + MOR does not apply. See Details.

truncate_at

Numeric minimum trip duration (hours) for MOR estimation. Default is 0.5 hours (30 minutes) per Hoenig et al. (1997) to prevent unstable variance from very short trips. Trips with duration < truncate_at are excluded before MOR estimation. Set to NULL to disable truncation (research mode only). Ignored for ratio-of-means estimator.

targeted

Logical. When TRUE (default), all trips are used. When FALSE, zero-catch trips are excluded before estimation — appropriate for non-targeted species where most trips have zero catch. The estimate is then the rate among the trips that were kept, not the fishery-wide rate. A cli_warn() is emitted when more than 70\ trips have zero catch and targeted = TRUE (possible mis-specification).

Which estimators read it, and what "zero catch" means to each:

  • "mor" / "mortr" without by = species: excludes trips with zero total catch.

  • "mor" / "mortr" with by = species: excludes, per species, the trips that caught none of that species. The 70\ total catch, which made them inert on any species request (GH #304).

  • "regression" with by = species: same per-species exclusion (GH #290).

  • "regression" without by = species: ignored.

  • "ratio-of-means": ignored on every path.

missing_sections

Character string controlling behavior when a registered section has no interview observations. "warn" (default) emits a cli_warn() and inserts an NA row with data_available = FALSE. "error" aborts with cli_abort(). Ignored for non-sectioned designs.

force_origin

Logical. When estimator = "regression", whether to force the regression through the origin (catch ~ effort - 1). Default TRUE (standard CPUE\(_3\) formulation per Petrere et al. 2010). Set to FALSE to allow a free intercept. Ignored for other estimators.

Value

A creel_estimates S3 object (list) with components: estimates (tibble with estimate, se, ci_lower, ci_upper, n columns, plus grouping columns if by is specified), method (character: names the estimator and the shape of the result. The base names are "ratio-of-means-cpue", "mean-of-ratios-cpue", "mean-of-ratios-truncated-cpue" and "regression-cpue", each gaining a "-sections" suffix on a sectioned design. The "-species" suffix, and the "-per-angler" suffix when normalized, mark a species-level and an angler-normalized result respectively; "regression-cpue-species" is returned for a species request under estimator = "regression"), variance_method (character: the variance that actually ran, which is the variance argument for every estimator except "regression" – the regression slope carries a leave-one-out jackknife SE and reports "jackknife" whatever variance was set to), design (reference to source creel_design), conf_level (numeric), and by_vars (character vector of grouping variable names or NULL). The estimator component records the estimator as you asked for it, "mortr" included, which method cannot: it reports mandatory truncation and the default threshold with the same string.

Details

Trip Type Selection (use_trips): When trip_status is provided, the use_trips parameter controls which trips are used for estimation. For access-point designs (interview_type = "access", the default), use_trips defaults to "complete": interviews are taken at trip end, so complete trips are representative and avoid length-of-stay bias (Pollock et al. 1994). For roving designs (interview_type = "roving"), use_trips automatically defaults to "all" with the MOR estimator: the clerk intercepts trips mid-stream, so all interviews — complete and incomplete — are valid inputs (Hoenig et al. 1997). Set use_trips = "incomplete" to restrict MOR to incomplete trips only. Set use_trips = "diagnostic" to run both complete and incomplete trip estimation and return a comparison object with difference metrics. Diagnostic mode requires both trip types. When trip_status is not provided, use_trips is ignored for backward compatibility with v0.2.0.

Ratio-of-Means (default): CPUE is estimated as the ratio of total catch to total effort. This is the appropriate estimator for complete trip interviews (interview at trip end). The function uses survey::svyratio() internally, which correctly accounts for the correlation between catch and effort in variance estimation.

Mean-of-Ratios (MOR): When estimator = "mor", CPUE is estimated as the mean of individual catch/effort ratios. This is the statistically appropriate estimator for incomplete trip interviews (interview during trip). MOR automatically filters to incomplete trips only and requires the trip_status field. The function uses survey::svymean() on individual ratios.

Trip Truncation: Very short incomplete trips can produce extreme catch/effort ratios that dominate variance estimation. Following Hoenig et al. (1997), the default truncate_at = 0.5 hours (30 minutes) excludes trips shorter than this threshold before MOR estimation. The survey design is rebuilt with the truncated sample for correct variance computation. Set truncate_at = NULL to disable truncation (research mode only). Truncation only applies to MOR estimator; ratio-of-means ignores this parameter.

The function performs sample size validation before estimation: errors if n < 10 (ungrouped or any group), warns if 10 <= n < 30. For MOR, validation uses the post-truncation sample size. This follows best practices for ratio estimation stability.

When grouped estimation is used (by is not NULL), survey::svyby() correctly accounts for domain estimation variance.

Variance estimation methods:

  • "taylor" (default): Taylor linearization, computationally efficient and appropriate for smooth statistics like ratios.

  • "bootstrap": Bootstrap resampling with 500 replicates. Appropriate for verifying Taylor assumptions.

  • "jackknife": Jackknife resampling (automatic JKn or JK1 selection based on design). Alternative resampling method.

Note

When called on a sectioned design, no .lake_total row is produced. Catch rates (fish per angler-hour) are not additive across sections. Lake-wide catch rate requires a separate unsectioned call on the full design. See estimate_total_catch() for lake-wide total catch estimation.

estimator = "regression" on a sectioned design fits one regression per section, on that section's interviews alone. The leave-one-out jackknife standard error therefore rests on the interviews in that section rather than on the whole sample, so section-level regression standard errors are based on fewer points than the unsectioned form and are correspondingly less stable. This is a property of sectioning rather than of the estimator; a section with fewer than three interviews cannot be fitted at all. species in by fits one regression per species, on that species' catch against the same angler effort, with zero-catch interviews retained by default.

Package Options

Complete Trip Percentage Threshold: The package option tidycreel.min_complete_pct controls the threshold for complete trip percentage warnings (default: 0.10 = 10\ percentage of complete trips falls below this threshold, a warning is issued referencing Pollock et al. roving-access design best practices. Users can set a custom threshold for their session:

options(tidycreel.min_complete_pct = 0.05)

The default 10\ scientifically valid estimation. Lowering the threshold is appropriate only for special cases with documented justification. Warnings help ensure data quality and guide users toward diagnostic validation when complete trip samples are insufficient.

Examples

# Basic ungrouped CPUE
calendar <- data.frame(
  date = as.Date(c("2024-06-01", "2024-06-02", "2024-06-03", "2024-06-04")),
  day_type = c("weekday", "weekday", "weekend", "weekend")
)
design <- creel_design(calendar, date = date, strata = day_type)

interviews <- data.frame(
  date = as.Date(rep(c("2024-06-01", "2024-06-02", "2024-06-03", "2024-06-04"), each = 10)),
  catch_total = rpois(40, lambda = 3),
  hours_fished = runif(40, min = 1, max = 6),
  trip_status = rep(c("complete", "incomplete"), each = 20),
  trip_duration = runif(40, min = 1, max = 6)
)

design_with_interviews <- add_interviews(design, interviews,
  catch = catch_total,
  effort = hours_fished,
  trip_status = trip_status,
  trip_duration = trip_duration
)
#> Warning: ! No `n_anglers` provided — assuming 1 angler per interview.
#>  Pass `n_anglers = <column>` to use actual party sizes for angler-hour
#>   normalization.
#>  If the interviews really are one angler each, pass `n_anglers = 1` to state
#>   that and silence this warning.
#> Warning: 3 interviews have zero catch.
#>  Zero catch may be valid (skunked) or indicate missing data.
#>  Added 40 interviews: 20 complete (50%), 20 incomplete (50%)
result <- estimate_catch_rate(design_with_interviews)
#>  Using complete trips for CPUE estimation
#>   (n=20, 50% of 40 interviews) [default]
#> Warning: Small sample size for CPUE estimation.
#> ! Sample size is 20. Ratio estimates are more stable with n >= 30.
#>  Variance estimates may be unstable with n < 30.
print(result)
#> 
#> ── Creel Survey Estimates ──────────────────────────────────────────────────────
#> Method: Ratio-of-Means CPUE
#> Variance: Taylor linearization
#> Confidence level: 95%
#> Unit: fish/party-hour
#> 
#> # A tibble: 1 × 5
#>   estimate     se ci_lower ci_upper     n
#>      <dbl>  <dbl>    <dbl>    <dbl> <int>
#> 1    0.610 0.0734    0.466    0.754    20

# Grouped by day_type
result_grouped <- estimate_catch_rate(design_with_interviews, by = day_type)
#> Warning: ! Only 0.0% of interviews are complete trips (threshold: 10%)
#>  Pollock et al. recommends >=10% complete trips for valid estimation
#>  Consider use_trips='diagnostic' to validate incomplete trip estimates
#>  Using complete trips for CPUE estimation
#>   (n=20, 50% of 40 interviews) [default]
#> Warning: Small sample size in 1 group:
#>  Group day_type=weekday: n=20
#> ! Ratio estimates are more stable with n >= 30 per group.
#>  Variance estimates may be unstable with n < 30.
print(result_grouped)
#> 
#> ── Creel Survey Estimates ──────────────────────────────────────────────────────
#> Method: Ratio-of-Means CPUE
#> Variance: Taylor linearization
#> Confidence level: 95%
#> Grouped by: day_type
#> Unit: fish/party-hour
#> 
#> # A tibble: 1 × 6
#>   day_type estimate     se ci_lower ci_upper     n
#>   <chr>       <dbl>  <dbl>    <dbl>    <dbl> <int>
#> 1 weekday     0.610 0.0734    0.466    0.754    20

# Custom confidence level
result_90 <- estimate_catch_rate(design_with_interviews, conf_level = 0.90)
#>  Using complete trips for CPUE estimation
#>   (n=20, 50% of 40 interviews) [default]
#> Warning: Small sample size for CPUE estimation.
#> ! Sample size is 20. Ratio estimates are more stable with n >= 30.
#>  Variance estimates may be unstable with n < 30.

# Bootstrap variance estimation
result_boot <- estimate_catch_rate(design_with_interviews, variance = "bootstrap")
#>  Using complete trips for CPUE estimation
#>   (n=20, 50% of 40 interviews) [default]
#> Warning: Small sample size for CPUE estimation.
#> ! Sample size is 20. Ratio estimates are more stable with n >= 30.
#>  Variance estimates may be unstable with n < 30.

# Mean-of-ratios for incomplete trips
result_mor <- estimate_catch_rate(design_with_interviews, estimator = "mor")
#>  Using incomplete trips for CPUE estimation
#>   (n=20, 50% of 40 interviews)
#> Warning: ! MOR estimator for incomplete trips. Complete trips preferred.
#>  Using MOR with n=20 incomplete of 20 total interviews.
#>  Incomplete trips may have length-of-stay bias (Pollock et al.).
#>  Validate incomplete estimates with `validate_incomplete_trips()` (Phase 19).
#>  MOR truncation: 0 trips excluded (all >= 0.5 hours)
#> Warning: Small sample size for CPUE estimation.
#> ! Sample size is 20. Ratio estimates are more stable with n >= 30.
#>  Variance estimates may be unstable with n < 30.

# Mean-of-ratios with custom truncation threshold
result_mor_1h <- estimate_catch_rate(design_with_interviews, estimator = "mor", truncate_at = 1.0)
#>  Using incomplete trips for CPUE estimation
#>   (n=20, 50% of 40 interviews)
#> Warning: ! MOR estimator for incomplete trips. Complete trips preferred.
#>  Using MOR with n=20 incomplete of 20 total interviews.
#>  Incomplete trips may have length-of-stay bias (Pollock et al.).
#>  Validate incomplete estimates with `validate_incomplete_trips()` (Phase 19).
#>  MOR truncation: 0 trips excluded (all >= 1 hours)
#> Warning: Small sample size for CPUE estimation.
#> ! Sample size is 20. Ratio estimates are more stable with n >= 30.
#>  Variance estimates may be unstable with n < 30.