Attaches count-side effort data to a creel_design object and constructs the
internal survey design object eagerly. The preferred workflow is to
standardize raw count-process data into sampled-day effort rows with
prep_counts_daily_effort() (or another prep_counts_*() helper) before
calling add_counts(). This keeps survey-specific count reconstruction logic
out of the core estimator path.
Compatibility paths for raw-ish count inputs remain supported: add_counts()
can still aggregate multiple within-day observations via count_time_col and
can still compute progressive-count daily effort from circuit_time and
period_length_col. Eager construction catches design errors at add_counts()
time when users have context about what data they are adding.
period_length_col applies to instantaneous counts as well as progressive
ones. An instantaneous count is a snapshot of the anglers present at one
moment, so it becomes effort only once multiplied by the period it was
randomised within; supply that column and the estimate is in angler-hours.
Usage
add_counts(
design,
counts,
count_col = NULL,
psu = NULL,
count_time_col = NULL,
count_type = "instantaneous",
circuit_time = NULL,
period_length_col = NULL,
unit_cols = NULL,
allow_invalid = FALSE
)Arguments
- design
A creel_design object (created with
creel_design())- counts
Data frame containing count-side effort data. Preferred input: sampled-day effort rows that already have a Date column matching the design's date_col, all strata columns from the design's strata_cols, at least one numeric effort column, and a PSU column (specified via
psu, defaults to date_col). Useprep_counts_daily_effort()to build this form from raw source data.Compatibility input paths remain available for raw-ish count workflows with multiple within-day observations or progressive counts.
- count_col
Tidy selector for the numeric column holding the angler counts. Defaults to NULL, in which case the column is inferred as the only numeric column that is not design metadata (date, strata, PSU, section, count-time, or period-length). If more than one numeric column qualifies,
add_counts()aborts and lists the candidates rather than choosing by position — name the column here to resolve it. The resolved name is stored on the design and used by every downstream estimator.- psu
Character string naming the PSU (Primary Sampling Unit) column in the count data. Defaults to NULL, which uses the design's date_col as the PSU (day-as-PSU is the most common creel design). For other designs, specify the PSU column explicitly (e.g., "site_day" for day-site PSUs).
- count_time_col
Tidy selector for a column that identifies distinct sub-PSU count observations (e.g.,
count_time_col = count_timewhere count_time contains "am" / "pm"). When supplied, multiple rows per PSU are aggregated to a single PSU-level mean (C-bar_d) and within-day variance components (SS_d, K_d) are stored indesign$within_day_var. When NULL (default), a single row per PSU is expected; duplicate PSU rows emit a CNT-06 warning.- count_type
Character string specifying the count method. Must be
"instantaneous"(default) or"progressive". When"progressive",circuit_timeandperiod_length_colare required.- circuit_time
Numeric. Circuit duration \(\tau\) in hours — the time required to complete one roving count circuit of the water body. Required when
count_type = "progressive"(CNT-05). Used to compute \(\kappa = T_d / \tau\) and \(\hat{E}_d = C \times \tau \times \kappa\). Ignored (with a warning) whencount_type = "instantaneous".- period_length_col
Tidy selector for the column containing T_d — the length in hours of the period each count was randomised within. Required when
count_type = "progressive"(CNT-05), optional but strongly recommended whencount_type = "instantaneous". Values must be positive and finite. The column is dropped fromdesign$countsafter Ê_d is computed (it must not be passed toestimate_effort()as a count variable).- unit_cols
Optional character vector naming the columns that together identify one sampling unit. When omitted, the unit is inferred from the design: the PSU column plus any strata, section, and site columns.
Supply it when the counts table carries a dimension the design does not declare. The commonest case is
effort_type, whichprep_counts_daily_effort()emits: bank and boat counts on the same day are two units, not one day counted twice. Inference cannot see such a column, so it would treat those rows as repeats — warning about them withoutcount_time_col, and averaging across them with it (GH #162).You are not required to guess when this matters. If aggregation would collapse rows that differ in an undeclared column,
add_counts()aborts and names the column rather than silently taking its first value.For instantaneous counts, supplying this is what makes the estimate angler-hours. A count is a snapshot of how many anglers were present at one moment; effort is that count times the period it was randomised within, \(\hat{E}_d = \bar{C}_d \times T_d\) (Hoenig et al. 1993). Without it,
estimate_effort()expands the counts to the season and returns them unmultiplied, and warns once per session that it has done so.Not accepted on aerial designs, which carry their period length as
h_openand scale the count byh_open / vwhen estimating. Supplying both would apply time twice, so this errors rather than choosing one.T_d is a property of the survey protocol — the period set by regulation, access hours, or field practice. It is not astronomical daylight.
day_length()is for simulation and planning; do not feed it here as a substitute for the period actually surveyed.The multiplication happens per PSU, before aggregation. Converting after the fact computes \(\bar{C} \times \bar{T}\) where the target is the mean of \(C \times T\); the two differ by Cov(C, T), which is positive in practice because anglers fish more on long days, so the collapsed form biases low.
- allow_invalid
Logical flag for validation behavior. If FALSE (default), validation failures abort with detailed error messages. If TRUE, validation failures generate warnings and attach counts anyway (use with caution).
Value
A new creel_design object (list) with components:
- calendar
Original calendar data frame
- date_col
Character name of date column
- strata_cols
Character vector of strata column names
- site_col
Character name of site column, or NULL
- design_type
Character design type
- counts
The count data frame (newly attached)
- psu_col
Character name of PSU column
- survey
Internal survey.design2 object (newly constructed)
- validation
creel_validation object with Tier 1 results
Immutability
add_counts() follows functional programming patterns and returns a new
creel_design object. The original design object is not modified. This
prevents accidental data loss and makes the workflow explicit:
design2 <- add_counts(design, counts) not add_counts(design, counts)
Validation
add_counts() performs Tier 1 validation:
Count data schema (Date column, numeric column) via validate_count_schema()
Design column presence (date_col, strata_cols, PSU in count data)
No NA values in design-critical columns (date, strata, PSU)
Survey construction (catches lonely PSU errors, stratification issues)
Party-size expansion carriers
derive_angler_count() attaches expansion_basis, expansion_se,
expansion_group, and expansion_of to the counts table so the party-size
sampling error can reach the effort standard error. The four are written
together and must travel together: add_counts() aborts on a table carrying
only some of them, since that state can only come from partial deletion and
would otherwise drop the variance component while leaving an expansion_se
visible in the data.
add_counts() also aborts when count_col is not the column named in
expansion_of. The basis is d(count)/d(party_size), so a count transformed
after derive_angler_count() no longer matches the basis carried beside it,
and the variance component would come out understated by exactly the scale
factor while still reading as propagated. Supply the untransformed count and
period_length_col, which scales the count and the basis together.
Dropping all four — which an ordinary select() will do — is
indistinguishable here from counts that never had them, and is silent.
PSU Specification
Per user decision (clarified 2026-02-08), PSU is specified only in add_counts(), not in creel_design() constructor. PSU is only meaningful when count data is present, making add_counts() the correct abstraction boundary. This design allows the same creel_design calendar to be used with different PSU structures.
See also
Other "Survey Design":
add_catch(),
add_interviews(),
add_lengths(),
add_sections(),
as_creel_svydesign(),
as_hybrid_svydesign(),
compute_angler_effort(),
compute_effort(),
creel_design(),
creel_schema(),
creel_vocabulary(),
derive_angler_count(),
est_effort_camera(),
impute_camera_counts(),
mean_party_size(),
prep_counts_boat_party(),
prep_counts_daily_effort(),
prep_interview_catch(),
prep_interviews_trips(),
validate_creel_schema()
Examples
# Preferred workflow: standardize sampled-day effort before attachment
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)
raw_counts <- data.frame(
sample_date = as.Date(c("2024-06-01", "2024-06-02", "2024-06-03", "2024-06-04")),
day_type = c("weekday", "weekday", "weekend", "weekend"),
effort_kind = c("bank", "bank", "bank", "bank"),
effort_value = c(15, 23, 45, 52)
)
counts_ready <- prep_counts_daily_effort(
raw_counts,
date = sample_date,
strata = day_type,
effort_type = effort_kind,
daily_effort = effort_value
)
design_with_counts <- add_counts(design, counts_ready)
#> Warning: No weights or probabilities supplied, assuming equal probability
print(design_with_counts)
#>
#> ── Creel Survey Design ─────────────────────────────────────────────────────────
#> Type: "instantaneous"
#> Date column: date
#> Strata: day_type
#> Calendar: 4 days (2024-06-01 to 2024-06-04)
#> day_type: 2 levels
#> Counts: 4 observations
#> PSU column: date
#> Count column: daily_effort
#> Party-size term: not carried
#> Count type: "instantaneous"
#> Survey: <survey.design2> (constructed)
#> Interviews: "none"
#> Sections: "none"
# Compatibility path: raw count rows with a custom PSU column
counts_with_site_psu <- 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"),
site_day = paste0("site_", 1:4),
count = c(15, 23, 45, 52)
)
design2 <- add_counts(design, counts_with_site_psu, psu = "site_day")
#> Warning: No weights or probabilities supplied, assuming equal probability
# Compatibility path: multiple counts per day (within-day variance via count_time_col)
# Two circuits per day: "am" and "pm"
calendar2 <- 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")
)
design3 <- creel_design(calendar2, date = date, strata = day_type)
multi_counts <- data.frame(
date = as.Date(rep(c("2024-06-01", "2024-06-02", "2024-06-03", "2024-06-04"), each = 2)),
day_type = rep(c("weekday", "weekday", "weekend", "weekend"), each = 2),
count_time = rep(c("am", "pm"), 4),
n_anglers = c(12, 18, 20, 26, 40, 50, 48, 56)
)
design_multi <- add_counts(design3, multi_counts,
count_time_col = count_time # nolint: object_usage_linter
)
#> Warning: No weights or probabilities supplied, assuming equal probability
# Compatibility path: progressive count type (Ê_d = C x circuit_time x kappa)
calendar3 <- 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")
)
design4 <- creel_design(calendar3, date = date, strata = day_type)
prog_counts <- 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"),
n_anglers = c(15L, 23L, 45L, 52L),
shift_hours = rep(8, 4)
)
design_prog <- add_counts(
design4, prog_counts,
count_type = "progressive",
circuit_time = 2,
period_length_col = shift_hours # nolint: object_usage_linter
)
#> Warning: No weights or probabilities supplied, assuming equal probability
