preening() solvesAge categorisation is one of the most routine and most error-prone
steps in surveillance analysis. The same dataset might need ABS 5-year
bands for a national comparison, ATAGI program bands for a vaccine
effectiveness study, and FluCAN bands for a sentinel surveillance report
— all in the same week. preening() provides a single
function backed by a catalogue of ~50 named, citable schemes so that
band choice is explicit, reproducible, and traceable to a published
source.
The name comes from the way a bird re-sorts its feathers into whichever functional arrangement suits the moment, without changing anything about the bird itself. The same raw age values are re-sorted into whichever standard grouping the analysis calls for.
The clearest approach — name the scheme directly.
set.seed(1)
df <- data.frame(age = c(0.2, 3, 14, 25, 50, 67, 80, 92))
preening(df, age_col = "age", scheme = "atagi_covid19_2025")$age_group
#> [1] 0-<5 0-<5 5-<18 18-64 18-64 65-74 75+ 75+
#> Levels: 0-<5 < 5-<18 < 18-64 < 65-74 < 75+
Supply family, focus, and/or
max_bands — if exactly one scheme matches, it is applied
automatically and a message names it so the choice is never silent.
# vaccination + paediatric + max 3 bands → exactly one match
preening(df, age_col = "age", family = "vaccination",
focus = "paediatric", max_bands = 3)$age_group
#> [1] 0-<6m 2y+ 2y+ 2y+ 2y+ 2y+ 2y+ 2y+
#> Levels: 0-<6m < 6m-<2y < 2y+
list_age_schemes() guide youBrowse the catalogue before committing to a scheme.
list_age_schemes(family = "surveillance")
#> scheme family focus
#> ed_syndromic surveillance surveillance|broad
#> flucan_sentinel surveillance surveillance|broad|national_au
#> hospital_admitted_patient surveillance surveillance
#> nndss_decadal surveillance surveillance|national_au
#> nndss_standard surveillance surveillance|fine_grained|national_au
#> nors_outbreak surveillance surveillance|broad
#> notifiable_std_bbv surveillance surveillance|fine_grained
#> racf_aged_care surveillance surveillance|aged_care
#> n_bands age_range
#> 6 0+
#> 5 0+
#> 6 0+
#> 9 0+
#> 10 0+
#> 5 0+
#> 6 0+
#> 5 0+
When a filter matches multiple schemes, preening() stops
and lists them so you can pick one explicitly. This is intentional —
preening() never guesses among ties.
# Multiple paediatric schemes exist — preening() asks you to choose
preening(df, age_col = "age", focus = "paediatric")
#> Error:
#> ! (*)> mudnester::preening() — 10 age schemes match the supplied filters (family = NULL, focus = c("paediatric"), max_bands = NULL):
#> - unicef_child_bands
#> - atagi_nip_schedule
#> - pneumococcal_program
#> - rsv_maternal_infant
#> - neonatal_early
#> - paediatric_developmental
#> - school_entry_bands
#> - who_paediatric_growth
#> - influenza_research
#> - rsv_research
#> Supply `scheme` explicitly to choose one, or narrow your filters further.
Schemes are organised into six families. Use family = to
restrict your search.
| Family | family = value |
Count | Examples |
|---|---|---|---|
| National statistical standards | "national_stats" |
10 | abs_5yr, abs_broad_lifecourse |
| International statistical standards | "international_stats" |
7 | who_life_course, eurostat_5yr |
| Vaccination/immunisation guidance | "vaccination" |
10 | atagi_covid19_2025, flucan_sentinel |
| Surveillance-system conventions | "surveillance" |
8 | nndss_standard, racf_aged_care |
| Clinical/developmental staging | "clinical_developmental" |
8 | geriatric_fine,
paediatric_developmental |
| Disease/research-specific | "disease_specific" |
7 | rsv_research, covid19_severity_strata |
age_unit = "days"Schemes in the neonatal family use days rather than years. Pass
age_unit = "days" and ensure the age column is in days.
neonates <- data.frame(age_days = c(0, 0.5, 2, 5, 15, 30))
preening(neonates, age_col = "age_days", scheme = "neonatal_early",
age_unit = "days")$age_group
#> [1] 0-<24h 0-<24h 24-<72h 72h-<7d 7-<28d 28d+
#> Levels: 0-<24h < 24-<72h < 72h-<7d < 7-<28d < 28d+
When no standard scheme fits, supply your own breaks and labels.
preening(
df,
age_col = "age",
scheme = "custom",
age_breaks = c(0, 18, 40, 65, Inf),
age_labels = c("0-17", "18-39", "40-64", "65+")
)$age_group
#> [1] 0-17 0-17 0-17 18-39 40-64 65+ 65+ 65+
#> Levels: 0-17 < 18-39 < 40-64 < 65+
Every scheme in the mudnester library spans 0 to
Inf, so preening() never returns
NA purely because a record fell outside a scheme’s
“intended” range. Bands marked with ⁺ in the documentation
(e.g. the 0-<60 floor band in
rsv_older_adult) are catch-alls — a meaningful count in one
of these bands is a signal to review the scheme choice, not a finding to
report.
# rsv_older_adult is scoped to 60+. A child record still gets a band.
data.frame(age = c(3, 65, 80)) |>
preening(age_col = "age", scheme = "rsv_older_adult")
#> age age_group
#> 1 3 0-<60
#> 2 65 60-74
#> 3 80 75+
preening() is typically called before
roost() to enable age-stratified counts:
df |>
preening(age_col = "age", scheme = "flucan_sentinel") |>
roost(date_col = "onset_date", time_unit = "month",
group_cols = "age_group")
See vignette("roost") for aggregation options, and
vignette("age-schemes") for the full scheme catalogue with
source citations.