6 Analyzing AMPLab data in R
This section provides a practical introduction to analysing AMPLab data
in R using the Rrepest package. It is intended for users with a basic
working knowledge of R and includes step-by-step examples covering
common AMPLab analyses.
Use the contents below to navigate directly to the topics most relevant to your analysis.
6.1 Package installation
To get started, you need to install the required packages once. In the R console, type:
- The
havenpackage is needed to read Stata.dtafiles. - The
Rrepestpackage automates the handling of plausible values and replicate weights, making it easier to analyse AMPLab data correctly in R. - The
surveyandmitoolspackages are used only for the alternative analyses shown in this chapter. - The
tidyversepackage is used for data wrangling. - The
gtpackage is used to present selected results in readable tables.
You only need to run install.packages() once; there is no need to
reinstall each time you open R.
6.2 Loading data in R
For hands-on examples we’ll start with the student data.
It is good practice to run your analysis from an R script rather than the console, as this makes your work reproducible. All examples in this section are written as script code.
6.3 Add variable and value labels
We have supplied an R script that applies variable and value labels to
the data. These files are in the data/ folder of this project.
# Apply variable and value labels
source("data/ampl-student-labels.R")
ampl_dat <- apply_ampl_stq_labels(ampl_dat)After loading the file, let’s check if we have the most important
variables that Rrepest uses. The five plausible values for language
and mathematics are pv1_read to pv5_read and pv1_math to
pv5_math, respectively. The final weight is fwgt. The replicate
weights are rwgt1 to rwgt135 and countries are indicated by cnt.
# Confirm key variables exist
ampl_dat |>
select(matches("^pv[1-5]_(read|math)$"), fwgt, rwgt1:rwgt10, cnt) |>
glimpse()## Rows: 22,214
## Columns: 22
## $ pv1_math <dbl> 0.22811525, -2.62520194, -2.76957273, -0.08613674, 0.13072035, 0.47429678, -0.70883948, -0.74843246, 0.32783359, 0.06470735, 1.34257…
## $ pv2_math <dbl> 0.462508053, -1.674260497, -3.285824299, 0.678741813, 0.057586100, -0.041360527, -0.605331600, -0.487574786, -0.000326509, -0.026982…
## $ pv3_math <dbl> 0.89248133, -1.76391876, -3.35175014, 0.95755553, 0.16893062, -0.08748718, -0.18168271, -1.29100907, 0.41965342, 0.42386377, 1.39329…
## $ pv4_math <dbl> 0.60037166, -2.21478987, -3.26386952, -0.47502181, -0.19398750, 0.01470500, -0.34860367, -1.24189603, 0.35222486, 0.07257382, 1.6342…
## $ pv5_math <dbl> 0.76853716, -1.63746548, -3.26517296, 0.27711055, 0.85098785, -0.25193688, 0.37577242, -1.31283343, 0.61149669, 0.60424227, 0.967157…
## $ pv1_read <dbl> 0.768692255, -1.638499618, -2.526624203, 0.606318355, 0.767933547, 0.678709507, 0.351070166, -0.241998076, 1.266379833, 0.826490045,…
## $ pv2_read <dbl> 0.57991463, -0.88148451, -2.35456824, 1.11718643, 0.89872378, 0.64056826, 0.09324962, -0.18746106, 1.17088628, 0.44311303, 1.9112074…
## $ pv3_read <dbl> 1.05408597, -1.38343024, -2.53575850, 1.52389026, 0.83753365, 0.45622772, 0.58972800, -0.49328646, 1.05618250, 1.15197921, 1.9779125…
## $ pv4_read <dbl> 1.198313951, -1.116216302, -2.667570114, 0.841243982, 0.547337115, 0.909113705, -0.142348379, -0.111854941, 0.898920715, 1.050507784…
## $ pv5_read <dbl> 0.94776469, -1.12560523, -2.62032199, 1.02373171, 0.85645103, 0.02454279, 0.43081874, -0.22813433, 1.29445171, 1.11652923, 1.5622578…
## $ fwgt <dbl> 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 24…
## $ rwgt1 <dbl> 495.3116, 495.3116, 495.3116, 495.3116, 495.3116, 495.3116, 495.3116, 495.3116, 495.3116, 495.3116, 495.3116, 495.3116, 495.3116, 49…
## $ rwgt2 <dbl> 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 24…
## $ rwgt3 <dbl> 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 24…
## $ rwgt4 <dbl> 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 24…
## $ rwgt5 <dbl> 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 24…
## $ rwgt6 <dbl> 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 24…
## $ rwgt7 <dbl> 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 24…
## $ rwgt8 <dbl> 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 24…
## $ rwgt9 <dbl> 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 24…
## $ rwgt10 <dbl> 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 247.6558, 24…
## $ cnt <chr> "KEN", "KEN", "KEN", "KEN", "KEN", "KEN", "KEN", "KEN", "KEN", "KEN", "KEN", "KEN", "KEN", "KEN", "KEN", "KEN", "KEN", "KEN", "KEN",…
All required variables for Rrepest are available in the data.
6.4 The Rrepest package
Rrepest automates the handling of plausible values and replicate
weights. Once the survey settings have been specified, you can use it to
estimate means, summary statistics, proficiency levels, group
differences, percentiles, and regression models while correctly
accounting for both plausible values and replicate weights.
6.5 Rrepest command syntax
The basic syntax of the Rrepest() function is as follows:
Rrepest(
data = <data frame or subset>,
svy = <survey name>,
est = est(<statistic>, target = <variable(s)>, regressor = <variable(s)>),
by = <grouping variable(s)>, # separate analysis per group
over = <subgroup variable(s)>, # within-group comparison
cm.weights = c(<final weight>, <replicate weights>),
var.factor = <variance scaling constant>,
n.pvs = <number of plausible values>
)6.6 Before you begin: Set up Rrepest for AMPLab
AMPLab is not one of the studies supported by the Rrepest package,
meaning that there aren’t built-in survey specifications. Therefore,
before running any analyses, you need to tell Rrepest how the AMPLab
data are structured.
You will need to use svy = "SVY" and supply the survey parameters
directly. To analyse AMPLab data correctly, Rrepest needs to know the
following survey specifications:
AMPLab uses the paired Jackknife method for creating the replicate weights (JK2).
There are 135 replicate weights in the data.
The final weight is given by fwgt.
There are five sets of plausible values for each of mathematics, pv1_math to pv5_math, and reading, pv1_read to pv5_read.
Define the weight vector once and reuse it throughout your analyses:
# Weight helpers — define once, use everywhere
cm <- c("fwgt", paste0("rwgt", 1:135)) # final + 135 replicate weightsWe will often present {Rrepest} output with the {gt} package. The
helper below reshapes linear-regression results into a tidy table of
estimates, standard errors, and \(t\)-ratios.
You do not need to understand this as it is for improving readability of the {Rrepest} output.
# Reshape Rrepest lm output for gt presentation
# b_cols / se_cols: character vectors of column names in the Rrepest result
# terms: labels shown in the table (same order as b_cols / se_cols)
rrepest_lm_table <- function(result, b_cols, se_cols, terms) {
df <- as.data.frame(result)
tibble(
term = terms,
estimate = as.numeric(unlist(df[1, b_cols], use.names = FALSE)),
se = as.numeric(unlist(df[1, se_cols], use.names = FALSE))
) |>
mutate(
t_value = estimate / se,
estimate_se = sprintf("%.3f (%.3f)", estimate, se)
)
}
show_lm_gt <- function(tbl, title, subtitle, source_note = NULL) {
out <- tbl |>
select(term, estimate_se, t_value) |>
gt() |>
tab_header(title = title, subtitle = subtitle) |>
cols_label(
term = "Term",
estimate_se = "Estimate (SE)",
t_value = "t"
) |>
fmt_number(columns = t_value, decimals = 2) |>
tab_style(
style = cell_text(weight = "bold"),
locations = cells_column_labels()
)
if (!is.null(source_note)) {
out <- out |> tab_source_note(source_note = source_note)
}
out
}Commands for analysing the AMPLab student data will have the following syntax:
6.7 AMPLab Analyses Examples
Before we start analysing proficiency scores, let us get a sense of the countries and grades presented in the data.
## # A tibble: 5 × 3
## cnt grade_stf n
## <chr> <dbl> <int>
## 1 GMB 3 4106
## 2 KEN 6 5238
## 3 LSO 7 3917
## 4 ZMB 4 4474
## 5 ZMB 7 4479
6.7.1 Calculating Mean Age by Country
Let’s start by calculating the mean age of students in Grade 3 in The Gambia. The age variable is called s_age.
ampl_dat |>
summarise(
n = n(),
min_age = min(s_age, na.rm = TRUE),
max_age = max(s_age, na.rm = TRUE),
n_missing_code = sum(s_age == 9999, na.rm = TRUE)
)## # A tibble: 1 × 4
## n min_age max_age n_missing_code
## <int> <dbl+lbl> <dbl+lbl> <int>
## 1 22214 6 9999 [Missing] 172
Note that ages are not integers, so treating age as a short list of categories would not be appropriate. Missing age values are coded as \(9999\). Because \(9999\) is a placeholder used to indicate missing data rather than a student’s actual age, these observations should be excluded from analyses involving age. Failure to do so may produce misleading results.
To calculate the average age of students in Grade 3 in The Gambia, the syntax is as follows:
# Calculating average age in The Gambia
result_age <- Rrepest(
data = subset(ampl_dat, cnt == "GMB" & s_age < 9999),
svy = "SVY",
est = est("mean", target = "s_age"),
cm.weights = cm,
var.factor = 135
)
print(result_age)## # A tibble: 1 × 3
## . b.mean.s_age se.mean.s_age
## <chr> <dbl> <dbl>
## 1 Total 10.7 0.0335
We present the {Rrepest} output above in a reader-friendly format using
the {gt} package.
as.data.frame(result_age) |>
transmute(
estimate = `b.mean.s_age`,
se = `se.mean.s_age`
) |>
gt() |>
tab_header(
title = "Mean student age",
subtitle = "The Gambia (Grade 3)"
) |>
cols_label(estimate = "Mean age", se = "SE") |>
fmt_number(columns = c(estimate, se), decimals = 3) |>
tab_style(
style = cell_text(weight = "bold"),
locations = cells_column_labels()
)| Mean student age | |
| The Gambia (Grade 3) | |
| Mean age | SE |
|---|---|
| 10.744 | 0.034 |
Because age is an observed variable rather than a plausible-value
variable, Rrepest uses only the sampling variance derived from the
replicate weights. The reported standard error therefore reflects
uncertainty arising from the sample design but not measurement
uncertainty.
The estimated mean age of Grade 3 students in The Gambia is \(10.74\) years (\(95\%\) CI: \(10.67–10.81\)). This reflects the substantial grade repetition and late entry common in the region, with many students aged \(11–23\) also enrolled in Grade 3.
AMPLab provides replicate weights specifically so that users can
reproduce the official variance estimation procedure. The survey
package in R can be configured to use the paired jackknife replicate
weights.
The syntax to generate the same result is:
options(survey.lonely.psu = "adjust")
des_age <- svrepdesign(
weights = ~fwgt,
repweights = as.matrix(ampl_dat[, paste0("rwgt", 1:135)]),
type = "other",
scale = 1,
rscales = rep(1, 135),
combined.weights = TRUE,
mse = TRUE,
data = ampl_dat
)
svymean(~s_age, subset(des_age, cnt == "GMB" & s_age < 9999), na.rm = TRUE)## mean SE
## s_age 10.744 0.0335
6.7.2 Calculating mean mathematics proficiency
Now we will use \(5\) plausible values to estimate mean mathematics scores in The Gambia.
Plausible values are a set of multiple imputations. The Rrepest
package automatically recognises plausible values when the variable name
contains the @ symbol.
For example:
tells Rrepest to analyse all five reading plausible values, combine
results appropriately, and calculate standard errors that reflect both
sampling and measurement uncertainty. This allows researchers to obtain
valid estimates without having to implement the multiple-imputation
calculations manually.
result_math <- Rrepest(
data = subset(ampl_dat, cnt == "GMB"),
svy = "SVY",
est = est("mean", target = "pv@_math"),
cm.weights = cm,
var.factor = 135,
n.pvs = 5
)
print(result_math)## # A tibble: 1 × 3
## . `b.mean.pv@_math` `se.mean.pv@_math`
## <chr> <dbl> <dbl>
## 1 Total -2.34 0.0364
We present the {Rrepest} output above in a reader-friendly format using
the {gt} package.
as.data.frame(result_math) |>
transmute(
estimate = `b.mean.pv@_math`,
se = `se.mean.pv@_math`
) |>
gt() |>
tab_header(
title = "Mean mathematics proficiency",
subtitle = "The Gambia (Grade 3) — Learning Progressions Scale"
) |>
cols_label(estimate = "Mean", se = "SE") |>
fmt_number(columns = c(estimate, se), decimals = 3) |>
tab_style(
style = cell_text(weight = "bold"),
locations = cells_column_labels()
)| Mean mathematics proficiency | |
| The Gambia (Grade 3) — Learning Progressions Scale | |
| Mean | SE |
|---|---|
| −2.339 | 0.036 |
The estimated mean mathematics proficiency in The Gambia is \(-2.34\) on the Learning Progressions Scale (\(95\%\) CI: \(-2.41\) to \(-2.27\); SE = \(0.036\)). Scores on this scale are centred near the AMPL proficiency cut scores, so a negative mean indicates that Grade 3 students in The Gambia are, on average, below the end-of-primary MPLb threshold. The standard error reflects both sampling uncertainty and uncertainty arising from the plausible values.
Unlike age, mathematics proficiency is represented by five plausible
values. Rrepest estimates the mean separately for each plausible
value, combines the five estimates using multiple-imputation formulas,
and then incorporates the replicate-weight variance to produce the final
standard error.
An alternative approach using the survey and mitools packages
reproduces the same result by looping over plausible values manually.
options(survey.lonely.psu = "adjust")
des_math <- svrepdesign(
weights = ~fwgt,
repweights = as.matrix(ampl_dat[, paste0("rwgt", 1:135)]),
type = "other",
scale = 1,
rscales = rep(1, 135),
combined.weights = TRUE,
mse = TRUE,
data = ampl_dat
)
pv_math <- withPV(
mapping = list(math ~ pv1_math + pv2_math + pv3_math + pv4_math + pv5_math),
data = subset(des_math, cnt == "GMB"),
action = function(dsgn) svymean(~math, dsgn, na.rm = TRUE),
rewrite = TRUE
)
summary(MIcombine(pv_math))## Multiple imputation results:
## withPV.svyrep.design(mapping = list(math ~ pv1_math + pv2_math +
## pv3_math + pv4_math + pv5_math), data = subset(des_math,
## cnt == "GMB"), action = function(dsgn) svymean(~math, dsgn,
## na.rm = TRUE), rewrite = TRUE)
## MIcombine.default(pv_math)
## results se (lower upper) missInfo
## pv1_math -2.339032 0.03639078 -2.410366 -2.267699 2 %
6.7.3 Proficiency across gender
We can go further and ask how mean proficiency compares between girls and
boys within The Gambia. To do this we first create an indicator variable
for girls (1 = girl, 0 = boy).
## # A tibble: 2 × 2
## s_gender n
## <dbl+lbl> <int>
## 1 1 [Female] 11592
## 2 2 [Male] 10622
ampl_dat <- ampl_dat |>
mutate(
girl = if_else(s_gender %in% c(1, 2), as.integer(s_gender == 1), NA_integer_)
)With the girl indicator created, we can compute mean mathematics and
reading proficiency using the by argument.
result_girl <- Rrepest(
data = subset(ampl_dat, cnt == "GMB"),
svy = "SVY",
est = est("mean", target = c("pv@_math", "pv@_read")),
by = "girl",
cm.weights = cm,
var.factor = 135,
n.pvs = 5
)
print(result_girl)## # A tibble: 2 × 5
## girl `b.mean.pv@_math` `se.mean.pv@_math` `b.mean.pv@_read` `se.mean.pv@_read`
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 0 -2.37 0.0393 -1.40 0.0314
## 2 1 -2.31 0.0459 -1.33 0.0363
We present the {Rrepest} output above in a reader-friendly format using
the {gt} package.
as.data.frame(result_girl) |>
mutate(
sex = if_else(as.integer(girl) == 1, "Girls", "Boys"),
math_b = `b.mean.pv@_math`,
math_se = `se.mean.pv@_math`,
read_b = `b.mean.pv@_read`,
read_se = `se.mean.pv@_read`
) |>
select(sex, math_b, math_se, read_b, read_se) |>
gt(rowname_col = "sex") |>
tab_header(
title = "Mean proficiency by gender",
subtitle = "The Gambia (Grade 3)"
) |>
tab_spanner(label = "Mathematics", columns = c(math_b, math_se)) |>
tab_spanner(label = "Reading", columns = c(read_b, read_se)) |>
cols_label(
math_b = "Mean",
math_se = "SE",
read_b = "Mean",
read_se = "SE"
) |>
fmt_number(columns = c(math_b, math_se, read_b, read_se), decimals = 3) |>
tab_style(
style = cell_text(weight = "bold"),
locations = cells_column_labels()
)| Mean proficiency by gender | ||||
| The Gambia (Grade 3) | ||||
|
Mathematics
|
Reading
|
|||
|---|---|---|---|---|
| Mean | SE | Mean | SE | |
| Boys | −2.372 | 0.039 | −1.397 | 0.031 |
| Girls | −2.312 | 0.046 | −1.328 | 0.036 |
In The Gambia, girls score slightly higher than boys in both domains. Mean mathematics is \(-2.31\) for girls (SE \(= 0.046\)) versus \(-2.37\) for boys (SE \(= 0.039\)); mean reading is \(-1.33\) for girls (SE \(= 0.036\)) versus \(-1.40\) for boys (SE \(= 0.031\)). The gaps are modest relative to the overall low level of performance on the LPS, but the pattern is consistent across subjects.
6.7.4 Proficiency by Country
Now let us compare mathematics and reading proficiency across countries.
Zambia administered AMPLab in two grades. In R we can group by both
cnt and grade (or create a combined cnt_grade label) so that
Zambia’s Grade 4 and Grade 7 samples are not averaged together.
ampl_dat <- ampl_dat |>
mutate(
grade_num = as.integer(haven::zap_labels(grade_stf)),
cnt_grade = paste(cnt, grade_num)
)
ampl_dat |>
count(cnt_grade)## # A tibble: 5 × 2
## cnt_grade n
## <chr> <int>
## 1 GMB 3 4106
## 2 KEN 6 5238
## 3 LSO 7 3917
## 4 ZMB 4 4474
## 5 ZMB 7 4479
We can now compute proficiency scores by country (and by grade for Zambia).
result_cntgrade <- Rrepest(
data = ampl_dat,
svy = "SVY",
est = est("mean", target = c("pv@_math", "pv@_read")),
by = "cnt_grade",
cm.weights = cm,
var.factor = 135,
n.pvs = 5
)
print(result_cntgrade)## # A tibble: 5 × 5
## cnt_grade `b.mean.pv@_math` `se.mean.pv@_math` `b.mean.pv@_read` `se.mean.pv@_read`
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 GMB 3 -2.34 0.0364 -1.36 0.0298
## 2 KEN 6 -0.438 0.0339 0.156 0.0390
## 3 LSO 7 -0.853 0.0434 -0.391 0.0494
## 4 ZMB 4 -2.67 0.0352 -1.65 0.0318
## 5 ZMB 7 -1.05 0.0374 -0.523 0.0418
We present the {Rrepest} output above in a reader-friendly format using
the {gt} package.
as.data.frame(result_cntgrade) |>
mutate(
country_grade = cnt_grade,
math_b = `b.mean.pv@_math`,
math_se = `se.mean.pv@_math`,
read_b = `b.mean.pv@_read`,
read_se = `se.mean.pv@_read`
) |>
select(country_grade, math_b, math_se, read_b, read_se) |>
gt(rowname_col = "country_grade") |>
tab_header(
title = "Mean proficiency by country and grade",
subtitle = "Learning Progressions Scale"
) |>
tab_spanner(label = "Mathematics", columns = c(math_b, math_se)) |>
tab_spanner(label = "Reading", columns = c(read_b, read_se)) |>
cols_label(
math_b = "Mean",
math_se = "SE",
read_b = "Mean",
read_se = "SE"
) |>
fmt_number(columns = c(math_b, math_se, read_b, read_se), decimals = 3) |>
tab_style(
style = cell_text(weight = "bold"),
locations = cells_column_labels()
)| Mean proficiency by country and grade | ||||
| Learning Progressions Scale | ||||
|
Mathematics
|
Reading
|
|||
|---|---|---|---|---|
| Mean | SE | Mean | SE | |
| GMB 3 | −2.339 | 0.036 | −1.359 | 0.030 |
| KEN 6 | −0.438 | 0.034 | 0.156 | 0.039 |
| LSO 7 | −0.853 | 0.043 | −0.391 | 0.049 |
| ZMB 4 | −2.666 | 0.035 | −1.648 | 0.032 |
| ZMB 7 | −1.050 | 0.037 | −0.523 | 0.042 |
Mean performance differs sharply by country–grade group. On mathematics, Kenya Grade 6 has the highest mean (\(-0.44\)), followed by Lesotho Grade 7 (\(-0.85\)) and Zambia Grade 7 (\(-1.05\)), while Zambia Grade 4 (\(-2.67\)) and The Gambia Grade 3 (\(-2.34\)) are much lower. Reading follows a similar ordering, with Kenya the only group whose mean reading score is positive (\(0.16\)). These contrasts partly reflect grade level as well as system differences, which is why separating Zambia’s Grade 4 and Grade 7 samples matters.
6.7.5 Moving beyond means: Summary statistics with Rrepest
Rrepest can estimate a range of statistics beyond the mean, such as
percentiles and standard deviations. Below we summarise the mathematics
proficiency distribution for Grade 3 students in The Gambia by gender.
result_summ <- Rrepest(
data = subset(ampl_dat, cnt == "GMB"),
svy = "SVY",
est = est(
c("mean", "std", "quant", 0.05, "quant", 0.25, "quant", 0.5, "quant", 0.75, "quant", 0.95),
target = "pv@_math"
),
by = "girl",
cm.weights = cm,
var.factor = 135,
n.pvs = 5
)
print(as.data.frame(result_summ))## girl b.mean.pv@_math se.mean.pv@_math b.std.pv@_math se.std.pv@_math b.quant00.pv@_math se.quant00.pv@_math b.quant025.pv@_math se.quant025.pv@_math
## 1 0 -2.371841 0.03931614 1.065351 0.03348136 -3.850363 0.03842642 -3.121192 0.04034239
## 2 1 -2.311790 0.04594274 1.090902 0.03767271 -3.820493 0.04860671 -3.092192 0.04298113
## b.quant05.pv@_math se.quant05.pv@_math b.quant075.pv@_math se.quant075.pv@_math b.quant095.pv@_math se.quant095.pv@_math
## 1 -2.522535 0.06196889 -1.790534 0.08457232 -0.3191085 0.1368639
## 2 -2.481752 0.05041328 -1.675401 0.07123222 -0.2163765 0.1640926
We present the {Rrepest} output above in a reader-friendly format using
the {gt} package.
as.data.frame(result_summ) |>
mutate(
sex = if_else(as.integer(girl) == 1, "Girls", "Boys"),
mean_b = `b.mean.pv@_math`,
mean_se = `se.mean.pv@_math`,
sd_b = `b.std.pv@_math`,
sd_se = `se.std.pv@_math`,
p50_b = `b.quant05.pv@_math`,
p50_se = `se.quant05.pv@_math`,
p75_b = `b.quant075.pv@_math`,
p75_se = `se.quant075.pv@_math`,
p95_b = `b.quant095.pv@_math`,
p95_se = `se.quant095.pv@_math`
) |>
select(sex, mean_b, mean_se, sd_b, sd_se, p50_b, p50_se, p75_b, p75_se, p95_b, p95_se) |>
gt(rowname_col = "sex") |>
tab_header(
title = "Mathematics distribution by gender",
subtitle = "The Gambia (Grade 3)"
) |>
tab_spanner(label = "Mean", columns = c(mean_b, mean_se)) |>
tab_spanner(label = "SD", columns = c(sd_b, sd_se)) |>
tab_spanner(label = "Median", columns = c(p50_b, p50_se)) |>
tab_spanner(label = "75th pct", columns = c(p75_b, p75_se)) |>
tab_spanner(label = "95th pct", columns = c(p95_b, p95_se)) |>
cols_label(
mean_b = "Est.", mean_se = "SE",
sd_b = "Est.", sd_se = "SE",
p50_b = "Est.", p50_se = "SE",
p75_b = "Est.", p75_se = "SE",
p95_b = "Est.", p95_se = "SE"
) |>
fmt_number(decimals = 3) |>
tab_style(
style = cell_text(weight = "bold"),
locations = cells_column_labels()
)| Mathematics distribution by gender | ||||||||||
| The Gambia (Grade 3) | ||||||||||
|
Mean
|
SD
|
Median
|
75th pct
|
95th pct
|
||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Est. | SE | Est. | SE | Est. | SE | Est. | SE | Est. | SE | |
| Boys | −2.372 | 0.039 | 1.065 | 0.033 | −2.523 | 0.062 | −1.791 | 0.085 | −0.319 | 0.137 |
| Girls | −2.312 | 0.046 | 1.091 | 0.038 | −2.482 | 0.050 | −1.675 | 0.071 | −0.216 | 0.164 |
Looking beyond average performance shows that the gender gap is small across the distribution. Boys and girls have similar spread (SD ≈ \(1.07\) vs \(1.09\)). At the median, girls score \(-2.48\) and boys \(-2.52\); at the 75th percentile the gap is a little larger (\(-1.68\) vs \(-1.79\)). Even the strongest performers remain well below zero on the LPS: the 95th percentile is about \(-0.22\) for girls and \(-0.32\) for boys.
To examine the shape of the distribution more closely, we can also
estimate selected quantile cut-points (here the 20th, 40th, 60th, and
80th percentiles), which play a similar role to Stata’s
quantiletable quintile cuts.
result_quant <- Rrepest(
data = subset(ampl_dat, cnt == "GMB"),
svy = "SVY",
est = est(
c("quant", 0.2, "quant", 0.4, "quant", 0.6, "quant", 0.8),
target = c("pv@_math", "pv@_read")
),
by = "girl",
cm.weights = cm,
var.factor = 135,
n.pvs = 5
)
print(as.data.frame(result_quant))## girl b.quant02.pv@_math se.quant02.pv@_math b.quant04.pv@_math se.quant04.pv@_math b.quant06.pv@_math se.quant06.pv@_math b.quant08.pv@_math
## 1 0 -3.248062 0.04803454 -2.769858 0.04997564 -2.262513 0.04622875 -1.538429
## 2 1 -3.219790 0.04848990 -2.735343 0.05043019 -2.203289 0.06672874 -1.434276
## se.quant08.pv@_math b.quant02.pv@_read se.quant02.pv@_read b.quant04.pv@_read se.quant04.pv@_read b.quant06.pv@_read se.quant06.pv@_read
## 1 0.08995220 -2.047014 0.03274443 -1.702325 0.02887452 -1.340615 0.03112681
## 2 0.06044208 -2.024973 0.03415830 -1.640170 0.03633984 -1.266828 0.04618761
## b.quant08.pv@_read se.quant08.pv@_read
## 1 -0.7967462 0.06035399
## 2 -0.6501527 0.06956877
We present the {Rrepest} output above in a reader-friendly format using
the {gt} package.
as.data.frame(result_quant) |>
mutate(
sex = if_else(as.integer(girl) == 1, "Girls", "Boys"),
math20 = `b.quant02.pv@_math`,
math80 = `b.quant08.pv@_math`,
read20 = `b.quant02.pv@_read`,
read80 = `b.quant08.pv@_read`
) |>
select(sex, math20, math80, read20, read80) |>
gt(rowname_col = "sex") |>
tab_header(
title = "Selected proficiency quantiles by gender",
subtitle = "The Gambia (Grade 3) - 20th and 80th percentiles"
) |>
tab_spanner(label = "Mathematics", columns = c(math20, math80)) |>
tab_spanner(label = "Reading", columns = c(read20, read80)) |>
cols_label(
math20 = "20th",
math80 = "80th",
read20 = "20th",
read80 = "80th"
) |>
fmt_number(decimals = 3) |>
tab_style(
style = cell_text(weight = "bold"),
locations = cells_column_labels()
)| Selected proficiency quantiles by gender | ||||
| The Gambia (Grade 3) - 20th and 80th percentiles | ||||
|
Mathematics
|
Reading
|
|||
|---|---|---|---|---|
| 20th | 80th | 20th | 80th | |
| Boys | −3.248 | −1.538 | −2.047 | −0.797 |
| Girls | −3.220 | −1.434 | −2.025 | −0.650 |
These cut-points summarise ordered parts of the score distribution. For example, among boys the mathematics 20th percentile is about \(-3.25\) and the 80th percentile about \(-1.54\); girls’ corresponding cut-points are slightly higher (about \(-3.22\) and \(-1.43\)), which matches the modest advantage for girls seen in the means and percentiles.
Other useful Rrepest statistics include "freq", "corr", and
"lm".
6.7.6 Estimating AMPL Proficiency Levels
AMPL established cut scores that correspond to the minimum proficiency levels required at the end of lower primary school (MPLa) and at the end of primary school (MPLb), for both mathematics and reading.
The cut scores for mathematics are defined by AMPL as follows:
| AMPL Proficiency Level | Score |
|---|---|
| Below MPLa | \(< -1.74\) |
| Between MPLa & MPLb | \([-1.74, -0.04)\) |
| Above MPLb | \(\geq -0.04\) |
These cut scores for reading and mathematics were established on the Learning Progressions Scale (LPS) with an international standard setting exercise undertaken in 2022. A pairwise comparison method (PCM) study was then used to confirm and validate the locations of those cut scores.
There is a variable for each plausible value that records this cut-score
information for both mathematics (pl_pv*_math) and reading
(pl_pv*_read). The file also supplies ready-made binary indicators for
reaching MPLa (pl_psa*_math, pl_psa*_read) and MPLb (pl_psb*_math,
pl_psb*_read).
## # A tibble: 3 × 2
## pl_pv1_read n
## <dbl+lbl> <int>
## 1 1 [Below standard a] 11986
## 2 2 [Above a standard and below standard b] 8049
## 3 3 [Above standard b] 2179
## # A tibble: 3 × 2
## pl_pv1_math n
## <dbl+lbl> <int>
## 1 1 [Below standard a] 9281
## 2 2 [Above a standard and below standard b] 9439
## 3 3 [Above standard b] 3494
Next, we estimate the proportion of students reaching the minimum
proficiency level required at the end of lower primary school (MPLa).
The student file already includes binary indicators for reaching MPLa
for each plausible value: pl_psa1_math–pl_psa5_math and
pl_psa1_read–pl_psa5_read (coded \(1\) = at or above MPLa, \(0\) =
below).
## # A tibble: 2 × 2
## pl_psa1_math n
## <dbl+lbl> <int>
## 1 0 [Below standard a] 9281
## 2 1 [Above standard a] 12933
Now we estimate the MPLa proportions for reading and mathematics in The Gambia.
result_mpla <- Rrepest(
data = subset(ampl_dat, cnt == "GMB"),
svy = "SVY",
est = est("mean", target = c("pl_psa@_read", "pl_psa@_math")),
cm.weights = cm,
var.factor = 135,
n.pvs = 5
)
print(result_mpla)## # A tibble: 1 × 5
## . `b.mean.pl_psa@_read` `se.mean.pl_psa@_read` `b.mean.pl_psa@_math` `se.mean.pl_psa@_math`
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 Total 0.215 0.0137 0.258 0.0128
We present the {Rrepest} output above in a reader-friendly format using
the {gt} package.
as.data.frame(result_mpla) |>
transmute(
read_pct = `b.mean.pl_psa@_read` * 100,
read_se = `se.mean.pl_psa@_read` * 100,
math_pct = `b.mean.pl_psa@_math` * 100,
math_se = `se.mean.pl_psa@_math` * 100
) |>
gt() |>
tab_header(
title = "Proportion reaching MPLa",
subtitle = "The Gambia (Grade 3)"
) |>
tab_spanner(label = "Reading", columns = c(read_pct, read_se)) |>
tab_spanner(label = "Mathematics", columns = c(math_pct, math_se)) |>
cols_label(
read_pct = "Percent",
read_se = "SE",
math_pct = "Percent",
math_se = "SE"
) |>
fmt_number(decimals = 1) |>
tab_style(
style = cell_text(weight = "bold"),
locations = cells_column_labels()
)| Proportion reaching MPLa | |||
| The Gambia (Grade 3) | |||
|
Reading
|
Mathematics
|
||
|---|---|---|---|
| Percent | SE | Percent | SE |
| 21.5 | 1.4 | 25.8 | 1.3 |
In The Gambia, about \(22\%\) of Grade 3 students reach reading MPLa (SE = \(0.014\)) and about \(26\%\) reach mathematics MPLa (SE \(= 0.013\)). In other words, roughly three-quarters of students are still below the end-of-lower-primary minimum proficiency threshold in each domain.
6.7.7 Testing for differences between groups
The table below highlights the distinction between two common types of comparisons in AMPLab data: within-country comparisons (e.g., boys versus girls in the same country) and between-country comparisons (e.g., Lesotho versus Kenya). Because these comparisons involve different survey structures, they require slightly different analytical approaches.
The examples that follow demonstrate how to test for differences in
achievement between groups using regression models in Rrepest.
| Comparison Type | What it measures | Survey design impact |
|---|---|---|
| Within-Country (e.g., Boys vs. Girls in Zambia) | The gap between two demographic subgroups who share the same sampling strata, schools, and teachers. | High covariance. Because groups are clustered together in the same schools, their errors are correlated. |
| Between-Country (e.g., Lesotho vs Kenya) | The gap between two entirely independent populations with completely separate sampling frames | Zero covariance. Sampling units in Country A have no mathematical relationship to sampling units in Country B |
In Rrepest you should use over for within-country comparisons and
by for between-country comparisons. You must NOT use over for
countries. When using svy = "SVY", the over option is designed for
within-country subgroup comparisons only. Using regression achieves the
same goal and works correctly with the SVY option.
6.7.7.1 Testing for differences between boys and girls
Are there differences in the proportion of students reaching reading MPLa
between girls and boys in Kenya? We can use linear regression to test
for this. The girl indicator is already available from earlier in the
chapter.
result_kenya <- Rrepest(
data = subset(ampl_dat, cnt == "KEN"),
svy = "SVY",
est = est("lm", target = "pl_psa@_read", regressor = "girl"),
cm.weights = cm,
var.factor = 135,
n.pvs = 5
)
print(result_kenya)## # A tibble: 1 × 7
## . b.reg_pl_psa@_read.interce…¹ se.reg_pl_psa@_read.…² b.reg_pl_psa@_read.g…³ se.reg_pl_psa@_read.…⁴ b.reg_pl_psa@_read.r…⁵ se.reg_pl_psa@_read.…⁶
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Total 0.749 0.0164 0.0671 0.0168 0.00664 0.00327
## # ℹ abbreviated names: ¹`b.reg_pl_psa@_read.intercept`, ²`se.reg_pl_psa@_read.intercept`, ³`b.reg_pl_psa@_read.girl`, ⁴`se.reg_pl_psa@_read.girl`,
## # ⁵`b.reg_pl_psa@_read.rsqr`, ⁶`se.reg_pl_psa@_read.rsqr`
We present the {Rrepest} output above in a reader-friendly format using
the {gt} package.
rrepest_lm_table(
result_kenya,
b_cols = c(
"b.reg_pl_psa@_read.intercept",
"b.reg_pl_psa@_read.girl",
"b.reg_pl_psa@_read.rsqr"
),
se_cols = c(
"se.reg_pl_psa@_read.intercept",
"se.reg_pl_psa@_read.girl",
"se.reg_pl_psa@_read.rsqr"
),
terms = c("(Intercept)", "Girl (vs Boy)", "R-squared")
) |>
show_lm_gt(
title = "Gender difference in reading MPLa",
subtitle = "Kenya — weighted linear regression",
source_note = "Dependent variable: pl_psa*_read (1 = at or above MPLa). Girl = 1, Boy = 0."
)| Gender difference in reading MPLa | ||
| Kenya — weighted linear regression | ||
| Term | Estimate (SE) | t |
|---|---|---|
| (Intercept) | 0.749 (0.016) | 45.52 |
| Girl (vs Boy) | 0.067 (0.017) | 3.98 |
| R-squared | 0.007 (0.003) | 2.03 |
| Dependent variable: pl_psa*_read (1 = at or above MPLa). Girl = 1, Boy = 0. | ||
In Kenya, girls are about \(6.7\) percentage points more likely than boys to reach reading MPLa (coefficient \(= 0.067\); SE \(= 0.017\)). The intercept implies that about \(75\%\) of boys meet reading MPLa, so the girl estimate corresponds to roughly \(82\%\) among girls. The \(95\%\) confidence interval for the girl coefficient is \(0.034\) to \(0.100\).
Rrepest does not always print p-values in the default table, but you
can compute a two-sided p-value from the coefficient and standard error.
# Two-sided p-value from z (large-sample normal approximation)
2 * pnorm(-abs(0.0670775 / 0.0168496))## [1] 6.863893e-05
A p-value near \(0\) means a gap of this size would be highly unusual if there were no true gender difference in the population. Substantively, Kenya shows a clear within-country advantage for girls on reading MPLa, even though a large share of both girls and boys already meet the threshold.
6.7.7.2 Testing for differences between countries
Next, we ask whether mean mathematics scores differ between students enrolled in Grade 7 in Zambia and Lesotho. Create a dummy variable that equals \(1\) for Grade 7 Lesotho, \(0\) for Grade 7 Zambia, and missing for everyone else.
ampl_dat <- ampl_dat |>
mutate(
grade_num = as.integer(haven::zap_labels(grade_stf)),
LSO_7 = case_when(
cnt == "LSO" & grade_num == 7 ~ 1L,
cnt == "ZMB" & grade_num == 7 ~ 0L,
TRUE ~ NA_integer_
)
)result_lso_zmb <- Rrepest(
data = subset(ampl_dat, !is.na(LSO_7)),
svy = "SVY",
est = est("lm", target = "pv@_math", regressor = "LSO_7"),
cm.weights = cm,
var.factor = 135,
n.pvs = 5
)
print(result_lso_zmb)## # A tibble: 1 × 7
## . `b.reg_pv@_math.intercept` se.reg_pv@_math.interce…¹ `b.reg_pv@_math.lso_7` se.reg_pv@_math.lso_…² `b.reg_pv@_math.rsqr` `se.reg_pv@_math.rsqr`
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Total -1.05 0.0374 0.197 0.0582 0.00240 0.00147
## # ℹ abbreviated names: ¹`se.reg_pv@_math.intercept`, ²`se.reg_pv@_math.lso_7`
We present the {Rrepest} output above in a reader-friendly format using
the {gt} package.
rrepest_lm_table(
result_lso_zmb,
b_cols = c(
"b.reg_pv@_math.intercept",
"b.reg_pv@_math.lso_7",
"b.reg_pv@_math.rsqr"
),
se_cols = c(
"se.reg_pv@_math.intercept",
"se.reg_pv@_math.lso_7",
"se.reg_pv@_math.rsqr"
),
terms = c("(Intercept)", "Lesotho Grade 7 (vs Zambia Grade 7)", "R-squared")
) |>
show_lm_gt(
title = "Mathematics difference: Lesotho vs Zambia (Grade 7)",
subtitle = "Weighted linear regression on plausible values",
source_note = "Dependent variable: mathematics plausible values. LSO_7 = 1 for Lesotho Grade 7."
)| Mathematics difference: Lesotho vs Zambia (Grade 7) | ||
| Weighted linear regression on plausible values | ||
| Term | Estimate (SE) | t |
|---|---|---|
| (Intercept) | -1.050 (0.037) | −28.07 |
| Lesotho Grade 7 (vs Zambia Grade 7) | 0.197 (0.058) | 3.39 |
| R-squared | 0.002 (0.001) | 1.63 |
| Dependent variable: mathematics plausible values. LSO_7 = 1 for Lesotho Grade 7. | ||
Lesotho Grade 7 students score about \(0.20\) LPS points higher in mathematics than Zambia Grade 7 students (SE \(= 0.058\); \(95\%\) CI: \(0.08\) to \(0.31\)). The intercept of \(-1.05\) is the Zambia Grade 7 mean. The gap is statistically clear and matches the earlier country–grade means.
We can also compare a lower part of the distribution using the 25th
percentile by country group. (Stata’s qreg estimates a quantile
regression coefficient directly; here we report the 25th percentiles for
each Grade 7 sample and interpret their difference.)
result_lso_zmb_q25 <- Rrepest(
data = subset(ampl_dat, !is.na(LSO_7)),
svy = "SVY",
est = est(c("quant", 0.25), target = "pv@_math"),
by = "LSO_7",
cm.weights = cm,
var.factor = 135,
n.pvs = 5
)
print(result_lso_zmb_q25)## # A tibble: 2 × 3
## LSO_7 `b.quant025.pv@_math` `se.quant025.pv@_math`
## <chr> <dbl> <dbl>
## 1 0 -1.72 0.0324
## 2 1 -1.50 0.0420
We present the {Rrepest} output above in a reader-friendly format using
the {gt} package.
as.data.frame(result_lso_zmb_q25) |>
mutate(
group = if_else(as.integer(LSO_7) == 1, "Lesotho Grade 7", "Zambia Grade 7"),
q25 = `b.quant025.pv@_math`,
se = `se.quant025.pv@_math`
) |>
select(group, q25, se) |>
gt(rowname_col = "group") |>
tab_header(
title = "25th percentile of mathematics proficiency",
subtitle = "Lesotho vs Zambia (Grade 7)"
) |>
cols_label(q25 = "25th percentile", se = "SE") |>
fmt_number(decimals = 3) |>
tab_style(
style = cell_text(weight = "bold"),
locations = cells_column_labels()
)| 25th percentile of mathematics proficiency | ||
| Lesotho vs Zambia (Grade 7) | ||
| 25th percentile | SE | |
|---|---|---|
| Zambia Grade 7 | −1.723 | 0.032 |
| Lesotho Grade 7 | −1.498 | 0.042 |
At the 25th percentile, Zambia Grade 7 scores about \(-1.72\) and Lesotho Grade 7 about \(-1.50\), a gap of roughly \(0.22\) LPS points. The gap is similar in size to the mean difference, which suggests that Lesotho’s higher mathematics performance is not confined to students near the centre of the distribution.
At Grade 7, the relevant minimum proficiency level is MPLb. The student
file already includes binary indicators for reaching MPLb for each
plausible value: pl_psb1_math–pl_psb5_math and
pl_psb1_read–pl_psb5_read (coded \(1\) = at or above MPLb, \(0\) =
below).
Are there differences in the percentage reaching the end-of-primary minimum proficiency level (MPLb) for mathematics?
result_mplb <- Rrepest(
data = subset(ampl_dat, !is.na(LSO_7)),
svy = "SVY",
est = est("lm", target = "pl_psb@_math", regressor = "LSO_7"),
cm.weights = cm,
var.factor = 135,
n.pvs = 5
)
print(result_mplb)## # A tibble: 1 × 7
## . b.reg_pl_psb@_math.interce…¹ se.reg_pl_psb@_math.…² b.reg_pl_psb@_math.l…³ se.reg_pl_psb@_math.…⁴ b.reg_pl_psb@_math.r…⁵ se.reg_pl_psb@_math.…⁶
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Total 0.161 0.0139 0.0359 0.0211 0.000608 0.000725
## # ℹ abbreviated names: ¹`b.reg_pl_psb@_math.intercept`, ²`se.reg_pl_psb@_math.intercept`, ³`b.reg_pl_psb@_math.lso_7`, ⁴`se.reg_pl_psb@_math.lso_7`,
## # ⁵`b.reg_pl_psb@_math.rsqr`, ⁶`se.reg_pl_psb@_math.rsqr`
We present the {Rrepest} output above in a reader-friendly format using
the {gt} package.
rrepest_lm_table(
result_mplb,
b_cols = c(
"b.reg_pl_psb@_math.intercept",
"b.reg_pl_psb@_math.lso_7",
"b.reg_pl_psb@_math.rsqr"
),
se_cols = c(
"se.reg_pl_psb@_math.intercept",
"se.reg_pl_psb@_math.lso_7",
"se.reg_pl_psb@_math.rsqr"
),
terms = c("(Intercept)", "Lesotho Grade 7 (vs Zambia Grade 7)", "R-squared")
) |>
show_lm_gt(
title = "Mathematics MPLb difference: Lesotho vs Zambia (Grade 7)",
subtitle = "Weighted linear regression",
source_note = "Dependent variable: pl_psb*_math (1 = at or above MPLb). Intercept = Zambia Grade 7 rate."
)| Mathematics MPLb difference: Lesotho vs Zambia (Grade 7) | ||
| Weighted linear regression | ||
| Term | Estimate (SE) | t |
|---|---|---|
| (Intercept) | 0.161 (0.014) | 11.56 |
| Lesotho Grade 7 (vs Zambia Grade 7) | 0.036 (0.021) | 1.70 |
| R-squared | 0.001 (0.001) | 0.84 |
| Dependent variable: pl_psb*_math (1 = at or above MPLb). Intercept = Zambia Grade 7 rate. | ||
About \(16\%\) of Zambia Grade 7 students reach mathematics MPLb (intercept \(= 0.161\)). The Lesotho coefficient is \(0.036\) (SE \(= 0.021\)), so Lesotho’s estimated rate is about \(20\%\). The \(95\%\) CI for the difference (\(-0.006\) to \(0.077\)) includes zero, so unlike the mean score gap, the MPLb gap is not statistically clear at conventional levels.
If you prefer an explicit p-value:
# Two-sided p-value from z (large-sample normal approximation)
2 * pnorm(-abs(0.0359181 / 0.0211479))## [1] 0.08942777
A p-value around \(0.09\) means this MPLb difference would not be unusual if the two Grade 7 populations had the same proficiency rate. The mean score contrast is clearer than the binary MPLb contrast because many students in both countries remain below the MPLb cut score.
6.7.8 Multivariate regression
In this example we look at student and family factors associated with
learning in Lesotho. We start with the association between reading
scores (pv@_read), gender, and the household wealth index (wealth).
ampl_dat |>
summarise(
n = n(),
min_wealth = min(wealth, na.rm = TRUE),
max_wealth = max(wealth, na.rm = TRUE),
n_missing_code = sum(wealth == 999, na.rm = TRUE)
)## # A tibble: 1 × 4
## n min_wealth max_wealth n_missing_code
## <int> <dbl+lbl> <dbl+lbl> <int>
## 1 22214 -3.79 999 [Missing] 346
Always exclude observations with missing wealth codes (wealth = 999).
Including the placeholder value as if it were a real score would distort
the regression.
result_multi_read <- Rrepest(
data = subset(ampl_dat, cnt == "LSO" & wealth < 999),
svy = "SVY",
est = est("lm", target = "pv@_read", regressor = c("girl", "wealth")),
cm.weights = cm,
var.factor = 135,
n.pvs = 5
)
print(result_multi_read)## # A tibble: 1 × 9
## . `b.reg_pv@_read.intercept` se.reg_pv@_read.interce…¹ `b.reg_pv@_read.girl` `se.reg_pv@_read.girl` b.reg_pv@_read.wealt…² se.reg_pv@_read.weal…³
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Total -0.447 0.0517 0.260 0.0397 0.159 0.0287
## # ℹ abbreviated names: ¹`se.reg_pv@_read.intercept`, ²`b.reg_pv@_read.wealth`, ³`se.reg_pv@_read.wealth`
## # ℹ 2 more variables: `b.reg_pv@_read.rsqr` <dbl>, `se.reg_pv@_read.rsqr` <dbl>
We present the {Rrepest} output above in a reader-friendly format using
the {gt} package.
rrepest_lm_table(
result_multi_read,
b_cols = c(
"b.reg_pv@_read.intercept",
"b.reg_pv@_read.girl",
"b.reg_pv@_read.wealth",
"b.reg_pv@_read.rsqr"
),
se_cols = c(
"se.reg_pv@_read.intercept",
"se.reg_pv@_read.girl",
"se.reg_pv@_read.wealth",
"se.reg_pv@_read.rsqr"
),
terms = c("(Intercept)", "Girl (vs Boy)", "Household wealth", "R-squared")
) |>
show_lm_gt(
title = "Predictors of reading proficiency",
subtitle = "Lesotho — weighted linear regression",
source_note = "Dependent variable: reading plausible values. Restricted to wealth < 999."
)| Predictors of reading proficiency | ||
| Lesotho — weighted linear regression | ||
| Term | Estimate (SE) | t |
|---|---|---|
| (Intercept) | -0.447 (0.052) | −8.63 |
| Girl (vs Boy) | 0.260 (0.040) | 6.55 |
| Household wealth | 0.159 (0.029) | 5.53 |
| R-squared | 0.073 (0.019) | 3.76 |
| Dependent variable: reading plausible values. Restricted to wealth < 999. | ||
In Lesotho, girls score about \(0.26\) LPS points higher in reading than boys after controlling for wealth (SE \(= 0.040\); \(95\%\) CI: \(0.18\) to \(0.34\)). A one-unit increase in the wealth index is associated with about \(0.16\) points higher reading (SE \(= 0.029\); \(95\%\) CI: \(0.10\) to \(0.22\)). Both associations are statistically clear. The intercept (\(-0.45\)) is the expected reading score for boys with wealth equal to zero on the index.
Next, we add parents’ highest education level and switch the outcome to
mathematics MPLb, so the coefficients are on the probability scale.
Because Rrepest treats named regressors as continuous unless you
create separate indicator variables, we build ISCED dummies with
“did not complete ISCED level 1” (hisced == 0) as the reference group.
## # A tibble: 7 × 2
## hisced n
## <dbl+lbl> <int>
## 1 0 [Did not complete ISCED level 1] 1480
## 2 1 [ISCED level 1] 3434
## 3 2 [ISCED level 2] 3461
## 4 3 [ISCED level 3] 5400
## 5 4 [ISCED level 4 or 5] 3328
## 6 5 [ISCED level 6 or higher] 4304
## 7 9 [Missing/Unknown] 807
ampl_dat_lso <- ampl_dat |>
filter(cnt == "LSO", wealth < 999, hisced < 9) |>
mutate(
hisced1 = as.integer(hisced == 1),
hisced2 = as.integer(hisced == 2),
hisced3 = as.integer(hisced == 3),
hisced4 = as.integer(hisced == 4),
hisced5 = as.integer(hisced == 5)
)
result_multi_mplb <- Rrepest(
data = ampl_dat_lso,
svy = "SVY",
est = est(
"lm",
target = "pl_psb@_math",
regressor = c("girl", "wealth", "hisced1", "hisced2", "hisced3", "hisced4", "hisced5")
),
cm.weights = cm,
var.factor = 135,
n.pvs = 5
)
print(result_multi_mplb)## # A tibble: 1 × 19
## . b.reg_pl_psb@_math.interce…¹ se.reg_pl_psb@_math.…² b.reg_pl_psb@_math.g…³ se.reg_pl_psb@_math.…⁴ b.reg_pl_psb@_math.w…⁵ se.reg_pl_psb@_math.…⁶
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Total 0.0914 0.0320 0.0624 0.0148 0.0313 0.00840
## # ℹ abbreviated names: ¹`b.reg_pl_psb@_math.intercept`, ²`se.reg_pl_psb@_math.intercept`, ³`b.reg_pl_psb@_math.girl`, ⁴`se.reg_pl_psb@_math.girl`,
## # ⁵`b.reg_pl_psb@_math.wealth`, ⁶`se.reg_pl_psb@_math.wealth`
## # ℹ 12 more variables: `b.reg_pl_psb@_math.hisced1` <dbl>, `se.reg_pl_psb@_math.hisced1` <dbl>, `b.reg_pl_psb@_math.hisced2` <dbl>,
## # `se.reg_pl_psb@_math.hisced2` <dbl>, `b.reg_pl_psb@_math.hisced3` <dbl>, `se.reg_pl_psb@_math.hisced3` <dbl>, `b.reg_pl_psb@_math.hisced4` <dbl>,
## # `se.reg_pl_psb@_math.hisced4` <dbl>, `b.reg_pl_psb@_math.hisced5` <dbl>, `se.reg_pl_psb@_math.hisced5` <dbl>, `b.reg_pl_psb@_math.rsqr` <dbl>,
## # `se.reg_pl_psb@_math.rsqr` <dbl>
We present the {Rrepest} output above in a reader-friendly format using
the {gt} package.
rrepest_lm_table(
result_multi_mplb,
b_cols = c(
"b.reg_pl_psb@_math.intercept",
"b.reg_pl_psb@_math.girl",
"b.reg_pl_psb@_math.wealth",
"b.reg_pl_psb@_math.hisced1",
"b.reg_pl_psb@_math.hisced2",
"b.reg_pl_psb@_math.hisced3",
"b.reg_pl_psb@_math.hisced4",
"b.reg_pl_psb@_math.hisced5",
"b.reg_pl_psb@_math.rsqr"
),
se_cols = c(
"se.reg_pl_psb@_math.intercept",
"se.reg_pl_psb@_math.girl",
"se.reg_pl_psb@_math.wealth",
"se.reg_pl_psb@_math.hisced1",
"se.reg_pl_psb@_math.hisced2",
"se.reg_pl_psb@_math.hisced3",
"se.reg_pl_psb@_math.hisced4",
"se.reg_pl_psb@_math.hisced5",
"se.reg_pl_psb@_math.rsqr"
),
terms = c(
"(Intercept)",
"Girl (vs Boy)",
"Household wealth",
"ISCED 1 (vs below ISCED 1)",
"ISCED 2 (vs below ISCED 1)",
"ISCED 3 (vs below ISCED 1)",
"ISCED 4 (vs below ISCED 1)",
"ISCED 5 (vs below ISCED 1)",
"R-squared"
)
) |>
show_lm_gt(
title = "Predictors of mathematics MPLb",
subtitle = "Lesotho - weighted linear regression",
source_note = "Dependent variable: pl_psb*_math (1 = at or above MPLb). Restricted to wealth < 999 and hisced < 9."
)| Predictors of mathematics MPLb | ||
| Lesotho - weighted linear regression | ||
| Term | Estimate (SE) | t |
|---|---|---|
| (Intercept) | 0.091 (0.032) | 2.86 |
| Girl (vs Boy) | 0.062 (0.015) | 4.21 |
| Household wealth | 0.031 (0.008) | 3.72 |
| ISCED 1 (vs below ISCED 1) | 0.012 (0.032) | 0.37 |
| ISCED 2 (vs below ISCED 1) | 0.062 (0.032) | 1.91 |
| ISCED 3 (vs below ISCED 1) | 0.114 (0.041) | 2.77 |
| ISCED 4 (vs below ISCED 1) | 0.226 (0.043) | 5.31 |
| ISCED 5 (vs below ISCED 1) | 0.185 (0.050) | 3.70 |
| R-squared | 0.073 (0.018) | 3.98 |
| Dependent variable: pl_psb*_math (1 = at or above MPLb). Restricted to wealth < 999 and hisced < 9. | ||
After controlling for wealth and parental education, girls remain about
\(6.2\) percentage points more likely than boys to reach mathematics MPLb
(SE \(= 0.015\)). Wealth retains a positive association (\(0.031\) per unit;
SE \(= 0.008\)). Relative to parents who did not complete ISCED level 1,
higher parental education is associated with higher MPLb rates,
especially at ISCED levels 3–5 (coefficients from about \(0.11\) to
\(0.23\)). The lowest education contrast (ISCED 1) is not statistically
distinguishable from the reference group. Restricting to hisced < 9
drops missing/unknown parental education codes, which is important for
valid inference.