Demographic Analysis in R using DHS Model Datasets

Author

Zahid Asghar

Published

November 19, 2025

🧭 Session 1: Introduction to DHS Data and R Setup

Objectives

  • Understand DHS data structure and variable naming
  • Load and explore a DHS model dataset
  • Prepare your R environment for analysis
Code
# Efficient package management using pacman (no pollster dependency)
if(!require(pacman)) install.packages("pacman")

pacman::p_load(
  tidyverse, haven, here, labelled, survey, broom, corrplot,
  naniar, gt, expss, xlsx
)

topline <- function(data, var, wt){
  data %>%
    dplyr::filter(!is.na({{ var }}), !is.na({{ wt }})) %>%
    dplyr::count({{ var }}, wt = {{ wt }}, name = "weight") %>%
    dplyr::mutate(weighted_pct = round(100 * weight / sum(weight), 2)) %>%
    dplyr::rename(category = {{ var }}) %>%
    dplyr::select(category, weighted_pct)
}

Step 2. Load the Model Dataset

Code
# Load the Women's (IR) file
pkir <- read_dta(here("data", "PKIR71FL.dta"))

# Save a copy for faster loading in future sessions
save(pkir, file = here::here("data/pkir.RData"))

# Reload saved version
load(here::here("data/pkir.RData"))

