Skip to content

Conversation

@sbfnk
Copy link
Contributor

@sbfnk sbfnk commented Dec 19, 2025

Description

This PR closes #1143.
This PR closes #1236.

Return structure

  • observations: Raw input data (list of data.frames passed to the function)
  • args: Stan data used for fitting
  • fit: The stan fit object

S3 methods

  • get_delays(): Extracts estimated truncation distribution as dist_spec
  • get_predictions(): Computes truncation-adjusted estimates from samples
  • get_samples(): Returns raw MCMC samples for truncation distribution parameters
  • summary(): Returns parameter estimates with custom print method showing distribution context
  • plot(): Validation plot comparing observations to predictions
  • print(): Shows summary (via epinowfit superclass)

Backward compatibility

  • Soft-deprecates old accessors ($obs, $data, $last_obs, $cmf, $dist) with warnings
  • [[ accessor delegates to $ for deprecated elements

Other changes

  • Renames dist_types() to dist_spec_distributions() in preparation for potential outsourcing
  • Adds epinowfit superclass for consistent print behaviour
  • Consolidates get_delays() into epinowfit method used by all three estimation functions
  • Refactors reconstruct_delay() into smaller helper functions
  • Refactors extract_delays() and extract_parameters() to take args separately (cleaner API)
  • Fixes get_samples() output consistency: all methods now return both variable (category) and parameter (specific name) columns
  • Fixes delay parameter naming with mixed parametric/nonparametric delays

Initial submission checklist

  • My PR is based on a package issue and I have explicitly linked it.
  • I have tested my changes locally (using devtools::test() and devtools::check()).
  • I have added or updated unit tests where necessary.
  • I have updated the documentation if required and rebuilt docs if yes (using devtools::document()).
  • I have followed the established coding standards (and checked using lintr::lint_package()).
  • I have added a news item linked to this PR.

After the initial Pull Request

  • I have reviewed Checks for this PR and addressed any issues as far as I am able.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 19, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The pull request converts estimate_truncation() to a formal S3 object (class "estimate_truncation", "epinowfit"), replacing legacy list fields with components observations, args, and fit. It adds new generics and S3 methods (get_delays, get_observations, get_predictions, get_samples, summary/print, and $/[[ dispatchors) and updates extract/formatting logic to use an args context for parameter/delay naming.

Changes

Cohort / File(s) Summary
Namespace exports
NAMESPACE
Added S3 registrations for $.estimate_truncation, [[.estimate_truncation, get_delays, get_observations, get_predictions, get_samples, summary.estimate_truncation, print.summary.estimate_truncation; exported get_delays and get_observations.
Estimate truncation core
R/estimate_truncation.R
Return value restructured from (dist,cmf,obs,last_obs,data,fit) → (observations,args,fit); added deprecated accessors via $.estimate_truncation and [[.estimate_truncation; plot method updated to use observations.
Accessors & delay reconstruction
R/get.R
Introduced generic get_delays() and methods get_delays.epinowfit, get_delays.estimate_truncation; generic get_observations() and methods for epinowfit/estimate_truncation; added get_predictions.estimate_truncation, get_samples.estimate_truncation; implemented helpers reconstruct_delay, reconstruct_parametric, reconstruct_nonparametric.
Summary / printing
R/summarise.R
Added summary.estimate_truncation(object, CrIs = ...) and print.summary.estimate_truncation to summarise truncation parameter estimates (exported).
Extraction API changes
R/extract.R, R/format.R, R/format.R calls
extract_parameters() and extract_delays() signatures now accept args (updated callers in formatting/plotting); extraction logic now prefers naming lookups from args.
dist_spec & Stan mapping
R/dist_spec.R, R/create.R
Added dist_spec_distributions() and updated create_stan_delays() to use it when mapping recognised distributions to Stan codes.
Class ordering changes
R/estimate_infections.R, R/estimate_secondary.R
Reordered S3 class vectors to prefer estimate_infections/estimate_secondary before epinowfit, affecting dispatch order.
Utilities / globals
R/utilities.R
Added "idx" to globalVariables.
Documentation
man/*.Rd (e.g., man/estimate_truncation.Rd, man/get_delays.Rd, man/get_observations.Rd, man/get_predictions.Rd, man/summary.estimate_truncation.Rd, man/extract_*.Rd, man/dist_spec_distributions.Rd, man/reconstruct_*.Rd)
Added/updated Rd docs for new generics, S3 methods, changed signatures (extract_* now accept args), and examples updated to use get_delays(...) / summary(...).
Tests & snapshots
tests/testthat/* (e.g., test-estimate_truncation.R, test-estimate_infections.R, test-estimate_secondary.R, test-delays.R, _snaps/*)
Updated tests to expect new object shape (observations, args, fit), deprecation warnings for legacy $/[[ access, added tests for get_delays() and fixed-delay behaviour, adjusted parameter-recovery assertions and snapshots.
NEWS / vignettes
NEWS.md, vignettes/prior_choice_guide.Rmd
Documented API change for estimate_truncation() return structure, new get_delays() generic, plotting style option addition, and updated truncation workflow examples to use get_delays(...)$truncation.

Possibly related PRs

  • PR #1180 — Standardises and extends S3 accessor methods (get_delays/get_samples/get_predictions, $/[[) and introduces delay/parameter extraction infrastructure using delay_id naming; strong overlap with this PR's additions to get.R, extract_* and NAMESPACE S3 registrations.
  • PR #1220 — Renames internal Stan parameter identifiers (e.g., dispersion → reporting_overdispersion, frac_obs → fraction_observed); closely related to this PR's changes to parameter naming and extraction logic.
  • PR #1144 — Adds/adjusts S3 get_samples interfaces and method registrations for estimate_* objects; relevant to this PR's expansion of get_samples.estimate_truncation and related method surface.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly summarizes the main change: refactoring estimate_truncation to adopt an S3 class pattern, which aligns with the primary objective of the changeset.
Linked Issues check ✅ Passed All coding requirements from issue #1143 are met: estimate_truncation now returns an S3 object with observations, args, and fit; S3 methods (summary, get_samples, get_delays, get_predictions, plot) are implemented; backward compatibility via deprecated accessors is provided; redundant elements (last_obs, cmf, dist direct access) are addressed.
Out of Scope Changes check ✅ Passed Changes to estimate_infections and estimate_secondary class reordering are supporting refactors for the epinowfit superclass alignment, directly enabling the S3 class pattern for estimate_truncation. All modifications are narrowly scoped to implement the linked objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/estimate-truncation-s3

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sbfnk sbfnk force-pushed the refactor/estimate-truncation-s3 branch from d97f2cf to a948b41 Compare December 19, 2025 17:51
@sbfnk
Copy link
Contributor Author

sbfnk commented Jan 5, 2026

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 5, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Fix all issues with AI Agents 🤖
In @R/estimate_truncation.R:
- Around line 329-341: The deprecated field handler in estimate_truncation()
maps old names to new ones but then calls NextMethod("$"), which looks up the
original (now-missing) name and returns NULL; instead, after calling
lifecycle::deprecate_warn for names in deprecated_map (obs/data), directly
return the value from the new field (use deprecated_map[[name]] to index the
object and return its contents) so est$obs and est$data continue to return
est$observations and est$args during the deprecation period rather than NULL.
- Around line 371-383: The `[[` method in estimate_truncation uses
NextMethod("[[") which forwards the old (deprecated) name that no longer exists;
update the call to forward the new name from deprecated_map instead (use the
mapped value, e.g. pass i = deprecated_map[[i]] to NextMethod) so the next
method receives the current field name; locate the deprecated_map and the
estimate_truncation `[[` method and replace the plain NextMethod call with one
that supplies the renamed field.
🧹 Nitpick comments (3)
NEWS.md (1)

44-45: Consider consolidating repeated guidance about get_delay() for clarity.

Lines 44 and 45 both instruct users to use get_delay(object). Whilst line 45 adds context about downstream usage in epinow(), the repetition could be tightened. Consider merging the removal explanation and the usage guidance into a single instruction for improved readability.

man/get_delay.Rd (2)

22-24: Clarify terminology and consider lifecycle consistency across estimate_truncation accessors.

The function is marked "experimental" (line 22), which is appropriate for a new public API. However, get_samples.estimate_truncation (documented in man/get_samples.Rd) would inherit the "Stable" lifecycle from its generic. This creates asymmetry in user messaging about API maturity for the two primary estimate_truncation accessors.

Additionally, the description (line 24) could be more explicit: "delay" is overloaded in EpiNow2 (reporting delay, generation time, truncation delay). Clarifying that this extracts the truncation delay distribution would improve clarity for new users.


26-31: Example is adequate but could show expected output.

The example demonstrates the main usage pattern (extracting the distribution for use in trunc_opts()), which is helpful. For an experimental function, this level of documentation is acceptable. Optional enhancement: show what get_delay(est) returns (a dist_spec object) to help users understand the output format and structure.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54c5f75 and e142e65.

📒 Files selected for processing (10)
  • NAMESPACE
  • NEWS.md
  • R/estimate_truncation.R
  • R/get.R
  • R/summarise.R
  • man/estimate_truncation.Rd
  • man/get_delay.Rd
  • man/get_samples.Rd
  • man/summary.estimate_truncation.Rd
  • tests/testthat/test-estimate_truncation.R
🧰 Additional context used
📓 Path-based instructions (4)
R/**/*.R

📄 CodeRabbit inference engine (CLAUDE.md)

R/**/*.R: Deprecate functionality using the {lifecycle} package. Follow the deprecation lifecycle: Warning with lifecycle::deprecate_warn(), then Stopping with lifecycle::deprecate_stop(), then Removal. Add lifecycle badge to documentation using #' @description r lifecycle::badge('deprecated')`
Use roxygen2 with Markdown syntax for documentation
Use sentence case for roxygen2 function titles, not title case. Good: 'Calculate confidence intervals'. Bad: 'Calculate Confidence Intervals'

Files:

  • R/get.R
  • R/summarise.R
  • R/estimate_truncation.R
{R/**/*.R,tests/**/*.R}

📄 CodeRabbit inference engine (CLAUDE.md)

Follow the tidyverse style guide. Limit lines to 80 characters for code and comments. Use styler package to apply styles. Don't restyle code unrelated to your changes.

Files:

  • R/get.R
  • tests/testthat/test-estimate_truncation.R
  • R/summarise.R
  • R/estimate_truncation.R
tests/testthat/test-*.R

📄 CodeRabbit inference engine (CLAUDE.md)

tests/testthat/test-*.R: Make test descriptions descriptive but brief using patterns like 'works as expected with default arguments', 'errors for bad X specifications', 'produces expected output', 'correctly handles X'
Use testthat for unit tests

Files:

  • tests/testthat/test-estimate_truncation.R
NEWS.md

📄 CodeRabbit inference engine (CLAUDE.md)

Add a NEWS.md item for user-facing changes before opening a PR. Describe user-facing changes, never reference issues or PRs. Start with action verbs: 'Added', 'Fixed', 'Updated', 'Changed'

Files:

  • NEWS.md
🧠 Learnings (2)
📚 Learning: 2025-12-19T15:41:21.706Z
Learnt from: CR
Repo: epiforecasts/EpiNow2 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T15:41:21.706Z
Learning: Applies to tests/testthat/test-*.R : Make test descriptions descriptive but brief using patterns like 'works as expected with default arguments', 'errors for bad X specifications', 'produces expected output', 'correctly handles X'

Applied to files:

  • tests/testthat/test-estimate_truncation.R
📚 Learning: 2025-12-19T15:41:21.706Z
Learnt from: CR
Repo: epiforecasts/EpiNow2 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-19T15:41:21.706Z
Learning: Applies to NEWS.md : Add a NEWS.md item for user-facing changes before opening a PR. Describe user-facing changes, never reference issues or PRs. Start with action verbs: 'Added', 'Fixed', 'Updated', 'Changed'

Applied to files:

  • NEWS.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: ubuntu-latest (oldrel-1)
  • GitHub Check: windows-latest (release)
  • GitHub Check: ubuntu-latest (release)
  • GitHub Check: macOS-latest (release)
  • GitHub Check: ubuntu-latest (devel)
  • GitHub Check: lint-changed-files
  • GitHub Check: test-coverage
  • GitHub Check: pkgdown
  • GitHub Check: R-CMD-as-cran-check
  • GitHub Check: document
🔇 Additional comments (17)
NEWS.md (1)

42-48: News entry follows guidelines and is well-structured.

The changelog entry appropriately documents user-facing breaking changes and new accessors with clear action-verb language, no PR/issue references, and helpful examples. The deprecation guidance is explicit.

man/get_samples.Rd (1)

9-9: Documentation addition is correct.

The new \alias{get_samples.estimate_truncation} and method signature are properly added and follow the established pattern for other S3 method variants in this file.

Also applies to: 22-22

NAMESPACE (1)

6-6: LGTM!

The auto-generated NAMESPACE correctly exports the new get_delay generic and registers all the new S3 methods for the estimate_truncation class ($, [[, get_delay, get_samples, summary). These additions align with the PR objectives to introduce an S3 class pattern.

Also applies to: 10-10, 18-18, 25-25, 48-48, 94-94

man/summary.estimate_truncation.Rd (1)

1-37: LGTM!

The documentation correctly describes the new summary.estimate_truncation() method with appropriate type options, follows sentence case for the title as per coding guidelines, and includes accurate return value descriptions for each type.

R/get.R (2)

256-304: LGTM!

The get_delay() generic and get_delay.estimate_truncation() method correctly extract the fitted delay parameters, construct Normal distributions for each parameter to represent posterior uncertainty, and return a properly formatted dist_spec. The experimental lifecycle badge is appropriate for this new API.


385-427: LGTM!

The get_samples.estimate_truncation() method correctly extracts posterior samples for delay parameters and reconstructed observations, combining them into a consistent format with the expected columns. The use of rbindlist(..., fill = TRUE) properly handles differing column sets between delay parameters and reconstructed observations.

R/summarise.R (1)

988-1039: LGTM!

The summary.estimate_truncation() method is well-implemented with clean dispatch based on the type argument. It correctly delegates to get_delay() for the distribution, directly accesses object$observations for observations data, and uses calc_summary_measures() for parameter summaries. The use of arg_match() ensures robust type validation.

tests/testthat/test-estimate_truncation.R (4)

22-29: LGTM!

The core test correctly validates the new return structure (observations, args, fit) and exercises the new accessors (get_delay(), summary(type = "dist"), and est$observations). Test descriptions follow the recommended patterns.


44-49: LGTM!

The cmdstanr backend variant test is appropriately updated to validate the same new structure and get_delay() accessor.


71-84: LGTM!

The filter_leading_zeros test correctly validates the new names and uses get_delay() to compare truncation distributions between fits. Accessing $dist on the returned dist_spec object (lines 77-78) and $args$obs_dist (lines 81-82) is appropriate for comparison.


97-99: LGTM!

The zero_threshold test is correctly updated to expect the new structure names and validate get_delay() returns a dist_spec.

man/estimate_truncation.Rd (2)

62-77: LGTM!

The return value documentation clearly describes the new estimate_truncation object structure with observations, args, and fit components. The guidance to use get_delay() for extracting the truncation distribution and the listing of available S3 methods is helpful for users.


128-145: LGTM!

The examples are correctly updated to demonstrate the new API patterns: get_delay(est), summary(est, type = "dist"), est$observations, and trunc_opts(get_delay(est)).

R/estimate_truncation.R (4)

55-68: LGTM!

The roxygen documentation accurately describes the new return structure and available S3 methods, following the coding guidelines for roxygen2 with Markdown syntax.


214-217: LGTM!

Storing dist_type and dist_max in stan_data (which becomes out$args) enables get_delay() to reconstruct the dist_spec with the fitted posterior parameters. This is a clean approach to preserve the distribution metadata needed for the accessor.


261-264: LGTM!

The output structure correctly uses the new naming conventions (observations instead of obs, args instead of data) as specified in the PR objectives, aligning with the estimate_infections() pattern.


288-313: LGTM!

The plot method is correctly updated to access x$observations (the renamed field) and passes it appropriately to the ggplot components.

@sbfnk sbfnk force-pushed the refactor/estimate-truncation-s3 branch from 1f8683c to b69669c Compare January 5, 2026 11:38
sbfnk and others added 4 commits January 5, 2026 12:04
get_delays() now always returns a named list of dist_spec objects,
providing a consistent return type. It extracts posterior estimates
when delay parameters were estimated (prior_sd > 0), otherwise returns
fixed values or priors.

Co-authored-by: sbfnk-bot <[email protected]>
epiforecasts-workflows bot and others added 16 commits January 14, 2026 20:08
Tests 5 scenarios for delay parameter naming:
- Single parametric delay
- Nonparametric followed by parametric (bug #1236 pattern)
- Two parametric delays
- Parametric followed by nonparametric
- Three delay types with mixed parametric/nonparametric

Co-authored-by: sbfnk-bot <[email protected]>
Remove workaround code that checked for either reporting[2] or
truncation[1] since parameters are now always named correctly.
Also removes the redundant regression test as this is now covered
by unit tests in test-delays.R.

Co-authored-by: sbfnk-bot <[email protected]>
Reduces the function from 24 lines to 3 by reusing the existing
reconstruct_delay helper that handles parameter extraction.

Co-authored-by: sbfnk-bot <[email protected]>
Moves data munging logic from estimate_truncation into a separate
internal function for better testability and code organisation.

Co-authored-by: sbfnk-bot <[email protected]>
Replace NextMethod() with .subset2() in $ and [[ methods for
estimate_truncation. NextMethod doesn't work correctly for
list-based S3 objects and can return NULL instead of the element.

Co-authored-by: sbfnk-bot <[email protected]>
- Add unit tests for prepare_truncation_obs helper function
- Add parameter recovery test verifying estimated truncation params
  match true values (meanlog ~ 0.9, sdlog ~ 0.6)
- Refactor tests to reuse single MCMC fit for better performance

Co-authored-by: sbfnk-bot <[email protected]>
- Fix obs_dist length expectation (3 datasets = 3 values)
- Replace parameter recovery test with structural test for dist_spec
  since MCMC with short chains doesn't reliably recover true params

Co-authored-by: sbfnk-bot <[email protected]>
Provides a cleaner interface for extracting a single delay distribution:
  get_delay(fit, "truncation")
instead of:
  get_delays(fit)$truncation

Co-authored-by: sbfnk-bot <[email protected]>
Since estimate_truncation inherits from epinowfit and sets up
delay_id_truncation in args, the epinowfit method works correctly.

Co-authored-by: sbfnk-bot <[email protected]>
Uses longer chains (4 chains, 2000 iter) to verify that estimated
truncation parameters are close to true values used to generate
example_truncated (meanlog ~ 0.9, sdlog ~ 0.6).

Co-authored-by: sbfnk-bot <[email protected]>
Update seealso to reference get_delays() instead of the removed
get_delays.estimate_truncation() method.

Co-authored-by: sbfnk-bot <[email protected]>
@sbfnk sbfnk enabled auto-merge January 19, 2026 11:52
@sbfnk sbfnk requested a review from seabbs January 19, 2026 11:52
seabbs
seabbs previously approved these changes Jan 19, 2026
Copy link
Contributor

@seabbs seabbs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good. I think ideally get_delay would be used in the example and vignette vs get_delays and there was instance I was expecting to see a internal function based on the review responses but didn't.

sbfnk and others added 2 commits January 19, 2026 16:00
- Add merge_predictions_obs() internal helper to deduplicate
  observation processing code in plot.estimate_truncation and
  deprecated $obs accessor
- Update examples to use get_delay(est, "truncation") instead of
  get_delays(est)$truncation
- Add unit test for merge_predictions_obs
- Fix test for [[ accessor (deprecation already fired via $)

Co-authored-by: sbfnk-bot <[email protected]>
Make it clear the helper is truncation-specific.

Co-authored-by: sbfnk-bot <[email protected]>
@sbfnk sbfnk added this pull request to the merge queue Jan 19, 2026
Merged via the queue into main with commit 65763bf Jan 19, 2026
12 of 13 checks passed
@sbfnk sbfnk deleted the refactor/estimate-truncation-s3 branch January 19, 2026 18:27
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.

Fix delay parameter naming when mixing parametric and nonparametric delays S3 class for estimate_truncation

3 participants