Standardises one raw POLIS environmental-samples table and derives the sample-level analytic variables ES surveillance relies on:
canonical snake_case names (via the crosswalk + janitor);
every collection/laboratory date parsed to
Dateand sanitised with the same "sensible date" rule clean_afp() uses – a value before the dawn of surveillance (min_year) or in the future is a data-entry error and is set toNA, never dropped (audit timestamps such aslast_update_datestay ISO strings for the keep-latest dedup);year_collection/month_collectionfrom the sanitisedcollection_date;the AFP-style virus classification (via
clean_es_classification()): a normalisedvirus_typelist,vtypeand the fusedclassification_alllabel in the sameWPV/cVDPV/aVDPV/iVDPVvocabulary the AFP cleaner emits (so onegrepl("WPV|cVDPV", classification_all)works across both streams), the per-serotype Sabin flagssabin1/sabin2/sabin3, thenpevandnvaccine(nOPV2) flags and the fusedev_detect"anything detected" flag;the same geography cleaning as
clean_afp(): normalised admin names, canonicalised admin GUIDs (braces stripped, lower-cased, so they are join-ready with the spatial layer), a title-casedsitelabel, and – when ashapeis supplied – admin-GUID reconciliation and coordinate-based admin recovery (all keyed onyear_collection);country-keyed enrichment from
polis_country_lookup()–country_actual,risk_group,epi_zones/epi_zones_v2– and thepolio_typeserotype (the ES counterpart of the AFP enrichment; the case-classification AFP flags do not apply to environmental samples);one row per POLIS
id(latest bylast_update_date).
The raw POLIS virus_types, vdpv_classifications, the per-serotype
vaccine*/vdpv*/wild* fields and sample_condition are kept as-is
alongside the derived columns. The business key sample_id + adm0 (the ES
analogue of the AFP epid + adm0 key, sample_id being the EPID-equivalent
sample identifier) is asserted as a tripwire: violations are flagged to QA,
never dropped. (Unlike AFP, a sample can legitimately yield several virus
detections, so this key is not collapsed.)
Usage
clean_es(
data,
cfg = polis_active_config(),
shape = NULL,
impute_geo = TRUE,
sites = NULL,
verbose = TRUE
)Arguments
- data
A raw POLIS environmental-samples 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$qato route ambiguity flags.- shape
Optional district shape used to reconcile admin names/GUIDs via
reconcile_admin_guids()(keyed onyear_collection), exactly asclean_afp()uses it. Either a long ADM2 attribute table (spatial_adm2_long_shape) or the polygon layer (spatial_global_adm2). A polygon is expanded to its long form here to drive the GUID reconcile and also drives coordinate-based admin recovery: samples still missingadm1/adm2(or their GUIDs) but carrying site coordinates have their admin recovered by a point-in-polygon join viaimpute_geo_from_coords()(the ES counterpart of AFP EPID-prefix recovery; ES samples carry no geocoded EPID). DefaultNULL(no shape-based recovery).- impute_geo
If
TRUE(default) samples still missingadm2_guidafter any shape-based recovery have their admin chain borrowed from other samples at the same site via the self-reference fill (see details); only sites that map unambiguously to one district are used, so conflicting sites are left flagged rather than guessed. Needs no shape, so it runs standalone. Adds ageo_sourceof"site_match"to filled rows.- sites
Optional reference list of known environmental site names (a data frame with a
site_namecolumn, or a character vector). When supplied, sites absent from it are flagged viavalidate_es_sites(). DefaultNULL(no site validation).- verbose
Emit cli progress messages for each phase. Default
TRUE.
Value
A tibble of cleaned ES records, one row per POLIS id, with columns
ordered identically to clean_afp() (id -> location -> time ->
classification -> dates -> other). The derived columns (year_collection,
month_collection, vtype, classification_all, sabin1/sabin2/
sabin3, npev, nvaccine, ev_detect) 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),
EnviroSampleId = c("E1", "E1", "E2"),
LastUpdateDate = c("2024-01-01", "2024-03-01", "2024-02-01"),
CollectionDate = c("2024-01-05", "2024-01-05", "2024-02-09"),
VirusTypes = c("cVDPV2", "cVDPV2", NA),
VACCINE1 = c("No", "No", "Yes"),
IsNPEV = c("No", "No", "Yes"),
Admin0Name = c("NIGERIA", "NIGERIA", "CHAD"),
check.names = FALSE
)
clean_es(raw)
#> ℹ Standardising names on 3 rows
#> ✔ Standardised names on 3 rows [21ms]
#>
#> ℹ Parsing dates and deriving year/month of collection
#> ✔ Parsed dates and derived year/month of collection [19ms]
#>
#> ℹ Deriving virus-detection flags
#> ✔ Derived virus-detection flags [20ms]
#>
#> ℹ Standardising admin names
#> ✔ Standardised admin names [14ms]
#>
#> ℹ Recovering missing admin from same-site samples
#> ✔ Recovered admin for 0 samples from same-site records [13ms]
#>
#> ℹ Enriching with country groupings
#> ✔ Enriched with country groupings [14ms]
#>
#> ℹ Deduplicating by id and finalising
#> ✔ Deduplicated by id and finalised [27ms]
#>
#> ✔ Cleaned 2 ES samples.
#> # A tibble: 2 × 20
#> id adm0 collection_date month_collection year_collection virus_types
#> <dbl> <chr> <date> <dbl> <dbl> <chr>
#> 1 1 NIGERIA 2024-01-05 1 2024 cVDPV2
#> 2 2 CHAD 2024-02-09 2 2024 NA
#> # ℹ 14 more variables: virus_type <chr>, vtype <chr>, sabin1 <int>,
#> # sabin2 <int>, sabin3 <int>, npev <int>, nvaccine <int>,
#> # classification_all <chr>, ev_detect <int>, polio_type <chr>,
#> # last_update_date <date>, enviro_sample_id <chr>, vaccine1 <chr>,
#> # is_npev <chr>