# Explore structure and variables
str(pkir)
tibble [15,068 × 5,331] (S3: tbl_df/tbl/data.frame)
 $ caseid   : chr [1:15068] "       1   1  2" "       1   4  1" "       1   5  2" "       1   6  2" ...
  ..- attr(*, "label")= chr "case identification"
  ..- attr(*, "format.stata")= chr "%15s"
 $ v000     : chr [1:15068] "PK7" "PK7" "PK7" "PK7" ...
  ..- attr(*, "label")= chr "country code and phase"
  ..- attr(*, "format.stata")= chr "%3s"
 $ v001     : num [1:15068] 1 1 1 1 1 1 1 1 1 1 ...
  ..- attr(*, "label")= chr "cluster number"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v002     : num [1:15068] 1 4 5 6 7 8 8 9 10 10 ...
  ..- attr(*, "label")= chr "household number"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v003     : num [1:15068] 2 1 2 2 2 4 7 4 2 7 ...
  ..- attr(*, "label")= chr "respondent's line number"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v004     : num [1:15068] 1 1 1 1 1 1 1 1 1 1 ...
  ..- attr(*, "label")= chr "ultimate area unit"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v005     : num [1:15068] 868683 868683 868683 868683 868683 ...
  ..- attr(*, "label")= chr "women's individual sample weight (6 decimals)"
  ..- attr(*, "format.stata")= chr "%12.0g"
 $ v006     : num [1:15068] 2 2 2 2 2 2 2 2 2 2 ...
  ..- attr(*, "label")= chr "month of interview"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v007     : num [1:15068] 2018 2018 2018 2018 2018 ...
  ..- attr(*, "label")= chr "year of interview"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v008     : num [1:15068] 1418 1418 1418 1418 1418 ...
  ..- attr(*, "label")= chr "date of interview (cmc)"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v008a    : num [1:15068] 43135 43134 43135 43133 43133 ...
  ..- attr(*, "label")= chr "date of interview century day code (cdc)"
  ..- attr(*, "format.stata")= chr "%12.0g"
 $ v009     : num [1:15068] 12 6 10 5 1 4 10 1 4 2 ...
  ..- attr(*, "label")= chr "respondent's month of birth"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v010     : num [1:15068] 1982 1980 1985 1979 1986 ...
  ..- attr(*, "label")= chr "respondent's year of birth"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v011     : num [1:15068] 996 966 1030 953 1033 ...
  ..- attr(*, "label")= chr "date of birth (cmc)"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v012     : num [1:15068] 35 37 32 38 32 27 21 28 38 35 ...
  ..- attr(*, "label")= chr "respondent's current age"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v013     : dbl+lbl [1:15068] 5, 5, 4, 5, 4, 3, 2, 3, 5, 5, 4, 2, 6, 7, 6, 4, 3, 6...
   ..@ label       : chr "age in 5-year groups"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:7] 1 2 3 4 5 6 7
   .. ..- attr(*, "names")= chr [1:7] "15-19" "20-24" "25-29" "30-34" ...
 $ v014     : dbl+lbl [1:15068] 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 1, 1, 1...
   ..@ label       : chr "completeness of age information"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:8] 1 2 3 4 5 6 7 8
   .. ..- attr(*, "names")= chr [1:8] "month and year - information complete" "month and age - year imputed" "year and age - month imputed" "year and age - year ignored" ...
 $ v015     : dbl+lbl [1:15068] 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1...
   ..@ label       : chr "result of individual interview"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:7] 1 2 3 4 5 6 7
   .. ..- attr(*, "names")= chr [1:7] "completed" "not at home" "postponed" "refused" ...
 $ v016     : num [1:15068] 4 3 4 2 2 2 2 3 3 3 ...
  ..- attr(*, "label")= chr "day of interview"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v017     : num [1:15068] 1345 1345 1345 1345 1345 ...
  ..- attr(*, "label")= chr "cmc start of calendar"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v018     : dbl+lbl [1:15068] 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7...
   ..@ label       : chr "row of month of interview"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num 0
   .. ..- attr(*, "names")= chr "no calendar"
 $ v019     : dbl+lbl [1:15068] 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, ...
   ..@ label       : chr "length of calendar"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num 0
   .. ..- attr(*, "names")= chr "no calendar"
 $ v019a    : dbl+lbl [1:15068] 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2...
   ..@ label       : chr "number of calendar columns"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num 0
   .. ..- attr(*, "names")= chr "no calendar"
 $ v020     : dbl+lbl [1:15068] 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1...
   ..@ label       : chr "ever-married sample"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:2] 0 1
   .. ..- attr(*, "names")= chr [1:2] "all woman sample" "ever married sample"
 $ v021     : num [1:15068] 1 1 1 1 1 1 1 1 1 1 ...
  ..- attr(*, "label")= chr "primary sampling unit"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v022     : dbl+lbl [1:15068] 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5...
   ..@ label       : chr "sample strata for sampling errors"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:16] 1 2 3 4 5 6 7 8 9 10 ...
   .. ..- attr(*, "names")= chr [1:16] "punjab rural" "punjab urban" "sindh rural" "sindh urban" ...
 $ v023     : dbl+lbl [1:15068] 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5...
   ..@ label       : chr "stratification used in sample design"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:16] 1 2 3 4 5 6 7 8 9 10 ...
   .. ..- attr(*, "names")= chr [1:16] "punjab rural" "punjab urban" "sindh rural" "sindh urban" ...
 $ v024     : dbl+lbl [1:15068] 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3...
   ..@ label       : chr "region"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:8] 1 2 3 4 5 6 7 8
   .. ..- attr(*, "names")= chr [1:8] "punjab" "sindh" "kpk" "balochistan" ...
 $ v025     : dbl+lbl [1:15068] 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2...
   ..@ label       : chr "type of place of residence"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:2] 1 2
   .. ..- attr(*, "names")= chr [1:2] "urban" "rural"
 $ v026     : dbl+lbl [1:15068] NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ...
   ..@ label       : chr "na - de facto place of residence"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:4] 0 1 2 3
   .. ..- attr(*, "names")= chr [1:4] "capital, large city" "small city" "town" "countryside"
 $ v027     : num [1:15068] 1 1 1 1 1 1 1 1 1 1 ...
  ..- attr(*, "label")= chr "number of visits"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v028     : num [1:15068] 1401 1401 1401 1403 1403 ...
  ..- attr(*, "label")= chr "interviewer identification"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v029     : num [1:15068] 19 19 19 19 19 19 19 19 19 19 ...
  ..- attr(*, "label")= chr "keyer identification"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v030     : num [1:15068] 1400 1400 1400 1400 1400 1400 1400 1400 1400 1400 ...
  ..- attr(*, "label")= chr "field supervisor"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v031     : num [1:15068] 1407 1407 1407 1407 1407 ...
  ..- attr(*, "label")= chr "field editor"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v032     : num [1:15068] NA NA NA NA NA NA NA NA NA NA ...
  ..- attr(*, "label")= chr "na - office editor"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v034     : dbl+lbl [1:15068]  1,  0,  0, NA,  1,  3,  6,  3,  1,  1,  0,  1,  1, ...
   ..@ label       : chr "line number of husband"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num 0
   .. ..- attr(*, "names")= chr "husband not in household"
 $ v040     : num [1:15068] NA NA NA NA NA NA NA NA NA NA ...
  ..- attr(*, "label")= chr "na - cluster altitude in meters"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v042     : dbl+lbl [1:15068] 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
   ..@ label       : chr "household selected for hemoglobin"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:2] 0 1
   .. ..- attr(*, "names")= chr [1:2] "not selected" "selected"
 $ v044     : dbl+lbl [1:15068] 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0...
   ..@ label       : chr "selected for domestic violence module"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:4] 0 1 2 3
   .. ..- attr(*, "names")= chr [1:4] "woman not selected" "woman selected and interviewed" "woman selected, but privacy not possible" "woman selected, but not interviewed"
 $ v045a    : dbl+lbl [1:15068] 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2...
   ..@ label       : chr "language of questionnaire"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:8] 1 2 3 4 5 6 7 8
   .. ..- attr(*, "names")= chr [1:8] "english" "urdu" "sindhi" "punjabi" ...
 $ v045b    : dbl+lbl [1:15068] 2, 2, 2, 7, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2...
   ..@ label       : chr "language of interview"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:8] 1 2 3 4 5 6 7 8
   .. ..- attr(*, "names")= chr [1:8] "english" "urdu" "sindhi" "punjabi" ...
 $ v045c    : dbl+lbl [1:15068] 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8...
   ..@ label       : chr "native language of respondent"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:8] 1 2 3 4 5 6 7 8
   .. ..- attr(*, "names")= chr [1:8] "english" "urdu" "sindhi" "punjabi" ...
 $ v046     : dbl+lbl [1:15068] 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1...
   ..@ label       : chr "translator used"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:2] 0 1
   .. ..- attr(*, "names")= chr [1:2] "no" "yes"
 $ v101     : dbl+lbl [1:15068] 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3...
   ..@ label       : chr "region"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:8] 1 2 3 4 5 6 7 8
   .. ..- attr(*, "names")= chr [1:8] "punjab" "sindh" "kpk" "balochistan" ...
 $ v102     : dbl+lbl [1:15068] 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2...
   ..@ label       : chr "type of place of residence"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:2] 1 2
   .. ..- attr(*, "names")= chr [1:2] "urban" "rural"
 $ v103     : dbl+lbl [1:15068] NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ...
   ..@ label       : chr "na - childhood place of residence"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:5] 0 1 2 3 4
   .. ..- attr(*, "names")= chr [1:5] "capital, large city" "city" "town" "countryside" ...
 $ v104     : dbl+lbl [1:15068] NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ...
   ..@ label       : chr "na - years lived in place of residence"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:4] 95 96 97 98
   .. ..- attr(*, "names")= chr [1:4] "always" "visitor" "inconsistent" "don't know"
 $ v105     : dbl+lbl [1:15068] NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ...
   ..@ label       : chr "na - type of place of previous residence"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:5] 0 1 2 3 4
   .. ..- attr(*, "names")= chr [1:5] "capital, large city" "city" "town" "countryside" ...
 $ v105a    : dbl+lbl [1:15068] NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ...
   ..@ label       : chr "na - region of previous residence"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:9] 1 2 3 4 5 6 7 8 96
   .. ..- attr(*, "names")= chr [1:9] "punjab" "sindh" "kpk" "balochistan" ...
 $ v106     : dbl+lbl [1:15068] 2, 0, 0, 0, 0, 2, 3, 0, 0, 3, 0, 0, 0, 1, 0, 3, 3, 0...
   ..@ label       : chr "highest educational level"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:4] 0 1 2 3
   .. ..- attr(*, "names")= chr [1:4] "no education" "primary" "secondary" "higher"
 $ v107     : dbl+lbl [1:15068]  5, NA, NA, NA, NA,  5,  2, NA, NA,  4, NA, NA, NA, ...
   ..@ label       : chr "highest year of education"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num 0
   .. ..- attr(*, "names")= chr "no years completed at level v106"
 $ v113     : dbl+lbl [1:15068] 42, 42, 42, 43, 43, 43, 43, 42, 41, 41, 41, 41, 43, ...
   ..@ label       : chr "source of drinking water"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:21] 10 11 12 13 14 20 21 30 31 32 ...
   .. ..- attr(*, "names")= chr [1:21] "piped water" "piped into dwelling" "piped to yard/plot" "piped to neighbor" ...
 $ v115     : dbl+lbl [1:15068]  20,  30,  25,  15,  60,  40,  40,  20,   5,   5,   ...
   ..@ label       : chr "time to get to water source"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:3] 996 997 998
   .. ..- attr(*, "names")= chr [1:3] "on premises" "not a dejure resident" "don't know"
 $ v116     : dbl+lbl [1:15068] 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, ...
   ..@ label       : chr "type of toilet facility"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:17] 10 11 12 13 14 15 20 21 22 23 ...
   .. ..- attr(*, "names")= chr [1:17] "flush toilet" "flush to piped sewer system" "flush to septic tank" "flush to pit latrine" ...
 $ v119     : dbl+lbl [1:15068] 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1...
   ..@ label       : chr "household has: electricity"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:3] 0 1 7
   .. ..- attr(*, "names")= chr [1:3] "no" "yes" "not a dejure resident"
 $ v120     : dbl+lbl [1:15068] 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0...
   ..@ label       : chr "household has: radio"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:3] 0 1 7
   .. ..- attr(*, "names")= chr [1:3] "no" "yes" "not a dejure resident"
 $ v121     : dbl+lbl [1:15068] 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0...
   ..@ label       : chr "household has: television"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:3] 0 1 7
   .. ..- attr(*, "names")= chr [1:3] "no" "yes" "not a dejure resident"
 $ v122     : dbl+lbl [1:15068] 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
   ..@ label       : chr "household has: refrigerator"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:3] 0 1 7
   .. ..- attr(*, "names")= chr [1:3] "no" "yes" "not a dejure resident"
 $ v123     : dbl+lbl [1:15068] 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
   ..@ label       : chr "household has: bicycle"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:3] 0 1 7
   .. ..- attr(*, "names")= chr [1:3] "no" "yes" "not a dejure resident"
 $ v124     : dbl+lbl [1:15068] 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0...
   ..@ label       : chr "household has: motorcycle/scooter"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:3] 0 1 7
   .. ..- attr(*, "names")= chr [1:3] "no" "yes" "not a dejure resident"
 $ v125     : dbl+lbl [1:15068] 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
   ..@ label       : chr "household has: car/truck"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:3] 0 1 7
   .. ..- attr(*, "names")= chr [1:3] "no" "yes" "not a dejure resident"
 $ v127     : dbl+lbl [1:15068] 11, 34, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, ...
   ..@ label       : chr "main floor material"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:18] 10 11 12 20 21 22 30 31 32 33 ...
   .. ..- attr(*, "names")= chr [1:18] "natural" "earth/sand" "dung" "rudimentary" ...
 $ v128     : dbl+lbl [1:15068] 14, 14, 14, 21, 21, 21, 21, 21, 21, 21, 21, 21, 31, ...
   ..@ label       : chr "main wall material"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:22] 10 11 12 13 14 15 20 21 22 23 ...
   .. ..- attr(*, "names")= chr [1:22] "natural" "no walls" "cane/palm/trunks" "dirt" ...
 $ v129     : dbl+lbl [1:15068] 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 23, ...
   ..@ label       : chr "main roof material"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:20] 10 11 12 13 20 21 22 23 24 30 ...
   .. ..- attr(*, "names")= chr [1:20] "natural" "no roof" "thatch/palm leaf" "sod/grass" ...
 $ v130     : dbl+lbl [1:15068] NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ...
   ..@ label       : chr "na - religion"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num 96
   .. ..- attr(*, "names")= chr "other"
 $ v131     : num [1:15068] NA NA NA NA NA NA NA NA NA NA ...
  ..- attr(*, "label")= chr "na - ethnicity"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v133     : dbl+lbl [1:15068] 10,  0,  0,  0,  0, 10, 12,  0,  0, 14,  0,  0,  0, ...
   ..@ label       : chr "education in single years"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num 97
   .. ..- attr(*, "names")= chr "inconsistent"
 $ v134     : dbl+lbl [1:15068] NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ...
   ..@ label       : chr "na - de facto place of residence"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:4] 0 1 2 3
   .. ..- attr(*, "names")= chr [1:4] "capital, large city" "small city" "town" "countryside"
 $ v135     : dbl+lbl [1:15068] 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1...
   ..@ label       : chr "usual resident or visitor"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:2] 1 2
   .. ..- attr(*, "names")= chr [1:2] "usual resident" "visitor"
 $ v136     : num [1:15068] 7 6 7 5 7 9 9 8 11 11 ...
  ..- attr(*, "label")= chr "number of household members (listed)"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v137     : num [1:15068] 1 1 0 0 0 1 1 2 1 1 ...
  ..- attr(*, "label")= chr "number of children 5 and under in household (de jure)"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v138     : num [1:15068] 1 1 1 1 1 2 2 1 2 2 ...
  ..- attr(*, "label")= chr "number of eligible women in household (de facto)"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v139     : dbl+lbl [1:15068] 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3...
   ..@ label       : chr "de jure region of residence"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:10] 0 1 2 3 4 5 6 7 8 97
   .. ..- attr(*, "names")= chr [1:10] "abroad" "punjab" "sindh" "kpk" ...
 $ v140     : dbl+lbl [1:15068] 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2...
   ..@ label       : chr "de jure type of place of residence"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:4] 0 1 2 7
   .. ..- attr(*, "names")= chr [1:4] "abroad" "urban" "rural" "not a dejure resident"
 $ v141     : dbl+lbl [1:15068] NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ...
   ..@ label       : chr "na - de jure place of residence"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:6] 0 1 2 3 4 7
   .. ..- attr(*, "names")= chr [1:6] "capital, large city" "city" "town" "countryside" ...
 $ v149     : dbl+lbl [1:15068] 4, 0, 0, 0, 0, 4, 5, 0, 0, 5, 0, 0, 0, 2, 0, 5, 5, 0...
   ..@ label       : chr "educational attainment"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:6] 0 1 2 3 4 5
   .. ..- attr(*, "names")= chr [1:6] "no education" "incomplete primary" "complete primary" "incomplete secondary" ...
 $ v150     : dbl+lbl [1:15068]  2,  1,  4,  4,  2,  4,  4,  4,  2,  2,  1,  2,  2, ...
   ..@ label       : chr "relationship to household head"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:19] 1 2 3 4 5 6 7 8 9 10 ...
   .. ..- attr(*, "names")= chr [1:19] "head" "wife" "daughter" "daughter-in-law" ...
 $ v151     : dbl+lbl [1:15068] 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2...
   ..@ label       : chr "sex of household head"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:2] 1 2
   .. ..- attr(*, "names")= chr [1:2] "male" "female"
 $ v152     : dbl+lbl [1:15068] 44, 37, 70, 82, 43, 74, 74, 81, 45, 45, 33, 27, 44, ...
   ..@ label       : chr "age of household head"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:2] 95 98
   .. ..- attr(*, "names")= chr [1:2] "95+" "don't know"
 $ v153     : dbl+lbl [1:15068] 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
   ..@ label       : chr "household has: telephone (land-line)"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:3] 0 1 7
   .. ..- attr(*, "names")= chr [1:3] "no" "yes" "not a dejure resident"
 $ awfactt  : num [1:15068] 107 103 107 104 107 121 238 117 104 104 ...
  ..- attr(*, "label")= chr "all woman factor - total"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ awfactu  : num [1:15068] 106 104 106 105 106 121 227 116 105 104 ...
  ..- attr(*, "label")= chr "all woman factor - urban/rural"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ awfactr  : num [1:15068] 119 100 114 110 114 107 215 119 110 102 ...
  ..- attr(*, "label")= chr "all woman factor - regional"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ awfacte  : num [1:15068] 103 104 111 105 111 118 661 107 105 105 ...
  ..- attr(*, "label")= chr "all woman factor - educational"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ awfactw  : num [1:15068] 102 103 104 104 104 130 250 111 108 105 ...
  ..- attr(*, "label")= chr "all woman factor - wealth index"
  ..- attr(*, "format.stata")= chr "%8.0g"
 $ v155     : dbl+lbl [1:15068] 2, 0, 0, 0, 0, 2, 2, 0, 0, 2, 0, 0, 0, 1, 0, 2, 2, 0...
   ..@ label       : chr "literacy"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:5] 0 1 2 3 4
   .. ..- attr(*, "names")= chr [1:5] "cannot read at all" "able to read only parts of sentence" "able to read whole sentence" "no card with required language" ...
 $ v156     : dbl+lbl [1:15068] NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ...
   ..@ label       : chr "na - ever participated in a literacy program outside of primary"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:2] 0 1
   .. ..- attr(*, "names")= chr [1:2] "no" "yes"
 $ v157     : dbl+lbl [1:15068] 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
   ..@ label       : chr "frequency of reading newspaper or magazine"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:4] 0 1 2 3
   .. ..- attr(*, "names")= chr [1:4] "not at all" "less than once a week" "at least once a week" "almost every day"
 $ v158     : dbl+lbl [1:15068] 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0...
   ..@ label       : chr "frequency of listening to radio"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:4] 0 1 2 3
   .. ..- attr(*, "names")= chr [1:4] "not at all" "less than once a week" "at least once a week" "almost every day"
 $ v159     : dbl+lbl [1:15068] 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0...
   ..@ label       : chr "frequency of watching television"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:4] 0 1 2 3
   .. ..- attr(*, "names")= chr [1:4] "not at all" "less than once a week" "at least once a week" "almost every day"
 $ v160     : dbl+lbl [1:15068]  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, ...
   ..@ label       : chr "toilet facilities shared with other households"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:3] 0 1 7
   .. ..- attr(*, "names")= chr [1:3] "no" "yes" "not a dejure resident"
 $ v161     : dbl+lbl [1:15068] 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8...
   ..@ label       : chr "type of cooking fuel"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:14] 1 2 3 4 5 6 7 8 9 10 ...
   .. ..- attr(*, "names")= chr [1:14] "electricity" "lpg" "natural gas" "biogas" ...
 $ v166     : dbl+lbl [1:15068] NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ...
   ..@ label       : chr "na - result of salt test for iodine"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:7] 0 7 15 30 994 995 997
   .. ..- attr(*, "names")= chr [1:7] "0 ppm (no iodine)" "7 ppm" "15 ppm" "30 ppm" ...
 $ v167     : dbl+lbl [1:15068] NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ...
   ..@ label       : chr "na - number of trips in last 12 months"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:2] 0 90
   .. ..- attr(*, "names")= chr [1:2] "none" "90+"
 $ v168     : dbl+lbl [1:15068] NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ...
   ..@ label       : chr "na - away for more than one month in last 12 months"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:2] 0 1
   .. ..- attr(*, "names")= chr [1:2] "no" "yes"
 $ v169a    : dbl+lbl [1:15068] 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1...
   ..@ label       : chr "owns a mobile telephone"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:2] 0 1
   .. ..- attr(*, "names")= chr [1:2] "no" "yes"
 $ v169b    : dbl+lbl [1:15068]  0,  0, NA,  0, NA,  0,  0, NA,  0,  0,  0, NA,  0, ...
   ..@ label       : chr "use mobile telephone for financial transactions"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:2] 0 1
   .. ..- attr(*, "names")= chr [1:2] "no" "yes"
 $ v170     : dbl+lbl [1:15068] 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0...
   ..@ label       : chr "has an account in a bank or other financial institution"
   ..@ format.stata: chr "%8.0g"
   ..@ labels      : Named num [1:2] 0 1
   .. ..- attr(*, "names")= chr [1:2] "no" "yes"
  [list output truncated]

