csfmt_rts_data_v1 table methods of short_term_trend() and signal_detection_hlm() are supported for counts without delayed reporting. The documentation no longer calls them deprecated, and their code does not change.glue moves from Suggests to Imports, because the table method of short_term_trend() calls it. csalert no longer imports ggplot2 or suggests csmaps, because no code uses them.dev/ and tests/manual/, the CRAN submission files, the R-hub workflow, two unused internal data generators and commented-out code. R/nowcast_evaluate.R is now R/nowcast_score.R, named after nowcast_score_v1().short_term_trend() floors the quasi-Poisson dispersion at 1. A window with less variation than Poisson now gets the Poisson standard error. Before, it could get a near-zero one, and a <measure>_trend_increasing_pr of exactly 0 or 1.interval and outage_gap_days arguments of nowcast_delay_ecdf_v1(). On 52 replayed weeks of Norwegian surveillance data, neither option beat the default empirical interval by enough to matter. Output without these arguments does not change. A call that still passes either argument stops with an error, so it cannot silently get the default.nowcast_estimate_calibration_v1(), nowcast_apply_calibration_v1() and nowcast_evaluate_v1(). The two calibration functions rescaled the intervals after the fit, so calibrate the intervals in the nowcast model instead. Replace nowcast_evaluate_v1() with nowcast_backtest(), nowcast_truth() and nowcast_score_v1(), as stage 2 of vignette("pipeline", package = "csalert") shows.nowcast_delay_ecdf_v1(), the default path and the option path share one selection of the weeks to complete. The output does not change.reporting_triangle_matrix() returns a new element, late: the count of each reference week reported at delay max_delay_days or later. These reports no longer count in the last delay column of mat.nowcast_delay_ecdf_v1() estimates the count reported within max_delay_days days, and nowcast_truth() returns that count. Before, the last delay column also held every later report, so a backtest scored the nowcast against everything ever reported.original, <denominator_col>_observed and the draws of a week that is not nowcast are rowSums(mat) + late. So nowcast_passthrough_to_ensemble_v1() and reporting_completion_v1() give the same output as in 2026.10.10.nowcast_delay_ecdf_v1() leaves a week out of the pool when all its reports are past the horizon, as in a bulk load of old weeks. The week stays in the output with its observed count.max_delay_days or later, the nowcast engines, nowcast_truth(), nowcast_backtest() and reporting_completion_v1() give output identical to 2026.10.10.R CMD check no longer gives a NOTE for qc_week_over_week_v1(): the column it filters on in 2026.10.8 is declared as a data.table column.family = "identity" kernel test of short_term_trend() compares the draws with expect_equal(tolerance = 1e-12) instead of expect_identical(). On macOS arm64 the draws differed from x86_64 in the last bits, so the r-universe macOS checks failed since at least 2026.10.7. A change of 1e-8 still fails the test.qc_week_over_week_v1() has a new argument, model_pattern, default "_trend_(gr|beta1)_". $integrity leaves out the columns whose names match it. Before, the Monte-Carlo noise of trend estimates in settled weeks filled $integrity: 2314 growth-rate and 217 slope rows of 2599 in one weekly run, which hid the real data revisions. Roleless published medians, such as cases_q50x0, are still compared.nowcast_delay_ecdf_v1() has two new options, outage_gap_days and
interval = "log_robust". A few pool weeks with a delivery outage have
extreme pool ratios T_s / O_s, and these set the upper limit of the
interval. The defaults are unchanged and give output identical to 2026.9.24
for the same seed.outage_gap_days removes a pool week from the pool when its delay days 0 to
d hold that many or more consecutive dates with no delivery. Here d is
the age in days of the reference week to nowcast. The full pool stays when
that week has its own outage, or when fewer than 3 pool weeks would remain.interval = "log_robust" draws the log pool ratio from a normal distribution
with the median and the MAD of the log pool ratios. The median and the MAD
change little when a few pool weeks have extreme ratios.nowcast_score_v1() is new and exported. It scores replayed nowcast
quantiles against the settled truth. The scores are the weighted interval
score (WIS), the WIS after log1p(), the 95% coverage and the 95% interval
width relative to the truth. Coverage alone rewards an interval that is too
wide, and WIS penalises both a miss and the width.nowcast_evaluate_v1() calls nowcast_score_v1() and gains the columns
wis, wis_log, coverage_95 and width_95_rel_median. The earlier
columns keep their values.reporting_triangle_matrix() sums the counts in a cell with na.rm = TRUE.
Before, one NA count set its whole cell to 0, so the real counts in that
cell were lost with no warning.nowcast_backtest() returns an empty data.table when no report is inside
the horizon and as_of_weeks is NULL. Before, it errored with
"NA/NaN argument".isoyearweek_week_start() is exported. It returns the Monday that starts an
ISO week, the day every delay counts from.nowcast_delay_ecdf_v1() draw is the observed
count times a quantile of the pool ratio T_s / O_s. The help page and the
pipeline vignette said observed / p(d), but p(d) cancels out.sim_reports() emits no delay beyond day 34.nowcast_delay_ecdf_v1() help page says that a
settled pool week can still receive late reports, so a long backlog makes the
nowcast run low, and that a pool week with no early reports cannot enter the
pool.mem_thresholds_v1() help page says it adds the five
threshold columns to $data, by reference, and not to $draws.signal_detection_hlm() help page says the ensemble
method codes a draw 1 for null and 2 for high, not 0 and 1.simulate_baseline_data() help page says phi = 1
gives Poisson counts. It said phi = 0, which gives NA.remove_last_isoyearweeks of
the deprecated signal_detection_hlm() method has no effect.prediction_interval() and its glm method are internal,
and left the reference index. The generic was never exported.denominator belongs
only to the deprecated trend method.75c0751, so this tree needs a
higher number.A late report is a report at delay max_delay_days or later, counted in days
from the reference week's Monday.
max_delay_days - 1.
Earlier versions dropped it. The delay columns are unchanged, 0 to
max_delay_days - 1.rowSums() of this matrix as original. Measured on 2026-09-23 in
luftveisovervaking_trend with one input file, s19_hosp_sari lost every
week from 2020-01 to 2025-26. That is 287 weeks and 346,306 admissions,
nearly all bulk-loaded on 2025-07-30. The settled weeks lost a net 2,453 in
msis_covid19, 1,367 in lab_hospitalised_sars_cov_2 and 448 in
daar_sari_scd2.original, every nowcast, nowcast_truth(), and mean_delay and
the pct_delayD curve of reporting_completion_v1(). A week whose every
report was late now carries its count, where before it was absent or zero.max_delay_days - 1 can still arrive and raise its count.2dfed42, which is this tree's
parent. This tree needs a number above it. 2026.9.23 is the release date of
this tree.The nowcast stack moves from a weekly delay axis to a daily one, and the default engine changes. Two of the six breaking changes below return wrong numbers rather than an error.
csfmt_reporting_triangle_v3() defaults reporting_col to
reporting_date, and that column MUST be a Date.
isoyearweek_reporting is gone. A character column raises an error naming
the class it got.as_of attribute is a Date. It held the largest ISO-week string
before. Code that compared as_of against an ISO week now compares two
types.NA used to set as_of to NA. No reference week then
settled, no delay pool built, and every engine returned the observed totals
while labelling them a nowcast. Densification dropped the row as well, so
the week's own count was understated too. Map every missing reporting date
to a date, or drop those rows, before you build the triangle.max_delay_days = 35 keeps delay days 0 to 34 inclusive, which is 35
columns. That is the span the old max_delay = 5 weekly columns covered.
Multiply an old weekly value by 7.max_delay_days:
reporting_triangle_matrix(), nowcast_passthrough_to_ensemble_v1(),
nowcast_truth(), nowcast_backtest(), nowcast_evaluate_v1(),
nowcast_delay_ecdf_v1(), reporting_completion_v1() and
reporting_completion_trend_v1().reporting_completion_v1() returns one pct_delay column per delay
day. A call that returned pct_delay0 to pct_delay4 now returns
pct_delay0 to pct_delay34, so 5 columns become 35. There are exactly
max_delay_days of them.max_delay_days, because their axis is the delay
in days. One name across both would mean days here and weeks there.max_delay_weeks to match the engines. The old name was
max_delay, and each new name states its own unit.truth / estimate ratios, and no longer parametric.nowcast_quasipoisson_v1() no longer exists. A call to it raises "could not
find function".n[ref, d] ~ Poisson(lambda[ref] * p[d]), the same
Poisson delay model. It estimates the delay profile saturated, one number
per delay day, rather than through a regression.coef() returns NA. The engine then fell back to
the observed count and labelled it a nowcast. Measured on
lab_hospitalised_influensavirus: 27 of 27 replays at horizon 2.670fe584, which is this tree's
parent. One version must never name two trees, so this tree takes a number
above it. 2026.9.22 is the release date of this tree.README.md told users to run install.packages("csalert"). With no repos
set that reaches CRAN, which carries 2024.6.24 from 2024-06-24. It installs
without complaint, so a reader following the README got a release two years
behind and nothing said so.repos to niphr.r-universe.dev first, which builds
every commit on main, and says to put that in ~/.Rprofile.pak can express, measured against pak 0.11.1: a
version range such as csalert@>=2026.8.27 is rejected with "Version ranges
are not implemented yet", an exact [email protected] pin works, and a
DESCRIPTION floor of Imports: csalert (>= x) resolves through
pak::local_install_deps().No code changed. This release is documentation only.
44a33f02 and then from 0f719ece. Two
trees under one version is the defect CalVer prevents, so the README tree
takes a number of its own. 2026.8.28 is the release date of this tree.short_term_trend() on a csfmt_ensemble_v3 no longer takes
propagate_slope_error. The slope's own sampling error now reaches every
draw, always, and there is no way to switch it off.increasing_pr is the share of draws whose slope is positive, so
without the second it is a sign test on the point slope wearing the name of a
probability. A threshold like > 0.975 on it then does nothing at all. That
was the default behaviour, and nothing warned._trend_beta1, _trend_gr and
_trend_increasing_pr a caller was producing under the old default is
different now, and the trend columns are no longer reproducible run to run
without a seed. Set one if you need that.$draws[["<measure>_trend_beta1"]] is no longer the point estimate. It
holds the perturbed draws. Call rolling_slope_matrix() for the unperturbed
slope, its standard error and its convergence flag.propagate_slope_error now raises an error naming the removal. The
method takes ..., so without that guard a caller who had turned it off
would get propagated numbers and no notice.family = "identity" and family = "quasipoisson" now refuse
trend_isoyearweeks = 2. Both read a dispersion off width - 2 residual
degrees of freedom, so se has no value at width 2 and no error_reference
repairs it. family = "binomial" fixes the dispersion at 1 and still accepts
width 2.n_sim moved from argument 5 to argument 4, which changes the meaning of a
positional call.error_reference is unchanged and still selects the reference distribution.The csfmt_rts_data_v1 method is untouched. It fits its own GLM per window and
never had this argument.
576b9c51, so this tree needs a
number above it. 2026.8.27 is the release date of this tree.short_term_trend() on a csfmt_ensemble_v3 takes family.
"quasipoisson" is a log link and "binomial" is a logit link. beta1 is
then the slope on the link scale. That is the coef(model)[2] the older
glm() pipelines report, so the two become comparable. "identity" is the
default and reproduces the previous numbers exactly.100 * (exp(beta1) - 1), a percent change
per week. Under "binomial" that percent change is in the odds.E[Y] = exp(b0 + b1 t) and
never takes log(Y).family = "binomial" needs the new denominator argument, which names the
$draws measure holding the binomial denominator. It enters the fit as the
prior weight, and measure MUST then be a proportion between 0 and 1.stats::glm().maxit stops on is finite, so is.finite() never removed it. A
window of c(0, 0, 0, 0, 0.2, 0.3, 0.4, 0.5) separates at width 4, with a
denominator of 50. Its growth rate reached $draws as 1.06e13 percent per
week. beta1, se and the growth rate are now NA_real_ there.rolling_slope_matrix() returns a fourth matrix, converged. It is logical,
and NA on the leading width-1 rows that hold no window.beta0 is NA_real_ there too. A lone intercept beside a missing slope reads
as a fitted level, and it is the same meaningless iterate.NA, and stats::glm() returns a slope of 0.
That is the one place the two disagree on purpose. stats::glm() stops on the
relative change in the deviance, which is 0 for an all-zero window from the
first iteration. This kernel stops on the coefficient step, and that step
never settles: the intercept marches to -Inf as the fitted mean goes to 0.
An all-zero window identifies no slope, and NA says so.maxit
separately. It claimed maxit iterations before. A batch holding a
missing-value window can settle earlier, because the stopping rule drops that
window with na.rm.short_term_trend() on a csfmt_ensemble_v3 takes error_reference.
"auto" is the default. It gives "identity" and "quasipoisson" a t on
trend_isoyearweeks - 2 degrees of freedom. Both estimate a dispersion from
that many residual degrees of freedom. That is the case summary.glm()
refers to a t."binomial" keeps a standard normal under "auto", because that family
fixes the dispersion at 1 and leaves nothing to estimate.error_reference = "normal" to match a pipeline whose interval comes
from stats::confint(), which profiles the deviance against an asymptotic
chi-squared. "t" forces the other direction on every family.propagate_slope_error = TRUE now needs trend_isoyearweeks >= 3 under
family = "identity" and family = "quasipoisson", whatever the
error_reference. Both read a dispersion off width - 2 residual degrees of
freedom, so at width 2 se is NaN under identity and Inf under
quasi-Poisson. The growth rate then reached $draws missing, or as a random
-100 percent. family = "binomial" fixes the dispersion at 1 and still
accepts width 2.family = "identity" is unchanged. It drew se * t_(width-2) before and it
still does.R/ passes the shared static-checks gate. The gate arrived with the
workflow pin two releases ago and reported 127 findings across 23 of the 24
code files. Every one is now fixed.stop() and warning() names call. = FALSE, because the message
goes to a user who cannot act on the call. Every function returns explicitly.
seq_len() and seq_along() replace 1:n, && and || replace & and
| inside a condition, and TRUE and FALSE replace T and F.short_term_trend_ensemble.R, rolling_irls_slope() gave
up irls_check_response(), irls_working() and irls_wald_se(), and
short_term_trend.csfmt_ensemble_v3() gave up stt_prior_weights() and
stt_error_reference().ed0749b8, so this tree cannot
reuse that number. 2026.8.26 is the release date of this tree.utils::globalVariables() list is gone. Each NSE column name is
now declared as NULL at the top of the one function that uses it: 18
declarations across 12 files. Only . stays package-wide, because it is read
as an undefined function and no local binding fixes that.level, method, period,
role and truth are also formal arguments of other functions, so declaring
them package-wide suppressed the note for a genuine undefined variable
anywhere in the package. coverage_raw and horizon were declared and used
nowhere.R/csfmt_ensemble.R already used this idiom at set_time_series_id(). The
package now uses it everywhere.The package drops magrittr. Every %>% is now the base pipe |>, and
magrittr is gone from DESCRIPTION.
The rewrite is a relocation, not an edit. Each %>% call was transformed the
way R's parser transforms |>, and the resulting tree was required to match
the tree parsed from the rewritten file. A file whose trees disagreed was left
untouched and converted by hand instead.
The version runs one day ahead of the calendar on purpose. r-universe already published today's number from a different commit, and one version must never name two trees.
R/globals.R declares ".". R CMD check read data.table's .(), its alias
for list(), as an undefined function rather than an undefined variable, so
the file's existing declarations did not cover it.DESCRIPTION Authors@R now declares that holder with role = "cph".
It declared no copyright holder at all, and neither did any other package
in the fleet. Nothing in R CMD check reports that.CLAUDE.md now carries a Licensing section, so the year gets checked
rather than silently ageing.?nowcast_evaluate_v1 claimed common random numbers, and the code does not
establish them. Every method starts from the same RNG state on each as-of
week, which pairs the comparison. Common random numbers would also need the
methods to consume compatible variates, and nothing enforces that. The
documentation now states the mechanism and stops there.
vignette("pipeline") already said this. The roxygen contradicted it in
three places, including a worked example.Every prose file in the repository is swept to ASD-STE100 (Simplified
Technical English). The sweep covers the roxygen blocks in R/, both
vignettes, README.md, index.md and this file. The sweep changed no claim,
number, hedge or scope qualifier, and no executable line. The one claim that
did change is under Corrections above.
Sentences over 25 words: 34 to 0 in roxygen, 80 to 0 in the vignettes.
Counted per authored unit, outside fenced code and outside code-bearing
roxygen tags. README.md went 3 to 0, index.md went 1 to 0, and this file
went 45 to 0. Long sentences were split, not shortened, so every condition
that made one true survives in the sentences that replaced it.
SHOULD and SHOULD NOT are now capitalised in the five places that state an
obligation. Three are in R/ and two are in vignette("pipeline"). A
lowercase "should" reads as advice and gets treated as optional.
The deprecation notes are unchanged in substance. They are on
?short_term_trend and ?signal_detection_hlm. Both still say the
replacement is not a drop-in, that migration is a rewrite of the call site,
and that the numbers will not match.
One roxygen section gained a bulleted list, where three parallel conditions
were buried in a single sentence. It is the "What it does NOT check" section
on ?validate_ensemble. The three conditions are unchanged.
31 @param descriptions gained the terminal full stop they were missing. No
wording changed. Without it a sentence counter runs straight through the
boundary between one field and the next, and reports one very long sentence.
New vignette("csalert"), the get-started page. It is an orientation and
runs no analysis. It covers what the package is for, which of the two
generations is current, where the csfmt_rts_data_* classes live, and what
cannot be done yet. pkgdown promotes a vignette named after the package to "Get started" in
the navbar, which is where the site's primary call to action now points.
vignette("nowcasting") is now vignette("pipeline"), retitled to "The
pipeline: from incomplete counts to published numbers". Its content, numbers
and statistical claims are unchanged; the opening paragraphs were rewritten in
plainer language. The 23 @seealso cross-references in R/ were updated with
it, so no help page points at a vignette that no longer exists.
vignette("short_term_trend") is deleted. It taught
short_term_trend.csfmt_rts_data_v1, which is deprecated, so it was teaching
new readers the route they should not take. The only worked example of that
method is now the one on ?short_term_trend. Its county-map demonstration was
not ported.
qc_week_over_week_v1() silently dropped every HLM alert transition.
signal_detection_hlm() writes naming-grammar role "hlmstatus", but the
function selected only role == "status" for its $signal table. An
escalation from normal to high therefore never appeared in the week-over-week
review.
Demonstrated on a 380-week series: a real transition from 1 to 2 at week
2025-14 returned 0 rows before the fix and 1 after. HLM needs roughly five
years of history before it produces a status at all, which is why a shorter
test series shows nothing.$integrity excluded
role != "status", so HLM status CODES were diffed as if they were continuous
medians. An ordinal 1-to-2 step read as a numeric revision.status_roles argument, which defaults to
c("status", "hlmstatus"). The roles excluded from $integrity are exactly
the roles whose transitions appear in $signal. Pass a different vector to
narrow or widen it.qc_week_over_week_v1() operates on collapsed
ensemble output. Its only callers are this package's tests and the
ensemble-first pipelines. Nothing on the pre-ensemble csfmt_rts_data_v1 route
reaches it.short_term_trend() on an ensemble broke the data.table's self-reference,
so the NEXT stage warned. It added its P(increasing) column with base
[[<-, which copies the table. Any later stage assigning with := then
emitted data.table's ten-line "shallow copy was taken" advisory. Bisected:
rate -> hlm is clean, rate -> trend -> hlm warns. That is the canonical
order, so every production pipeline running a trend before another ensemble
stage saw it on every run. Now uses data.table::set(). Proven causally red:
reverting the one line reproduces the warning.vignette("nowcasting") now runs the whole canonical chain on one
synthetic triangle with a denominator. The stages are nowcast, replay
validation, reporting completion, rate, short-term trend, MEM intensity, HLM
signal detection, and the terminal collapse. It opens by stating the rule the
architecture turns on. Every analytical stage takes a csfmt_ensemble_v3 and
returns one, and ens_collapse() is terminal. A collapsed table is therefore
output and never input.
ens_add_rate(), mem_thresholds_v1() and signal_detection_hlm() were
previously named in prose but never run; production depends on all three.short_term_trend.csfmt_rts_data_v1() and
signal_detection_hlm.csfmt_rts_data_v1() are deprecated. They are the
pre-ensemble architecture. Both still work and NEITHER WARNS AT RUN TIME, so
existing pipelines are undisturbed; the mark is a signpost for new work.numerator,
denominator, prX and the naming-prefix arguments, and return a status
label. The ensemble methods take one measure and return a per-draw slope,
growth rate and P(increasing), with no classification. Migrating is a rewrite
of the call site and the numbers will not match.short_term_trend() and signal_detection_hlm() now have
csfmt_rts_data_v3 methods that only stop(), naming the architecture
instead of failing with a bare UseMethod error. A collapsed table carries
quantiles, not draws, so a per-draw trend cannot be recovered from one.reporting_completion_v1(triangle, max_delay = 1) returned nonsense,
silently. With a single delay column apply(M, 1, cumsum) returns a vector
rather than a matrix. The transpose then produced a 1-by-n_settled matrix. The
function emitted one pct_delay column PER SETTLED WEEK instead of one in
total, with complete_by_md far below 1. Measured on a 30-week triangle: 30
completion columns and complete_by_md of 0.033, where both should be 1. No
error was raised. Fixed, with a regression test that also pins the column count
to max_delay at horizons 1 through 4.vignette("nowcasting") is now a four-stage end-to-end pipeline on one
seeded synthetic triangle: nowcast, replay validation, reporting completion,
short-term trend. Every chunk executes under R CMD check. The
reporting-completion section answers the question the old text left open: is
the first completion column the reference week or the week after? It walks a
named ISO week day by day. It shows that delay is measured in whole ISO weeks,
so a Monday run and a Friday run bucket every report identically.nowcast_truth()'s description was off by one, in both bounds. It said it
summed "all delays up to max_delay" and kept weeks "at least max_delay
weeks before" the as-of. The code sums delays 0 .. max_delay - 1 and keeps
age >= max_delay - 1. Measured: at max_delay = 3 the newest settled
reference week is 2 weeks before as-of, not 3. Corrected.reporting_completion_v1() renames its completion columns from pct_wN to
pct_delayD, and they are now 0-based and indexed by DELAY. With
max_delay = 3 the columns were pct_w1, pct_w2, pct_w3; they are now
pct_delay0, pct_delay1, pct_delay2.pct_w1 held delay 0. The number in the name never
equalled the delay it represented. Next to a reporting triangle whose
columns are delays 0, 1, 2, it read as "the week after". pct_delay0 says
outright that it is the reference week itself.max_delay argument: the column count equals
max_delay and the highest index is max_delay - 1.reporting_completion_trend_v1() passes
the columns through and changes with it.@param and @returns, but
nothing a reader could run. The new examples all execute under R CMD check.
None is wrapped in \dontrun{} or \donttest{}, because nothing in the package
needs a live database, a credential or a mounted share to demonstrate. The
nowcast pipeline examples share one 40-week synthetic reporting triangle,
right-truncated so the newest weeks are genuinely incomplete.@seealso that says which vignette
covers it, including when the honest answer is "neither". Only eight exports
are actually run inside a vignette code chunk. ens_add_rate(),
mem_thresholds_v1() and
signal_detection_hlm() are named in the closing "Where next" list of the
nowcasting vignette, but not demonstrated there. The @seealso now says so
rather than implying coverage.@family groups added as topical navigation, so the reference pages
cross-link. The groups are ensemble format functions, reporting triangle
functions, naming grammar functions, nowcast engines, nowcast diagnostics,
nowcast calibration functions, ensemble operations, and reporting completion
functions. These group
functions by the concept they belong to, not by a shared call signature. Only
nowcast engines is a set of interchangeable implementations. The other seven
are pipeline stages or helpers around one concept, and each member's @seealso
states its own specific role. The two qc_* functions were deliberately not
made a family. qc_surveillance_data_v1() screens one input feed and returns a
verdict. qc_week_over_week_v1() diffs two finished runs and returns two
tables. They are cross-linked with @seealso instead.compare_results() gained an "Identity columns" section. It finds its value
columns with csfmt_interpret(). An identity column outside the csfmt
structural schema — location rather than location_code — is therefore read
as a value column. cur/prv then come back as character instead of numeric.
qc_week_over_week_v1() then fails outright with
Error in cur - prv : non-numeric argument to binary operator. The nowcasting
vignette teaches exactly those non-schema names, so this is easy to hit.No behaviour changed. Each of these help pages described a model, or a guarantee, that the code does not implement. An adversarial review found them.
nowcast_quasipoisson_v1() is not fitted without an intercept. The
documentation said "no intercept" in two places. But the formula is built as
y ~ d1 + d2 + ..., which carries R's default intercept. An inline comment in
the source already said # + intercept. The documented model now matches the
fitted one.nowcast_evaluate_v1(), which exists to measure it.|truth - median| / halfwidth, not the conformal order statistic. That
symmetric residual is only faithful for intervals roughly symmetric about the
median. There is no finite-sample guarantee. coverage_raw is now documented as
what the engine did on the replayed weeks, not as a property of the engine.complete_by_md is documented as identically 1. It was described as
detecting reporting that continues past the horizon. It cannot:
reporting_triangle_matrix() discards delays at or beyond max_delay before
the total is formed. The final cumulative fraction of that truncated total is
therefore always 1, and pct_delay<max_delay-1> is always 100. To look for a tail, re-run with a
larger max_delay. This is a source defect left unfixed and now documented.q_value() is no longer described as the inverse of q_label(). The label
format holds two integer-percent digits, so q_label(1) gives "q100x0", which
q_value() returns NA for. The round trip holds on [0, 1).csfmt_parse() is no longer described as the inverse of csfmt_var(). It
strips a role from the right against a fixed vocabulary. On the package's own
rate name numerator_nowcasted_vs_denominator_nowcasted_pr100 it eats the
denominator's _nowcasted as the role, and returns denom = "denominator". A
new section documents the limit with that exact name.validate_ensemble() is retitled "Check a csfmt_ensemble_v3's structural
shape". It checks class, two column names, and that each draw matrix has one
row per row of $data — the row COUNT only. Permuting the rows of $data or of
a draw matrix passes, as do a missing key, a broken time_series_internal_id
and a deleted time_series_label. The page previously implied it was a safety
net for hand-edited objects. It now says to rebuild with csfmt_ensemble_v3()
instead, and its example demonstrates a permutation passing.qc_week_over_week_v1() cannot see HLM transitions. signal_detection_hlm()
writes role "hlmstatus" while the check selects role "status", so only
mem_thresholds_v1() output reaches qc$signal. The example said otherwise and
now records the gap.^pkgdown$ and ^Rplots\.pdf$ added to .Rbuildignore. pkgdown::build_site()
leaves a pkgdown/favicon/ directory behind, and an example that draws a plot
leaves an Rplots.pdf. Both otherwise ship in the tarball and raise a
non-standard-top-level-file NOTE.short_term_trend() can now propagate the slope's own sampling error.
The growth rate is 100 * beta1 / Y. Until now only the uncertainty of
the level Y reached it, and the OLS standard error of beta1 was computed
and discarded. So every trend interval reflected nowcast uncertainty alone. A
passthrough (single-draw) series got a zero-width interval and a
P(increasing) of exactly 0 or 1. That is a bare sign test on a three-point
slope presented as certainty.
propagate_slope_error = TRUE adds se * t_(width-2) per draw, widening the
trend's own draw axis to n_sim when the incoming ensemble is degenerate.
Defaults to FALSE, so published numbers are unchanged until it is switched
on deliberately. Note the degrees of freedom are trend_isoyearweeks - 2: at
the default window of 3 that is a Cauchy, so widen the window first.nowcast_estimate_calibration_v1() / nowcast_apply_calibration_v1() are
back, after being removed earlier in this release cycle. They are available
to check an engine with, not applied to published numbers. The estimator
reports a per-horizon factor. So "your 90% interval would need to be 1.4x
wider to actually cover 90%" is readable straight off the backtest. That is a
far more actionable red flag than a bare coverage fraction, and it keeps the
published number the model's own.nowcast_quasipoisson_v1. The old assertions
(factor > 1 everywhere, raw coverage below 0.82) encoded the removed
nowcast_survrtrunc_v1's behaviour. The current engine covers about 0.87 on
the same drifting-delay synthetic, close enough to nominal that no widening is
warranted.prediction_interval.glm is now registered with S3method(). It resolved
when the package was installed, but not under devtools::load_all(). The
method's own test therefore failed in a dev tree while passing in
R CMD check.reporting_completion_trend_v1 returns the completion curve by calendar year
(all) + by month (last N, per series), with a scope column. That is the
year/month trend the luftveis pipeline used to assemble by hand.nowcast_evaluate_v1 is now the single entry point for scoring nowcasts.
Give it a triangle and one method, or a NAMED list. It replays each --
paired by a shared seed -- and returns one per-horizon table of interval
coverage + point-estimate revision, with a method column. It subsumes the
former nowcast_score (coverage, via scoringutils), nowcast_revision,
nowcast_compare and nowcast_validate. Coverage is read straight off the
interval quantiles, so scoringutils is no longer a dependency (WIS was
dropped).nowcast_survrtrunc_v1 engine, and its flexsurv
dependency. Also removed the conformal calibration functions
(nowcast_estimate_calibration_v1 / _apply_ / print.nowcast_calibration).
Both were already dropped from the production pipeline.mem_thresholds -> mem_thresholds_v1 (versioned-engine convention).print.csfmt_ensemble_v3 method; added the
missing @returns to the ensemble/nowcast S3 methods (mem_thresholds_v1,
short_term_trend, signal_detection_hlm, nowcast_quasipoisson_v1).@examples to the naming-grammar functions (csfmt_var,
csfmt_parse, q_label, q_value) and to the nowcast analysis functions
(nowcast_evaluate_v1, reporting_completion_trend_v1).% in the nowcast / reporting_completion roxygen that had
been corrupting their generated .Rd, plus copy-paste errors in the
simulation-helper docs.A new draw-parallel ensemble format and the full analysis pipeline built on it (reporting triangle -> nowcast -> trend -> MEM thresholds -> quantile collapse).
csfmt_ensemble_v3: S3 container ($data data.table + per-measure $draws
matrices) with invariants enforced by validate_ensemble.csfmt_var / csfmt_parse (order-independent parse of
trailing coordinates), q_label / q_value, and csfmt_interpret for
self-describing datasets.csfmt_reporting_triangle_v3: reference-by-delay input format with a reshape
that completes the reference axis.nowcast(): reporting triangle -> ensemble via flexsurv survrtrunc + negbin
(no epinowcast dependency). Can nowcast a denominator alongside the numerator
(full % positive -> MEM path) and surface the observed denominator total. The
reporting-before-reference check is NA-safe.observed_ensemble(): passthrough (degenerate single-draw) ensemble for
indicators that should not be nowcasted.collapse(): ensemble -> quantile summary; collapse(heal = TRUE) heals the
result into cstidy csfmt_rts_data_v3.add_rate(): numerator-vs-denominator rate (% positive).short_term_trend.csfmt_ensemble: batched, shared-design-matrix kernel that
also emits P(increasing).mem_thresholds.csfmt_ensemble_v3: MEM intensity thresholds with provisional
seasons. exclude_seasons drops anomalous seasons from the baseline. Training
is capped to the most recent i.seasons before na.omit. A non-zero-season
guard plus a quiet memmodel wrapper prevent sparse indicators from erroring.signal_detection_hlm.csfmt_ensemble_v3: per-draw exceedance detection.add_holiday_effect for the ensemble format.qc_surveillance_data (generic input QC, verdict only) and
qc_week_over_week (A/B revision comparison across runs).add_rate and collapse are renamed to ens_add_rate / ens_collapse.
They are now S3 generics dispatching on csfmt_ensemble_v3 (the ens_
family), so the ensemble class carries the "operates on an ensemble" meaning.
Behaviour is unchanged. (add_holiday_effect is a simulation-data helper on a
plain data.table and keeps its name).nowcast and observed_ensemble are renamed to nowcast_survrtrunc_v1 and
nowcast_passthrough_to_ensemble_v1 -- concrete, VERSIONED nowcast engines that
share the contract f(reporting_triangle, ...) -> csfmt_ensemble_v3. Behaviour
is unchanged; the _vN suffix versions the algorithm (a future nowcast_stan_v1
or nowcast_simple_v2 slots in beside them), selected by a caller-side registry.
The validation harness (nowcast_backtest/score/compare/validate/censor/truth) is
generic tooling and is NOT versioned.nowcast_survrtrunc_v1 gains delay_window (default 26 weeks): the reporting-delay
distribution is estimated from only the most recent weeks, so a non-stationary /
drifting delay (e.g. a backfilled history then live prospective reporting) is
tracked instead of averaged into a stale pooled curve. Fixes the median bias
that caused sub-nominal interval coverage. Residual under-coverage (plug-in delay)
is documented by the calibration test, and awaits per-draw delay uncertainty or
backtest-driven recalibration. NULL restores the old pool-all-history behaviour.test-nowcast-calibration.R): empirical interval coverage vs
nominal on synthetic data, with a stationary case (calibrated) and a
drifting-delay case (reproduces the real-data under-coverage synthetically).nowcast_simple_v1 renamed to nowcast_survrtrunc_v1 (the name now states the
method: right-truncated survival delay + negbin completion).nowcast_quasipoisson_v1: a discriminative (regression) nowcast.
For each horizon, regress the settled TOTAL on the counts reported so far, on
the recent settled weeks. The regression is
total ~ n[delay 0] + n[delay 1] + ..., quasipoisson/identity, no intercept.
Then simulate the incomplete weeks (parameter
uncertainty from the fit + a dispersion-matched negbin). No per-week magnitude
parameter, so it is robust for the recent weeks and honestly dispersed.
Drifting-delay synthetic coverage is ~0.79, vs the plug-in survrtrunc's ~0.72
(nominal 0.90). Base stats only; same f(triangle) -> ensemble contract ->
drops into the registry as a candidate key.nowcast_estimate_calibration_v1 learns a
per-group (default horizon) conformal interval-scaling correction from past
nowcasts vs settled truth. nowcast_apply_calibration_v1 applies it, so a
method's intervals hit nominal coverage regardless of internal misspecification.
A nowcast_calibration S3 object with a print method sits between. Turns the
backtest into calibration data: engine -> backtest -> estimate -> apply ->
honest intervals. Distribution-free (split conformal); estimate on past
backtests, apply to the current nowcast.nowcast_censor
reconstructs what was known as-of a past week from the reporting triangle.
nowcast_truth gives settled totals. nowcast_backtest replays any
f(triangle) -> ensemble across as-of weeks into tidy quantile nowcasts.
nowcast_evaluate_v1 scores one or several methods on interval coverage +
point-estimate revision by horizon -- see the Simplification section above.reporting_completion: the empirical reporting-delay summary of a triangle.
From the settled weeks it gives the mean delay, and the weeks-observed to reach
25/50/75/90/95% of a reference week's cases. It also gives the fraction
actually in by
max_delay. period = "year" / "month" stratifies the settled weeks in time,
by the ISO year / midweek-day month. A drift in reporting speed then shows up as
a trend in mean delay, instead of being averaged away.