csalert makes alerts from weekly public-health surveillance counts that arrive late.
It starts from a reporting triangle: the counts, and the date each count was reported. A nowcast engine completes the weeks still being reported, as Monte-Carlo draws. Rate, trend, MEM intensity and baseline exceedance run on those draws, so the reporting uncertainty reaches the alert.
The table methods of short_term_trend() and signal_detection_hlm(), after
Benedetti (2019),
doi:10.5588/pha.19.0002, are for counts
without delayed reporting. They take different arguments from the ensemble
methods, and give different numbers.
Install from the niphr r-universe, which builds every commit on main:
options(repos = c(
niphr = "https://niphr.r-universe.dev",
CRAN = "https://cloud.r-project.org"
))
install.packages("csalert")
Put those repos in your ~/.Rprofile. Without them, R installs 2024.6.24
from CRAN, years behind, and nothing warns you.
With the repos set, pak works too:
pak::pak("csalert") # the r-universe build
pak::pak("niphr/csalert") # straight from GitHub, ignores the repo list
pak rejects a version range such as csalert@>=2026.8.27. Pin an exact
version, csalert@2026.8.27, or declare Imports: csalert (>= 2026.8.27) in a
DESCRIPTION and run pak::local_install_deps().
library(csalert)
library(data.table)
# counts, and the date each count was reported on
mondays <- as.Date("2023-01-02") + 7 * (0:51)
monday <- rep(mondays, each = 3)
d <- data.table(
isoyearweek_reference = format(monday, "%G-%V"),
reporting_date = monday + rep(c(3, 10, 17), 52),
numerator = rpois(156, c(40, 20, 8)),
indicator_tag = "hospitalisation", location_code = "nation",
age = "total", sex = "total"
)
d <- d[reporting_date <= mondays[52] + 6]
tri <- csfmt_reporting_triangle_v3(
d, id_cols = c("indicator_tag", "location_code", "age", "sex")
)
# complete the weeks still arriving, then the trend on the draws
ens <- nowcast_delay_ecdf_v1(tri, max_delay_days = 21, n_sim = 500)
ens <- short_term_trend(ens, measure = "numerator_nowcasted", trend_isoyearweeks = 5)
# one tidy table at the end
res <- ens_collapse(ens, heal = TRUE)
res[, .(isoyearweek, numerator_nowcasted_q50x0,
numerator_nowcasted_trend_increasing_pr)]
| Goal | Function |
|---|---|
| Build the input triangle | csfmt_reporting_triangle_v3() |
| Complete the weeks still being reported | nowcast_delay_ecdf_v1() |
| Pass an indicator through with no nowcast | nowcast_passthrough_to_ensemble_v1() |
| Score a nowcast on replayed weeks |
nowcast_backtest(), then nowcast_score_v1()
|
| Measure how fast cases arrive | reporting_completion_v1() |
| Add a rate | ens_add_rate() |
| Estimate recent direction, per draw | short_term_trend() |
| Classify intensity by MEM thresholds | mem_thresholds_v1() |
| Flag counts above a historical baseline | signal_detection_hlm() |
| Turn the draws into quantile columns | ens_collapse() |
| Check an input feed | qc_surveillance_data_v1() |
| Compare this run with the previous run |
qc_week_over_week_v1(), compare_results()
|
| Simulate counts with known outbreaks | simulate_baseline_data() |
short_term_trend() and signal_detection_hlm() run the ensemble method on a
csfmt_ensemble_v3, and the table method on a csfmt_rts_data_v1.
Read https://niphr.github.io/csalert/. Start with Get started, then The pipeline, which runs one series through every stage.