
Test whether the reporting-delay distribution drifts over time
diagnose_drift.RdRuns an autocorrelation-robust monotonic-trend test on the per-period, count-weighted delay summaries, to answer "do delays drift over time?" in a way that respects the fact that a delay series is correlated with itself.
Arguments
- x
A
tbl_nowobject.- ...
Passed to the underlying modifiedmk function (e.g.
nsimfor"block-bootstrap").- stat
Which delay summaries to test: any of
"median","mean","iqr"(q75 - q25) and"spread"(q90 - q10). Defaults to median + spread, i.e. one location and one dispersion statistic.- method
Trend test:
"hamed-rao"(default; Hamed-Rao variance correction,modifiedmk::mmkh()),"yue-pilon"(Yue-Pilon,modifiedmk::mmky()) or"block-bootstrap"(block-bootstrap MK,modifiedmk::bbsmk()). See the Choosing a method section.- by_strata
Logical (default
FALSE). WhenTRUE, the test is run separately per stratum.- strata
Character vector of columns to group on when
by_strata = TRUE.NULL(default) uses the object'sstrata.- mature_only
Logical (default
TRUE). Drop event dates after thelevelincompleteness cutoff before testing.- level
Completeness level for the maturity cutoff (default
0.95).- alpha
Significance level for the
driftverdict column (default0.05).- axis
Which time axis the delay is measured to:
"report"(default) or"validation". Both are measured from the event, so the two are directly comparable – run each in turn and the gap between them is the time the laboratory adds. (This is not the same quantity as the.validation_delaycolumn, which is the laboratory's own turnaround, measured from the report.) Needs a validation process (seeadd_validation_date()); cases still"pending"are left out.
Value
A tibble with one row per requested stat per
stratum, and the following columns:
stratacharacter. The stratum the row refers to. Whenby_strata = FALSE(the default) there is a single stratum labelled"all"; otherwise one level per observed combination ofstrata.statcharacter. Which delay summary was tested — one of"median","mean","iqr"or"spread"."median"/"mean"are location statistics (are delays getting longer?);"iqr"/"spread"are dispersion statistics (are delays getting more erratic?).ninteger. Length of the tested series, i.e. the number of event dates contributing a non-missing value after themature_onlyfilter. This is a count of periods, not a count of cases. Series withn < 10(or with zero variance) are not tested and returnNAfor every test column.taunumericin[-1, 1]. Kendall's rank correlation between the statistic and time — the effect size. Positive means delays are growing, negative means they are shrinking. Roughly,|tau|below 0.1 is a negligible trend even whenp_valueis small.sens_slopenumeric. Sen's slope: the median pairwise rate of change, expressed in delay units per period — so for weekly data with delays measured in weeks, "weeks of delay gained per week elapsed". Multiply bynfor the total drift implied across the series. Unlike an OLS slope this is robust to outlying periods.statisticnumeric. The autocorrelation-corrected Mann-KendallZscore. Under the null it is standard normal, so|Z| > 1.96corresponds top_value < 0.05.p_valuenumeric. Two-sided p-value for the null hypothesis of no monotonic trend, after the serial-correlation correction implied bymethod.NAwhen the series was too short or constant.methodcharacter. Themethodactually used, echoed back so the result is self-documenting when several runs are bound together.driftlogical. The verdict:TRUEwhenp_value < alpha.NAp-values giveFALSE, so aFALSEmeans "no drift detected" and not necessarily "no drift".
Details
For each requested stat (and each stratum) it builds the per-event-date
series of that statistic and tests it for a monotonic trend with the
modifiedmk package, which corrects the Mann-Kendall variance for serial
autocorrelation. A plain Mann-Kendall (or an OLS slope) would be
anti-conservative here, because positive autocorrelation shrinks the effective
sample size.
By default the test uses only mature event dates (those on or before the
level incompleteness cutoff), because the recent, not-yet-fully-reported
dates would otherwise inject a spurious downward trend.
Interpreting the result
Read tau and sens_slope before p_value. On long surveillance series a
tiny, operationally irrelevant trend will still be highly significant, so
drift = TRUE on its own is not a reason to act. The question to ask is
whether sens_slope * n — the total drift implied over the observed window —
is large relative to the delays themselves.
The location and dispersion statistics answer different questions and can disagree, which is informative rather than contradictory:
mediandrifting up,spreadflat: reporting is uniformly slower.medianflat,spreaddrifting up: the typical case is unaffected but the tail is getting worse — often a subset of reporting sites degrading.both drifting up: broad deterioration in reporting timeliness.
A detected drift means a nowcasting model fitted on a fixed delay distribution will be biased, because it is averaging over delay regimes that are not exchangeable. Consider a model with a time-varying delay, or fitting only to the recent, homogeneous stretch of data.
Because this is a trend test it will not find an abrupt one-off shift; a
step change can even cancel out to a non-significant monotonic trend. Pair it
with diagnose_changepoint(), which is built for exactly that case.
Choosing a method
All three options are Mann-Kendall tests that correct for the serial correlation of a delay series; they differ in what they assume about that correlation, and in cost.
"hamed-rao"(default)Inflates the Mann-Kendall variance using all significant autocorrelation lags of the detrended ranks. It makes no AR(1) assumption, is deterministic, and is effectively instantaneous, so it is the sensible default for a routine diagnostic. Its variance correction is known to be unstable on short series — treat results with
nbelow roughly 30 as indicative only."yue-pilon"Trend-free pre-whitening, which effectively assumes the series is AR(1). That assumption is a poor fit for daily reporting delays, which carry strong day-of-week periodicity, and pre-whitening is known to remove part of the very trend being tested. Offered for comparability with the hydrology literature; rarely the right choice here.
"block-bootstrap"Resamples contiguous blocks, so it accommodates arbitrary dependence within a block — including weekly periodicity, if the block length covers it. Statistically the most defensible for daily data, and the best cross-check when a
hamed-raoresult is borderline. Two caveats: it is stochastic, so callset.seed()first for a reproducible p-value, and it is thousands of times slower — it scales at roughly the square of the series length, so a multi-year daily series can take many minutes per statistic. Reducensim(passed through...) or restrict to a shorter window before reaching for it.
When a decision matters, run the default first and confirm a borderline
result with method = "block-bootstrap" on a restricted window.
See also
diagnose_changepoint() for an abrupt shift rather than a gradual trend;
plot_delay_drift() to see the series being tested;
censor_reporting_delays_above() once you decide some delays are not to be believed;
diagnose_signposts(), which tells you when this
test is worth running.
Examples
data(denguedat)
dengue <- tbl_now(denguedat,
event_date = "onset_week", report_date = "report_week", verbose = FALSE
)
diagnose_drift(dengue)
#> Warning: ! `diagnose_drift()` is experimental: results are not guaranteed and the
#> interface may change.
#> ℹ Interpret a significant result as a potential trend change, not a confirmed
#> one.
#> This warning is displayed once every 8 hours.
#> # A tibble: 2 × 9
#> strata stat n tau sens_slope statistic p_value method drift
#> <chr> <chr> <int> <dbl> <dbl> <dbl> <dbl> <chr> <lgl>
#> 1 all median 1090 -0.00918 0 -0.243 0.808 hamed-rao FALSE
#> 2 all spread 1090 -0.178 0 -2.32 0.0203 hamed-rao TRUE