🧩 Exercise 1

Inspect one variable’s category labels.

Code
print_labels(pkir$v106)  # Education level

Labels:
 value        label
     0 no education
     1      primary
     2    secondary
     3       higher

🧩 Session 2: Recoding Variables

Code
print_labels(pkir$v313)  # Check method type codes

Labels:
 value              label
     0          no method
     1   folkloric method
     2 traditional method
     3      modern method
Code
pkir <- pkir %>%
  mutate(modfp = case_when(
    v313 == 3 ~ 1,
    v313 < 3 ~ 0
  )) %>%
  set_value_labels(modfp = c("Yes" = 1, "No" = 0)) %>%
  set_variable_labels(modfp = "Currently used any modern method")
Code
pkir <- pkir %>%
  mutate(age_child = v008 - b3_01,
         ancvisits = case_when(
           m14_1 == 0 ~ 0,
           m14_1 == 1 ~ 1,
           m14_1 %in% c(2,3) ~ 2,
           m14_1 >= 4 & m14_1 <= 90 ~ 3,
           TRUE ~ NA_real_
         )) %>%
  set_value_labels(ancvisits = c("None" = 0, "1" = 1, "2-3" = 2, "4+" = 3)) %>%
  set_variable_labels(ancvisits = "Number of ANC visits")

