Deterministic, group-safe utilities that turn daily environmental exposure series — pollen and spore counts, and other time-varying exposures such as ozone or particulate matter — into analysis-ready lagged and moving-average features for environmental epidemiology.
Building a lagged exposure looks like a one-liner. It isn’t, because daily surveillance data has two different defects that are easy to conflate:
NA where a sample failed;is.na() cannot see a gap. And a lag computed by row
position on a gapped series is a lag by position, not by
time: a “1-day lag” silently becomes however many days happen
to separate two adjacent rows. The resulting exposure column looks
plausible, runs cleanly through a regression, and yields a misaligned
lag–response estimate that nothing downstream will flag.
SporeLag makes that failure impossible: the temporal functions refuse to operate on a gapped grid.
# install.packages("remotes")
remotes::install_github("friveramariani/SporeLag")library(SporeLag)
model_ready <- pollen_demo |>
complete_daily_grid(date = "date", by = "site") |> # close gaps in TIME
assign_iso_week(date = "date") |> # iso_year + iso_week
assign_season(date = "date") |> # season label
impute_weekly_mean(value = "count", by = "site") |> # fill NAs, flag them
build_moving_average(value = "count_imputed", window = c(3, 7),
date = "date", by = "site") |>
apply_lag(value = "count_imputed", lags = 0:3,
date = "date", by = "site")
names(model_ready)
#> [1] "site" "date" "count"
#> [4] "iso_week" "iso_year" "season"
#> [7] "count_imputed" "count_imputed_flag" "count_imputed_ma3"
#> [10] "count_imputed_ma7" "count_imputed_lag0" "count_imputed_lag1"
#> [13] "count_imputed_lag2" "count_imputed_lag3"complete_daily_grid() must come first, or the last two
steps error. The pipeline enforces its own correctness.
| Function | Purpose | Appends |
|---|---|---|
complete_daily_grid() |
insert rows for absent calendar days | rows, not columns |
assign_iso_week() |
ISO 8601 week and year | iso_week, iso_year |
assign_season() |
configurable season label | season |
impute_weekly_mean() |
fill NAs with the ISO-week mean |
{value}_imputed, {value}_imputed_flag |
build_moving_average() |
trailing windowed mean | {value}_ma{k} |
apply_lag() |
time-indexed lags | {value}_lag{n} |
by is a character vector
of column names. Values never bleed across a group boundary.NA
group keys, and negative lags all raise classed errors rather than
producing plausible-looking wrong answers.rlang and
cli. That’s it.SporeLag builds exposure variables. It does not fit models, merge outcomes, or define aeroallergen or pollutant seasons for you — those are analytic decisions that belong to you, not to a preprocessing package.
SporeLag is developed in collaboration with Dr. Benjamín Bolaños-Rosero, director of the San Juan and Caguas, Puerto Rico, aeroallergen monitoring stations — both certified by the National Allergy Bureau (NAB). His expertise in aeroallergen surveillance and access to longitudinal Puerto Rico pollen and spore count data have directly informed the package’s design priorities: complete-grid enforcement, group-safe operations, and explicit, auditable exposure-feature construction.
vignette("getting-started", package = "SporeLag") walks
through the pipeline and, more importantly, through the ways it can go
wrong.