Comparing indicators across multiple survey rounds is essential for
longitudinal policy evaluation. However, structural differences across
the survey instruments pose a major barrier. This vignette walks through
how ihsMW streamlines cross-round harmonisation and
analysis.
Over the years, the Malawi Integrated Household Survey (IHS) questionnaires have evolved. Variables are added, retired, renamed, or relocated to different modules.
For instance, the household size indicator is named
hhsize in some files, hh_size in others, or is
represented by variables counting household members. Similarly, nominal
consumption expenditure variables and agricultural crop names frequently
change, making direct cross-round comparisons error-prone and
tedious.
To resolve this, ihsMW bundles a static crosswalk
database containing mappings for over 6,400 variables across IHS2, IHS3,
IHS4, IHS5, and IHS6. It acts as a translation layer, mapping
round-specific variable names to consistent, harmonised names.
You can inspect the crosswalk programmatically:
Use ihs_harmonise() to standardise column names in raw
dataframes:
To compile a cross-round dataset for longitudinal analysis, load the data from each round, harmonise them separately, and bind them together:
library(dplyr)
# Load and harmonise IHS4
ihs4_raw <- read_dta("path/to/IHS4/hh_mod_a_filt.dta")
ihs4_harm <- ihs_harmonise(ihs4_raw, round = "IHS4")
# Load and harmonise IHS5
ihs5_raw <- read_dta("path/to/IHS5/hh_mod_a_filt.dta")
ihs5_harm <- ihs_harmonise(ihs5_raw, round = "IHS5")
# Load and harmonise IHS6
ihs6_raw <- read_dta("path/to/IHS6/hh_mod_a_filt.dta")
ihs6_harm <- ihs_harmonise(ihs6_raw, round = "IHS6")
# Bind rows - ihs_harmonise adds an `ihs_round` column automatically
pooled_data <- bind_rows(ihs4_harm, ihs5_harm, ihs6_harm)Before pooling, check how many of your variables actually survive across every round you intend to use. Coverage is far from universal:
library(ihsMW)
cw <- ihs_crosswalk_check(verbose = FALSE)
table(cw$n_rounds_avail)
#>
#> 1 2 3 4 5
#> 3293 664 1165 1311 49Only a small core of variables appears in all five rounds. Everything else needs a narrower round window or a concept-level judgement call from you.
IHS6 (2024/25) is largely continuous with IHS5, but three differences matter:
stratum to strata.
ihs_svydesign() and ihs_panel_ids() both
handle this, but hard-coded stratum references in your own
scripts will break;adulteq (adult equivalents) is not
present in the IHS6 consumption aggregate, so
per-adult-equivalent measures need to be constructed yourself;ihs_deflate() to
put every round on a common basis before comparing.Some IHS6 crop and unit codes are also new and have no published NSO
conversion factor yet; ihs_convert_units() returns
NA for those rows and names the combinations it could not
map.
Within a single survey round, information is split across multiple
modules (e.g., household demographics, agriculture, food consumption).
Use ihs_merge() to merge these dataframes:
# Load household demographics and crop harvest modules
hh_demog <- read_dta("path/to/IHS5/hh_mod_a_filt.dta") |> ihs_harmonise("IHS5")
hh_agri <- read_dta("path/to/IHS5/ag_mod_i.dta") |> ihs_harmonise("IHS5")
# Merge modules - automatically detects common ID columns (e.g., case_id)
merged_data <- ihs_merge(hh_demog, hh_agri)ihs_merge() checks if the join type results in
unexpected row expansion and issues a warning if many-to-many joins
occur.
When comparing monetary values (e.g., household consumption, crop
sales) across different years, nominal values must be deflated to
account for inflation. ihs_deflate() uses bundled Malawi
Consumer Price Index (CPI) data to convert nominal values to real
values, with 2019 (IHS5 baseline) as the default reference year:
To verify that the crosswalk mappings are valid and check how many
variables are successfully mapped, use
ihs_crosswalk_check():
Additionally, ihs_panel_ids() returns the standard ID
columns (e.g. household ID, individual ID, enumeration area ID, strata,
weights) for a given round to help you construct panel keys or verify
design structures: