Standardises one raw POLIS case table and derives the case-level analytic variables AFP surveillance relies on:
canonical snake_case names (via the crosswalk + janitor) and merged-EPID remapping;
parsed epidemiological/laboratory
*_datecolumns;year_onset/month_onsetfromparalysis_onset_date(falling back to the stool-1 then notification year when onset is missing), and a numericage_months;the standard onset-relative date intervals (
onset_to_notify,onset_to_invest,onset_to_stool1,onset_to_stool2,invest_to_stool1,stool1_to_stool2,notify_to_invest,onset_to_followup), in days;the
stool1_missing/stool2_missing/stool_missingflags;stool
timelinessand the 60-day follow-up flags (needs_60day_followup,got_60day_followup,followup_on_time);the fused analytic classification
classification_all(with its building blocksvtype/vtype_fixed) plus the Sabin flagssabin1/sabin2/sabin3and a recomputedhot_case;country-keyed enrichment from
polis_country_lookup()–country_actual,risk_group,epi_zones/epi_zones_v2– thepolio_typeserotype, and the surveillance AFP flagsafp_class,afp,npafpandpending_results;the harmonised clinical diagnosis (
diagnosis_harmonised,diagnosis_source,diagnosis_class,is_non_afp) coalesced from the four scattered POLIS diagnosis fields viaclean_afp_diagnosis(), plus the 60-dayresidual_paralysisoutcome and thefebrile_asymmetric_onsetpresentation flag;normalised admin names and one row per POLIS
id(latest bylast_update_date).
The raw POLIS classification, polio_virus_types, vdpv_classifications,
adequate_stool and paralysis_hot_case fields are kept as-is alongside the
derived columns. Records sharing the business key
epid + paralysis_onset_date + adm0 (the same case re-entered under a new
POLIS Id) are collapsed to the latest by last_update_date; cases that share
an EPID and country but differ in onset date are treated as distinct and kept.
A tripwire then flags any epid + adm0 still spanning multiple Ids to QA,
never dropping it – so a genuine reclassification or a same-EPID onset
conflict surfaces for review rather than vanishing.
Usage
clean_afp(
data,
cfg = polis_active_config(),
shape = NULL,
impute_geo = TRUE,
verbose = TRUE
)Arguments
- data
A raw POLIS case data frame.
- cfg
A
polis_config()object. Defaults topolis_active_config()– the config most recently built bypolis_config()this session – so a no-cfgcall inherits the active session settings rather than fresh defaults. Supplycfg$synonymsto remap merged EPIDs andcfg$qato route ambiguity flags.- shape
Optional district shape that drives admin recovery. Either form works and a single input does everything:
a polygon layer (
spatial_global_adm2, ansfobject) – its long form is derived here (asprocess_spatial()does) for the GUID/name reconcile viareconcile_admin_guids(), and its geometry drives coordinate recovery viaimpute_geo_from_coords()for cases still missing a district but carrying coordinates;a long ADM2 attribute table (
spatial_adm2_long_shape) – reconcile only, since it has no geometry for the coordinate step.
Reconciliation adds a
geo_sourcecolumn. DefaultNULL(no shape-based recovery).- impute_geo
If
TRUE(default) cases still missingadm1/adm2(and their GUIDs) after reconciliation have them recovered from the EPID prefix viaimpute_geo_from_epid()(self-reference + prefix matching, every row kept). Adds*_sourceprovenance columns.- verbose
Emit cli progress messages for each phase. Default
TRUE.
Value
A tibble of cleaned AFP records, one row per POLIS id (and at most
one per epid + paralysis_onset_date + adm0 business key after the
duplicate collapse), with
columns ordered id -> location -> time -> other. The canonical and derived
columns
(year_onset, month_onset, age_months, the *_to_* intervals,
onset_date_quality, timeliness and the 60-day follow-up flags) are added
only when their prerequisite source columns are present in data, so a
trimmed input yields a correspondingly trimmed output rather than an error.
Examples
raw <- data.frame(
Id = c(1, 1, 2),
Epid = c("A-1", "A-1", "B-2"),
LastUpdateDate = c("2024-01-01", "2024-03-01", "2024-02-01"),
ParalysisOnsetDate = c("2024-01-02", "2024-01-02", "2024-02-03"),
NotificationDate = c("2024-01-05", "2024-01-05", "2024-02-06"),
Admin0Name = c("NIGERIA", "NIGERIA", "CHAD"),
check.names = FALSE
)
clean_afp(raw)
#> ℹ Standardising names on 3 rows
#> ✔ Standardised names on 3 rows [242ms]
#>
#> ℹ Parsing dates and deriving onset/age/intervals/timeliness
#> ✔ Parsed dates and derived onset/age/intervals/timeliness [27ms]
#>
#> ℹ Classifying virus type and case classification
#> ✔ Classified virus type and case classification [20ms]
#>
#> ℹ Harmonising the clinical diagnosis
#> ✔ Harmonised the clinical diagnosis [13ms]
#>
#> ℹ Standardising admin names
#> ✔ Standardised admin names [20ms]
#>
#> ℹ Recovering missing admin from the EPID
#> ✔ Recovered admin for 0 cases from the EPID [13ms]
#>
#> ℹ Enriching with country groupings and AFP flags
#> ✔ Enriched with country groupings and AFP flags [14ms]
#>
#> ℹ Deduplicating by id and finalising
#> ✔ Deduplicated by id and finalised [70ms]
#>
#> ✔ Cleaned 2 AFP cases.
#> # A tibble: 2 × 10
#> id epid adm0 paralysis_onset_date month_onset year_onset
#> <dbl> <chr> <chr> <date> <dbl> <dbl>
#> 1 1 A-1 NIGERIA 2024-01-02 1 2024
#> 2 2 B-2 CHAD 2024-02-03 2 2024
#> # ℹ 4 more variables: notification_date <date>, onset_date_quality <chr>,
#> # onset_to_notify <dbl>, last_update_date <date>