⚖️ Session 3: Understanding and Applying Survey Weights

Why Use Weights?

DHS employs complex sampling. Weights adjust for unequal selection probabilities and ensure representativeness.

Code
pkir <- pkir %>%
  mutate(wt = v005 / 1e6)
Code
topline(pkir, modfp, wt)

🧮 Session 4: Defining the Survey Design Object

Code
mysurvey <- svydesign(
  id = pkir$v021,
  strata = pkir$v022,
  weights = pkir$wt,
  data = pkir,
  nest = TRUE
)
options(survey.lonely.psu = "adjust")
Code
prop.table(svytable(~modfp, mysurvey))
modfp
        0         1 
0.7565067 0.2434933 

📊 Session 5: Crosstabulations and Associations

Code
table_edu <- svyby(~modfp, by = ~v106, design = mysurvey, FUN = svymean, vartype = c("se", "ci"))
table_edu
Code
svychisq(~modfp + v106, mysurvey)

    Pearson's X^2: Rao & Scott adjustment

data:  svychisq(~modfp + v106, mysurvey)
F = 10.046, ndf = 2.715, ddf = 1210.875, p-value = 4.043e-06

📈 Session 6: Logistic Regression Analysis

Code
reg1 <- svyglm(modfp ~ v025, design = mysurvey, family = binomial(link = "logit"))
summary(reg1)

Call:
svyglm(formula = modfp ~ v025, design = mysurvey, family = binomial(link = "logit"))

Survey design:
svydesign(id = pkir$v021, strata = pkir$v022, weights = pkir$wt, 
    data = pkir, nest = TRUE)

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) -0.64717    0.10343  -6.257 9.24e-10 ***
v025        -0.30138    0.06782  -4.444 1.12e-05 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1.000081)

Number of Fisher Scoring iterations: 4
Code
exp(reg1$coefficients)
(Intercept)        v025 
  0.5235268   0.7397958 
Code
reg2 <- svyglm(modfp ~ v025 + as.factor(v106) + as.factor(v013) + as.factor(v190),
               design = mysurvey, family = binomial(link = "logit"))

library(broom)
tidy(reg2, exponentiate = TRUE, conf.int = TRUE)

🧮 Session 7: Correlation and Diagnostics

Code
cordata <- pkir %>% select(v025, v106, v013, v190)
cor_matrix <- cor(cordata, use = "pairwise.complete.obs")

corrplot(cor_matrix, method = "circle")

📤 Session 8: Exporting and Presenting Results

Code
write.xlsx(table_edu, "outputs/weighted_tables.xlsx", sheetName = "Education vs FP")
Code
top_tbl <- topline(pkir, modfp, wt) %>%
  mutate(category = as.character(category)) 

# Convert from labelled to text



gt_tbl <- gt::gt(top_tbl)

gt_tbl <- gt::fmt_number(
  data = gt_tbl,
  columns = dplyr::vars(weighted_pct),
  decimals = 2
)

gt_tbl <- gt::tab_header(
  gt_tbl,
  title = gt::md("**Modern Contraceptive Use among Women (Weighted)**")
)

gt_tbl
Modern Contraceptive Use among Women (Weighted)
category weighted_pct
0 75.65
1 24.35

🎓 Wrap-Up and Reflection

Step Concept Function(s)
Data loading Importing DHS data read_dta()
Recoding Creating new indicators mutate(), case_when()
Weighting Adjust for complex survey v005 / 1e6
Design setup Declare design svydesign()
Analysis Weighted stats svymean(), svyby(), svyglm()
Export Save outputs write.xlsx(), gt()
Back to top