This is a supplementary material for the paper titled ‘A multilevel analysis on the predictors of client satisfaction with family planning services in Ethiopia: Evidence from the Ethiopian Service Provision Assessment (ESPA) 2021/22’. It will explore the steps taken during the analysis and reports additional information that might be of interest for the reader.
The data was accessed through the demographic and health survey website.
For the paper two data sets were merged; namely the client data-set (containing client-provider observations and client exit interviews) and the facility data-set (containing facility inventory data). This are named ‘fpclient’ and’fpfacility’ respectively.
We used a left_join with inv_id as unique identifier so the unit of analysis remained as the number of clients.
alldata <- fpclient %>%
left_join(fpfacility, by='inv_id')
The variables included in our analysis are described in detail as an appendix in the paper.
We initially created a composite variable to measure client satisfaction. This composite variable was constructed using survey question number 202 (refer to the final SPA report), which inquiries about common problems clients face at health facilities.
attach(alldata)
overall_client_satisfaction <- data.frame(c502a,c502b,c502c,c502e,
c502f,c502g,c502h,c502i,c502j,c502k,
c502l,c_overall_satisfaction)
overall_client_satisfaction[overall_client_satisfaction == 8] <- NA
overall_client_satisfaction[overall_client_satisfaction == 9] <- NA
overall_client_satisfaction <- na.omit(overall_client_satisfaction)
composite_client_satisfaction <- overall_client_satisfaction %>%
select(c502a, c502b, c502c, c502e, c502f, c502g, c502h, c502i, c502j, c502k, c502l)
pca_composite <- prcomp(composite_client_satisfaction, scale. = TRUE)
pc1 <- pca_composite$x[, 1]
library(factoextra)
fviz_eig(pca_composite, addlabels = TRUE, main = "Variance Explained by Principal Components")
However, once the principal components of the composite measure were identified, a spearman correlation test was conducted to see if the composite variable is actually measuring in line with the overall client satisfaction.
single_variable <- overall_client_satisfaction %>%
pull(c_overall_satisfaction)
if (length(pc1) == length(single_variable)) {
cor_spearman <- cor(pc1, single_variable, method = "spearman")
print(paste("Spearman Rank Correlation: ", cor_spearman))
} else {
stop("Dimensions of PC1 and c_overall_satisfaction do not match!")
}
## [1] "Spearman Rank Correlation: 0.269412558848388"
The very low correlation between the composite measure and the overall client satisfaction indicates the composite variable is measuring a dimension of patient service that is not correlated with client satisfaction. As a result, a decision to use the single variable measure was made.
In our model client satisfaction is measured using a 4-level likert scale that has been dicothomized into more satisfied and less satisfied.
#Creating facility weight
weight_list <- data.frame(final_df1$f_weight, final_df1$inv_id)
weight_list <- unique(weight_list)
facility_list <- data.frame(final_df1$f_number, final_df1$inv_id)
facility_list<- unique(facility_list)
weight_facility <- weight_list %>%
inner_join(facility_list, by='final_df1.inv_id')
n1 <- nrow(weight_facility)
weight_facility$final_df1.f_weight <- n1 * (weight_facility$final_df1.f_weight /1000000) /
sum(weight_facility$final_df1.f_weight /1000000)
sum(weight_facility$final_df1.f_weight)
## [1] 529
# Creating client-weight
n <- nrow(final_df1)
final_df1$c_houseweight <- n * (final_df1$weight/1000000) / sum(final_df1$weight/1000000)
sum(final_df1$c_houseweight)
## [1] 2071
A descriptive table before and after applying weights is presented in the paper.
nullmodel_adapt <- mixed_model(fixed = c_overall_satisfaction ~1 ,
random = ~ 1 |f_number,
data = final_df1,
family = binomial("logit"),
weights = weight_facility$final_df1.f_weight)
summary(nullmodel_adapt)
##
## Call:
## mixed_model(fixed = c_overall_satisfaction ~ 1, random = ~1 |
## f_number, data = final_df1, family = binomial("logit"), weights = weight_facility$final_df1.f_weight)
##
## Data Descriptives:
## Number of Observations: 2071
## Number of Groups: 529
##
## Model:
## family: binomial
## link: logit
##
## Fit statistics:
## log.Lik AIC BIC
## -963.5288 1931.058 1939.6
##
## Random effects covariance matrix:
## StdDev
## (Intercept) 2.0497
##
## Fixed effects:
## Estimate Std.Err z-value p-value
## (Intercept) 0.3539 0.1213 2.9166 0.003539
##
## Integration:
## method: adaptive Gauss-Hermite quadrature rule
## quadrature points: 11
##
## Optimization:
## method: hybrid EM and quasi-Newton
## converged: TRUE
tab_model(nullmodel_adapt)
| Â | c_overall_satisfaction | ||
|---|---|---|---|
| Predictors | Odds Ratios | CI | p |
| (Intercept) | 1.42 | 1.12 – 1.81 | 0.004 |
| Random Effects | |||
| σ2 | 3.29 | ||
| τ00 f_number | 4.20 | ||
| ICC | 0.56 | ||
| N f_number | 529 | ||
| Observations | 2071 | ||
| Marginal R2 / Conditional R2 | 0.000 / 0.561 | ||
#Median odds ratio
var.area.mnull <- nullmodel_adapt$D
or.mnull <- exp(0.95*sqrt(var.area.mnull))
or.mnull
## (Intercept)
## (Intercept) 7.00914
## attr(,"L")
## [1] 2.0497
Conducting a similar test for the variable ‘region’ gives the following result.
Non significant variance attribution value for region renders it’s use as a third level irrelevant.
library(ggplot2)
library(egg)
# ggplot butterfly
re = data.frame(condval = unlist(nullmodel_adapt$post_modes)[,1],
condsd = sqrt(unlist(nullmodel_adapt$post_vars)),
grp = 1:length(nullmodel_adapt$post_vars))
# sort for plot
dataPlot = re[order(re$condval), ]
dataPlot$rank = 1:NROW(dataPlot)
dataPlot$li = dataPlot$condval - dataPlot$condsd*qnorm(0.975)
dataPlot$ui = dataPlot$condval + dataPlot$condsd*qnorm(0.975)
ggplot(data = dataPlot, mapping = aes(x = rank, y = condval,
ymin = li,
ymax = ui))+
geom_ribbon(fill = "steelblue", color = NA)+
geom_line()+
egg::theme_article()
#less dense plot
ggplot(data = dataPlot[as.numeric(as.character(dataPlot$grp)) < 50,],
mapping = aes(x = rank,
y = condval, ymin = li, ymax = ui))+
ylab("Random effects for facility intercept")+
xlab("Facility rank")+
geom_hline(color = "pink", yintercept = 0, linetype = "dashed", alpha
= 0.5)+
geom_errorbar(color = "steelblue", width = 5)+
geom_line(color = "steelblue", alpha = 0.1)+
geom_point(color = "steelblue")+
egg::theme_article()
| Variable | Level | OR | Lower.CI | Upper.CI | p.value |
|---|---|---|---|---|---|
| c_fp_history_physical | c_fp_history_physical | 1.0314735 | 0.9558653 | 1.1130622 | 0.4249611 |
| c_previous_contact | c_previous_contactYes | 1.2112652 | 0.8464520 | 1.7333096 | 0.2945159 |
| c_visual_aids | c_visual_aidsYes | 1.1499795 | 0.7200107 | 1.8367127 | 0.5585737 |
| c_confidentiality | c_confidentialityYes | 1.2211428 | 0.7630783 | 1.9541767 | 0.4049390 |
| c_auditory_privacy | c_auditory_privacyYes | 1.6067342 | 1.0898727 | 2.3687121 | 0.0166394 |
| c_visual_privacy | c_visual_privacyYes | 1.6599048 | 1.0631111 | 2.5917178 | 0.0257997 |
| c_facility_closeness | c_facility_closenessYes | 1.3985798 | 0.8394186 | 2.3302145 | 0.1977675 |
| c_prepayment_plan | c_prepayment_planYes | 0.9681164 | 0.6915261 | 1.3553347 | 0.8502779 |
| c_fp_status | c_fp_statusPast User | 1.8384331 | 1.1434912 | 2.9557168 | 0.0119546 |
| c_fp_status | c_fp_statusNever Used | 1.0526124 | 0.6133096 | 1.8065798 | 0.8524022 |
| c_wait_time | c_wait_time | 0.9919341 | 0.9862190 | 0.9976823 | 0.0060126 |
| c_asked_ques | c_asked_quesYes | 0.5763606 | 0.4070327 | 0.8161299 | 0.0019035 |
| c_concerns_disscussed | c_concerns_disscussedYes | 0.6921383 | 0.4877266 | 0.9822213 | 0.0393558 |
| c_partner_attitude | c_partner_attitudeYes | 0.9381833 | 0.4988424 | 1.7644609 | 0.8430454 |
| c_followup | c_followupYes | 1.1020399 | 0.4545849 | 2.6716506 | 0.8297236 |
| c_problem_occur | c_problem_occurYes | 2.6360889 | 1.8987786 | 3.6597025 | 0.0000000 |
| c_sideeffect | c_sideeffectYes | 1.9166866 | 1.3884664 | 2.6458598 | 0.0000764 |
| c_how_to_use | c_how_to_useYes | 1.2376985 | 0.8479216 | 1.8066502 | 0.2691101 |
| c_educationlevel | c_educationlevelPrimary | 0.5224971 | 0.3678555 | 0.7421480 | 0.0002884 |
| c_educationlevel | c_educationlevelHigher | 0.6390332 | 0.4312163 | 0.9470039 | 0.0256594 |
| c_maritalstatus | c_maritalstatusMarried | 0.9940164 | 0.5220273 | 1.8927526 | 0.9854277 |
| c_age | c_age22 TO 30 | 1.3739005 | 0.9764225 | 1.9331823 | 0.0682937 |
| c_age | c_ageMore than 30 | 3.5774911 | 2.2794761 | 5.6146421 | 0.0000000 |
Adjusted estimates along with LLR, median odds ratio and proportional change in variance are reported in the original paper.
| Variable | Level | OR | Lower.CI | Upper.CI | p.value |
|---|---|---|---|---|---|
| f_training | f_trainingYes | 0.5532235 | 0.3330734 | 0.9188853 | 0.0222091 |
| f_catchment_pop | f_catchment_pop25K TO 1.5M | 1.0809172 | 0.6401130 | 1.8252745 | 0.7709833 |
| f_catchment_pop | f_catchment_popMore than 1.5M | 2.8299819 | 1.2142779 | 6.5955227 | 0.0159640 |
| f_no_of_days | f_no_of_daysAbove 20 | 1.1870360 | 0.7214108 | 1.9531928 | 0.4997951 |
| f_quality_structure | f_quality_structureQuality Unit & Committe | 2.4422251 | 1.2224772 | 4.8789975 | 0.0114408 |
| f_quality_structure | f_quality_structureQuality Committe | 3.1102251 | 1.9098011 | 5.0651874 | 0.0000051 |
| f_guideline_national | f_guideline_nationalAvailable | 1.2224549 | 0.7581554 | 1.9710947 | 0.4098922 |
| f_routine_action | f_routine_actionYes | 0.9107589 | 0.5672359 | 1.4623224 | 0.6988041 |
| f_location | f_locationRural | 0.4770849 | 0.2923226 | 0.7786263 | 0.0030641 |
| f_ownership | f_ownershipPrivate | 2.3901438 | 1.0490418 | 5.4457196 | 0.0380835 |
| f_no_trained_provider | f_no_trained_providerYes | 0.6500749 | 0.3908701 | 1.0811708 | 0.0970544 |
| f_methods_offered | f_methods_offeredMore than/equal 8 | 0.9599982 | 0.5915292 | 1.5579897 | 0.8687513 |
| fp_equipment | fp_equipment | 1.1034273 | 0.9773649 | 1.2457495 | 0.1118118 |
| f_supervisory_visit | f_supervisory_visitWithin 6 month | 1.4155655 | 0.5973812 | 3.3543499 | 0.4297978 |
| f_supervisory_visit | f_supervisory_visitMore than 6 month | 1.0326358 | 0.5342701 | 1.9958756 | 0.9239020 |
| f_routine_fees | f_routine_feesFixed | 0.3650711 | 0.1649555 | 0.8079568 | 0.0129142 |
| f_routine_fees | f_routine_feesSeparate | 2.4775930 | 1.5269827 | 4.0199979 | 0.0002386 |
| f_monthly_meetings | f_monthly_meetingsYes | 0.7123443 | 0.4053152 | 1.2519501 | 0.2384079 |
| f_3month_comm_meetings | f_3month_comm_meetingsYes | 0.8519934 | 0.5274847 | 1.3761399 | 0.5126016 |
| f_waiting_area | f_waiting_areaYes | 4.9687153 | 2.6125993 | 9.4496432 | 0.0000010 |
| f_standard_precaution | f_standard_precautionYes | 1.5990471 | 0.9738851 | 2.6255166 | 0.0635384 |
| f_performance_check | f_performance_checkClinical audit | 1.3936506 | 0.7250508 | 2.6787942 | 0.3194362 |
| f_performance_check | f_performance_checkRegular audit | 1.1589273 | 0.6638520 | 2.0232108 | 0.6038762 |
| f_performance_check | f_performance_checkFeedback | 3.1588163 | 1.4410269 | 6.9243122 | 0.0040735 |
| f_dhis2 | f_dhis2Yes | 1.7049208 | 1.0626578 | 2.7353630 | 0.0269691 |
| f_risk_assessment | f_risk_assessmentYes | 1.0449422 | 0.6019792 | 1.8138571 | 0.8758448 |
Adjusted estimates along with LLR, median odds ratio and proportional change in variance are reported in the original paper.
A combination of client and facility level variables that were significant in the previous models are reported for adjusted estimates along with LLR, median odds ratio and proportional change in variance in the original paper.
Thank you for taking interest in our work.