Start with the design object

How tidycreel connects a creel survey to an analysis

tidycreel
rstats
fisheries
A practical introduction to the tidycreel design object and the role of tidycreel.connect in a reproducible creel-survey workflow.
Published

August 27, 2026

When I start looking at a creel survey, I usually have more than one table in front of me. There is a sampling schedule, a table of angler counts, interview records, and often a separate table for catch or lengths. Each table tells part of the story. None of them, by itself, defines the survey.

That distinction matters. A count of 25 anglers has a different meaning if it came from a sampled weekend day than if it came from an opportunistic visit on a Tuesday afternoon. Likewise, an interview record is not enough to estimate catch or harvest unless we retain the design that produced it.

This is the problem that the creel_design object in tidycreel is meant to solve. It keeps the survey structure and its observation tables together so that the estimation step can use the same design information that guided field sampling.

The design object is where analysis begins

It is tempting to think that the analysis begins when the first estimator is called. I think it begins earlier, when we state what was sampled, how it was stratified, and what each observation represents.

For a straightforward instantaneous-count survey, the smallest useful example looks like this:

library(tidycreel)

# The calendar says which days belong to each stratum.
design <- creel_design(
  example_calendar,
  date = date,
  strata = day_type
)

# The count table supplies the observations made on sampled days.
design <- add_counts(design, example_counts)

# `design` now carries both the sampling structure and the count data.
design

creel_design() does not estimate anything. It creates an object that records the survey’s calendar, date field, strata, and survey type. add_counts() then attaches the count observations and uses the design information to establish the sampling structure needed downstream. The result is still an ordinary R object, but it has an important job: it keeps the design logic from being recreated, and possibly recreated differently, every time we estimate effort, catch, or a rate.

This is particularly helpful for graduate students learning design-based inference. You do not need to start by writing the machinery that connects strata, sampling units, and weights. You do need to be able to explain those parts of your own survey. The package asks for that information in terms that look like a creel survey: calendar, date, strata, counts, interviews, catch, and lengths.

Why the schedule belongs in the analysis

In many projects the schedule is treated as paperwork that can be set aside once the field season is over. I prefer to treat it as data. It tells us the available days, the strata, and which opportunities to sample were actually selected.

That is why the calendar is the starting point for a design object. The count table tells us what was observed. The calendar tells us what those observations need to represent. If a weekday and a weekend are separate strata in the field, they should remain separate in the object used for estimation unless there is a defensible reason to combine them.

The same idea carries into interviews and catch. As the analysis grows, the design object can be extended with add_interviews(), add_catch(), and add_lengths(). Those additions do not turn a pile of tables into a black box. They make the links between the tables explicit and allow the later functions to check that the pieces make sense together.

The design object also makes error checking more useful

The design object supports two different kinds of error checking. They use the same survey context, but they have different jobs.

Automatic guardrails run under the hood when you create or extend a design object, and again when an estimator needs a particular piece of information. They are there to stop an analysis before an assumption is silently violated. For example, creel_design() requires the selected date to be a real Date with no missing values and strata to be usable categories. add_counts() can catch ambiguous repeated sampling units or information that would make a count or its variance hard to interpret. Other survey types bring their own checks: a bus-route design, for example, verifies its inclusion probabilities. These are not optional data-cleaning suggestions; they protect the weights, expansions, units, and variance calculations the estimator will use.

Diagnostic tools for the analyst are the checks you run deliberately to understand the data and decide what to do next. validation_report() is a table-level diagnostic that reports column types and flags missingness, implausible dates, negative numeric values, empty text values, and—when a species field is supplied—unmatched species names. It does not decide whether a flagged value should be deleted or changed; it gives the analyst a concise record to investigate.

check_completeness() is the design-aware companion diagnostic. After data are attached, it compares the observed tables with the calendar held by the design object and can identify scheduled days with no count data and strata with too few interviews for a dependable rate estimate. In other words, it asks not only, “Is this cell blank?” but also, “What was supposed to have been sampled here, and what does its absence mean?”

Here is a useful diagnostic sequence before estimation:

# Inspect the incoming tables; investigate any flags.
validation_report(
  counts = counts,
  interviews = interviews,
  species_col = "species"
)

# Use the calendar in the completed design to assess coverage.
design <- creel_design(calendar = schedule, date = date, strata = day_type) |>
  add_counts(counts = counts) |>
  add_interviews(interviews = interviews)

check_completeness(design)

The distinction matters because neither kind of checking substitutes for a field biologist’s judgment. A flagged long trip may be a data-entry error, or it may be the most informative trip in the dataset. The guardrails prevent invalid analysis states; the diagnostic tools surface questions that require an informed decision before a result is reported.

Where tidycreel.connect fits

Most agencies do not export a dataset with the exact variable names used in an R package. The interview identifier might be InterviewID; the date might be SurveyDate; effort might be HoursFished. That is normal. The problem comes when those translations happen informally in several scripts or, worse, by hand.

tidycreel.connect is the layer I use before building the design object. It connects to CSV files, a SQL Server database, or a REST API; applies an explicit column map; and returns tables with the canonical names expected by tidycreel.

Here is the basic CSV pattern. In a real project, the schema would document the names used by that agency’s export.

library(tidycreel)
library(tidycreel.connect)

schema <- creel_schema(
  survey_type = "instantaneous",
  interview_uid_col = "InterviewID",
  date_col = "SurveyDate",
  effort_col = "HoursFished",
  trip_status_col = "TripStatus",
  bank_anglers_col = "BankAnglers",
  angler_boats_col = "AnglerBoats"
)

conn <- creel_connect(
  con = list(
    interviews = "data/interviews.csv",
    counts = "data/counts.csv"
  ),
  schema = schema
)

interviews <- fetch_interviews(conn)
counts <- fetch_counts(conn)

The connection does not decide how the survey was designed. It performs the translation so that the analysis does not depend on whatever a particular database or spreadsheet happened to call a field. The sampling schedule remains an explicit input, and the fetched tables become the observation inputs. From there, the workflow becomes much easier to read:

design <- creel_design(
  calendar = schedule,
  date = date,
  strata = day_type
)

design <- add_counts(
  design,
  counts = counts,
  count_col = bank_anglers,
  count_time_col = count_time
)

The important point is not that every project must use a database connection. Many projects will begin with a folder of CSV files. The point is that the translation from agency data to analysis data should be declared once, inspected, and reused. That makes a handoff to a colleague, a new field season, or a future version of yourself much less fragile.

A runnable walkthrough

If you would rather learn by running a complete example, I have also prepared a one-hour tidycreel walkthrough that you can download and open in RStudio. It moves from planning a survey and building a schedule through validation, effort, catch, biological data, plots, and export. The script is deliberately more expansive than this post: use it as a guided tour, and return here when you want the reasoning behind the design object.

A useful habit before estimating anything

Before running an estimator, I would ask four questions:

  • What are my sampling units?
  • What are my strata, and why do they belong in the design?
  • Which table contains the observation, and which table describes what it represents?
  • Can another analyst see how my agency fields became the variables in the analysis?

If those answers are clear, the design object becomes more than a programming convenience. It is a compact, inspectable record of the survey logic. If they are not clear, changing estimators rarely fixes the underlying problem.

The tidycreel reference site has the function-level details. The chapter Building a Creel Design Object in Modern Creel Survey Analysis in R walks through a fuller example, including interviews and species-level catch. The companion chapter on tidycreel.connect shows the connection workflow in more detail.

In the next post, I will look more closely at the sampling calendar and why it needs to survive all the way to the final estimate.