Skip to content

feat: support svyrep.design (replicate-weight) survey objects - #356

Open
szimmer wants to merge 2 commits into
pharmaverse:mainfrom
szimmer:355_support_svyrep_design
Open

feat: support svyrep.design (replicate-weight) survey objects#356
szimmer wants to merge 2 commits into
pharmaverse:mainfrom
szimmer:355_support_svyrep_design

Conversation

@szimmer

@szimmer szimmer commented Aug 30, 2026

Copy link
Copy Markdown

What changes are proposed in this pull request?

  • Added svyrep.design methods for ard_attributes(), ard_total_n(), ard_missing(), ard_tabulate(), ard_tabulate_value() and ard_summary(), so these functions now accept replicate-weight survey designs created with survey::svrepdesign() or survey::as.svrepdesign(). Because svyrep.design is a sibling class of survey.design rather than a subclass, such designs previously failed S3 dispatch. Statistics are computed by the same survey functions as for survey.design, all of which already support replicate designs, so variance estimates correctly reflect the replicate weights. The survey test and confidence interval functions (e.g. ard_survey_svychisq(), ard_categorical_ci(), ard_continuous_ci()) do not yet accept replicate designs. (Feature Request: Support svyrep.design (replicate-weight) survey objects #355, @szimmer)

Provide more detail here as needed.

svyrep.design is a sibling of survey.design, not a subclass:

class(survey::svydesign(...))      #> "survey.design2" "survey.design"
class(survey::as.svrepdesign(...)) #> "svyrep.design"
inherits(rep, "survey.design")     #> FALSE

so replicate designs were rejected at dispatch, before reaching any statistical code.

No new statistical code is introduced. Each new method is a thin wrapper that forwards to its survey.design counterpart, which reaches survey through .compute_svy_stat() and .svytable_counts():

survey function called replicate support
svymean, svytotal, svyvar, svyquantile, svytable dedicated svyrep.design methods
svyby handled by svyby.default
SE, deff class-agnostic extractors

Scope. This covers the first of the two blockers described in #355 — the missing S3 methods. The seven functions with hard check_class(data, "survey.design") guards are untouched here, so gtsummary::add_p() and add_ci() will still fail on replicate designs after this merges. I'd like to follow up with a second PR relaxing five of them (ard_survey_svychisq(), ard_survey_svyttest(), ard_survey_svyranktest(), ard_categorical_ci(), ard_continuous_ci()), each of which wraps a survey function that already handles replicate designs, and a third for ard_emmeans_contrast()/ard_emmeans_emmeans(), which I have not yet tested against replicate designs. Splitting it this way keeps each PR to changes I can evidence. Happy to combine them if you'd rather review one.

Tests (tests/testthat/test-svyrep.design.R) cover:

  • dispatch of all six generics on a svyrep.design
  • cards::check_ard_structure() on the results
  • weighted point estimates (n, N, p, median) agreeing exactly with the equivalent linearized design, since the sampling weights are identical
  • replicate variance genuinely being used — the p.std.error values differ from the linearized design and match survey::SE(survey::svymean(...)) on the same design, so this is not a silent fallback to linearization
  • every replicate type: JK1, JKn, bootstrap, subbootstrap, Fay, and BRR built directly with svrepdesign()
  • by= tabulation

For reference, on apiclus1:

stype linearized SE replicate SE
E 0.04633655 0.05144131
H 0.02681022 0.02777990
M 0.02964905 0.03317430

Motivation. Replicate weights are the standard variance-estimation method shipped with most large public-use survey files — NHANES, NHIS, BRFSS, ACS and the NCES surveys, and anything using BRR or jackknife. This is also the blocker for gtsummary::tbl_svysummary() supporting replicate designs (ddsjoberg/gtsummary#1441, #1535).

Reference GitHub issue associated with pull request.

Addresses #355 (the first of the two blockers described there; please leave the issue open for the check_class() guards).


Pre-review Checklist (if item does not apply, mark is as complete)

  • All GitHub Action workflows pass with a ✅
  • PR branch has pulled the most recent updates from master branch: usethis::pr_merge_main()
  • If a bug was fixed, a unit test was added.
  • If a new ard_*() function was added, it passes the ARD structural checks from cards::check_ard_structure().
  • If a new ard_*() function was added, set_cli_abort_call() has been set.
  • If a new ard_*() function was added and it depends on another package (such as, broom), is_pkg_installed("broom") has been set in the function call and the following added to the roxygen comments: @examplesIf do.call(asNamespace("cardx")$is_pkg_installed, list(pkg = "broom""))
  • Code coverage is suitable for any new functions/features (generally, 100% coverage for new code): devtools::test_coverage()

Notes on the checklist:

  • covr reports 100% for R/svyrep.design.R (6/6 lines, exercised by the new tests); package coverage is 96.73%.
  • set_cli_abort_call() and is_pkg_installed("survey") are inherited from the survey.design functions the new methods delegate to.
  • The methods share help topics with their survey.design counterparts via @rdname; each topic gained an @examplesIf block showing a replicate design.

szimmer and others added 2 commits August 30, 2026 10:42
`svyrep.design` is a sibling class of `survey.design` rather than a
subclass, so replicate-weight designs failed S3 dispatch before reaching
any statistical code.

Register `svyrep.design` methods for the six survey ard_* generics. The
implementations are shared with the existing `survey.design` methods:
statistics are computed by survey::svymean(), svytotal(), svyvar(),
svyquantile(), svytable() and svyby(), all of which already handle
replicate designs, so no separate calculation is needed.

Tests cover dispatch, ARD structure, agreement of weighted point
estimates with the linearized design, that replicate variance is
genuinely used rather than silently falling back to linearization, and
all replicate types (JK1, JKn, bootstrap, subbootstrap, Fay, BRR).

Refs pharmaverse#355

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The methods were previously alias assignments
(`ard_tabulate.svyrep.design <- ard_tabulate.survey.design`). That form
is invisible to tooling that reasons about the source statically:

  - covr reported no rows at all for R/svyrep.design.R -- the file was
    absent from the coverage report rather than scoring 0% or 100%,
    because top-level assignments carry no instrumentable expressions
  - covr::file_coverage() errored outright with
    "object 'ard_attributes.survey.design' not found", since the aliases
    resolve only at load time and depend on file collation order
  - static analysis could not resolve the right-hand sides

Defining each method as a thin wrapper that forwards to its
`survey.design` counterpart fixes all three: R/svyrep.design.R now
reports 100% coverage (6/6 lines hit by the test suite) and no longer
depends on files being sourced in a particular order. Behaviour is
unchanged -- the same `survey.design` implementations do the work, and
tidyselect arguments pass through `...` untouched.

Also:

  - add an @examplesIf block to each of the six shared help topics
    showing a replicate design built with as.svrepdesign(), and
    regenerate the .Rd files
  - scope the NEWS entry to the six functions actually covered, and note
    that the survey test and confidence interval functions do not yet
    accept replicate designs, so the release notes do not promise more
    than the change delivers
  - satisfy lintr in the tests: rename BRRrep to brr_rep (snake_case) and
    load the survey data into an explicit environment so apiclus1 is not
    flagged as an undefined global inside the helper

Refs pharmaverse#355

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant