In a public health study investigating the impact of environmental factors, socio-economic conditions and healthcare interventions on malaria incidence, data were collected from several health districts during a peak transmission period. Each observation describes local environmental and living conditions, a specific medical intervention and the resulting malaria incidence rate, measured in cases per 1,000 inhabitants. This practical exercise aims to model and explain malaria incidence using a multiple linear regression approach based on available environmental, socioeconomic and prevention variables.
The dataset includes the following variables:
Rainfall_mm: Total rainfall in the district (mm)
Average_temp_C: Average monthly temperature (°C)
Humidity_%: Average relative humidity (%)
Stagnant_water_index: Index of stagnant water surface area (scale from 0 to 10)
Bednet_coverage_%: Percentage of the population regularly using insecticide-treated bed nets (%)
Healthcare_distance_km: Average distance to the nearest health center (km)
Poverty_rate_%: Percentage of the population living below the poverty line (%)
Malaria_incidence_rate: Number of confirmed malaria cases per 1,000 inhabitants
Here is the part where we will rename variables if need and add the labels for these variables
## 'data.frame': 200 obs. of 8 variables:
## $ Rainfall_mm : num 179 130 153 177 171 ...
## $ Average_temp_C : num 32.2 26.2 30.1 30.5 28.1 ...
## $ Humidity_pct : num 73.8 67.1 64 64.7 71.6 ...
## $ Stagnant_water_index : num 7.17 5.81 6.54 5.68 4.39 6.28 5.54 6.89 8.17 3.38 ...
## $ Bednet_coverage_pct : num 21.7 37.5 39.5 68.3 25.3 ...
## $ Healthcare_distance_km: num 20.33 6.92 19.61 7.25 18.16 ...
## $ Poverty_rate_pct : num 25.5 45.9 67.8 41 40 ...
## $ Malaria_incidence_rate: num 107.7 72.7 110 83.8 87.7 ...
The following figure shows the histograms of all the independant variables.
From this visualisation, we can notice that, the rainfall, average temperature, humidity and stagnant water index seams to follows normal distribution. But for the others, by using shapiro-test we notice that the p-value was less that 0.05, the we rejected the null hypothesis and confirm that, they don’t follow normal distribution.
hist(Malaria[-8], main = "Histograms for all the variables in the dataset")
Here you can find the results for the shapiro test
(Rain <- shapiro.test(Malaria$Rainfall))
##
## Shapiro-Wilk normality test
##
## data: Malaria$Rainfall
## W = 0.99453, p-value = 0.6794
(Temp <- shapiro.test(Malaria$temperature))
##
## Shapiro-Wilk normality test
##
## data: Malaria$temperature
## W = 0.99622, p-value = 0.9064
(Hum <- shapiro.test(Malaria$Humidity))
##
## Shapiro-Wilk normality test
##
## data: Malaria$Humidity
## W = 0.99337, p-value = 0.5102
(SWI <- shapiro.test(Malaria$Stagnant_water_index))
##
## Shapiro-Wilk normality test
##
## data: Malaria$Stagnant_water_index
## W = 0.98922, p-value = 0.1369
(Bnet <- shapiro.test(Malaria$Bednet))
##
## Shapiro-Wilk normality test
##
## data: Malaria$Bednet
## W = 0.92625, p-value = 1.717e-08
(Dist <- shapiro.test(Malaria$distance))
##
## Shapiro-Wilk normality test
##
## data: Malaria$distance
## W = 0.9322, p-value = 5.073e-08
(Povty <- shapiro.test(Malaria$Poverty))
##
## Shapiro-Wilk normality test
##
## data: Malaria$Poverty
## W = 0.95242, p-value = 3.253e-06
hist(Malaria$incidence_rate, col = "skyblue", probability = TRUE, ylim = c(0,0.025), main = "Histogram of malaria incidence rate with the bellshaped curve", xlab="Malaria incidence ratio")
curve(dnorm(x, mean = mean(Malaria$incidence_rate), sd(Malaria$incidence_rate)), col = "red", lwd = 2, add = TRUE)
legend("topright", legend = c("Bellshaped curve", "p-value = 0.3066"), col=c("red","skyblue"), lwd =2)
Malaria %>% tbl_summary(
include = everything(-c(5,6,7)),
statistic = all_continuous()~ "{mean} ± {sd}"
)
| Characteristic | N = 2001 |
|---|---|
| Total rainfall in the district (mm) | 183 ± 49 |
| Average monthly temperature (°C) | 27.79 ± 2.55 |
| Average relative humidity (%) | 73 ± 10 |
| 1 Mean ± SD | |
| Variable | Minimum | Mean | Standard deviation | Maximun | Normality test |
|---|---|---|---|---|---|
| Total rainfall in the district (mm) | 40.91 | 182.64 | 48.57 | 302.62 | 0.6794 |
| Average monthly temperature (°C) | 20.17 | 27.79 | 2.55 | 35.1 | 0.9064 |
| Average relative humidity (%) | 45.58 | 72.88 | 10.04 | 95 | 0.5102 |
| Index of stagnant water surface area (scale from 0 to 10) | 1.51 | 6.47 | 1.79 | 10 | 0.1369 |
| Variable | Minimum | 1st quatile | median | 2nd quartile | Normality test |
|---|---|---|---|---|---|
| Percentage of the population regularly using insecticide-treated bed nets (%) | 20.74 | 32.23 | 51.72 | 73.36 | 1.72^{-8} |
| Average distance to the nearest health center (km) | 2.12 | 7.1 | 10.45 | 14.8 | 5.07^{-8} |
| Percentage of the population living below the poverty line (%) | 15.38 | 28.26 | 45.59 | 57.82 | 3.2526159^{-6} |
The following diagrams shows the boxplots for all the variables inside the dataset. One can notice that there are some outliers. We observe them for the variables rainfall, incidence rate, temparature and distance. After investigation, we can see that they are not errorneous. So we will keep it and analyse them at the end.
par(mfrow =c(2,2))
boxplot(Malaria[,1])
boxplot(Malaria[,c(3,5,7,8)])
boxplot(Malaria[,c(2,4,6)])
In this part, we are going to model and malaria incidence using a multiple linear regression approach based on available environmental, socioeconomic and prevention variables
The model will be
incidence_rate = \(\beta_0\) + \(\beta_1\) × Rainfall + \(\beta_2\) × temperature + \(\beta_3\) × Humidity+ \(\beta_4\) × Stagnant_water_index + \(\beta_5\) × Bednet + \(\beta_6\) × distance + \(\beta_7\) × Poverty + \(\epsilon\)
The figure above present the correlation between all the variables in the dataset with the scatterplots. Pearson’s correlation analysis revealed strong positive correlations among the environmental variables. Rainfall was highly correlated with humidity (r = 0.88) and the stagnant water index (r = 0.78), whereas humidity was also strongly correlated with the stagnant water index (r = 0.73), suggesting potential multicollinearity among these predictors. Temperature showed negligible correlations with the remaining explanatory variables. Malaria incidence was moderately and positively correlated with rainfall (r = 0.37), temperature (r = 0.37), stagnant water index (r = 0.37), humidity (r = 0.32), poverty (r = 0.29), and distance (r = 0.23), but bednet coverage exhibited a weak negative correlation with malaria incidence (r = −0.27). Overall, these findings indicate that climatic factors are moderately associated with malaria incidence, while strong intercorrelations among rainfall, humidity, and stagnant water warrant assessment of multicollinearity before multivariable modeling.
cormat <- cor(Malaria)
pval <- cor.mtest(Malaria)
par(mfrow=c(1,2))
corrplot(cormat,method="number", type = "full")
corrplot(cormat, method="color",type = "upper", p.mat = pval$p, insig = "p-value", number.digits = 3, sig.level = 0.05)
Model1 <- lm(incidence_rate~.,data = Malaria)
anova(Model1)
## Analysis of Variance Table
##
## Response: incidence_rate
## Df Sum Sq Mean Sq F value Pr(>F)
## Rainfall 1 15297 15297.1 50.0879 2.686e-11 ***
## temperature 1 13799 13798.7 45.1817 1.995e-10 ***
## Humidity 1 89 89.3 0.2924 0.5892852
## Stagnant_water_index 1 1649 1648.5 5.3979 0.0212087 *
## Bednet 1 7022 7022.0 22.9925 3.254e-06 ***
## distance 1 4812 4811.8 15.7554 0.0001018 ***
## Poverty 1 8343 8343.5 27.3193 4.478e-07 ***
## Residuals 192 58638 305.4
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
summary(Model1)
##
## Call:
## lm(formula = incidence_rate ~ ., data = Malaria)
##
## Residuals:
## Min 1Q Median 3Q Max
## -72.554 -8.504 1.042 9.341 53.755
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -48.12435 19.66504 -2.447 0.01530 *
## Rainfall 0.11129 0.05913 1.882 0.06135 .
## temperature 3.11819 0.49166 6.342 1.59e-09 ***
## Humidity -0.21322 0.26746 -0.797 0.42631
## Stagnant_water_index 3.03431 1.11948 2.710 0.00733 **
## Bednet -0.24267 0.05683 -4.270 3.07e-05 ***
## distance 0.99401 0.21930 4.533 1.02e-05 ***
## Poverty 0.37611 0.07196 5.227 4.48e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 17.48 on 192 degrees of freedom
## Multiple R-squared: 0.4652, Adjusted R-squared: 0.4457
## F-statistic: 23.86 on 7 and 192 DF, p-value: < 2.2e-16
Variance Inflation Factors (VIFs) were computed to assess multicollinearity among the explanatory variables. VIF values ranged from 1.02 to 5.37, indicating generally low multicollinearity. Rainfall (VIF = 5.37) and humidity (VIF = 4.69) exhibited moderate multicollinearity, reflecting their strong pairwise correlation (r = 0.88). However, all VIF values were well below the commonly accepted threshold of 10, suggesting that multicollinearity was not severe and that all predictors were retained in the final model.
vif(Model1)
## Rainfall temperature Humidity
## 5.374762 1.021030 4.694208
## Stagnant_water_index Bednet distance
## 2.614735 1.039260 1.022684
## Poverty
## 1.018634
When we used `step()` to variable selection, the process excluded the humidity but this wasn’t have any difference with the model with all the variable (p.value=0.4263). So, we decided to keep all the variables for prédiction.
Model2 <- step(Model1, direction = "both")
## Start: AIC=1152.16
## incidence_rate ~ Rainfall + temperature + Humidity + Stagnant_water_index +
## Bednet + distance + Poverty
##
## Df Sum of Sq RSS AIC
## - Humidity 1 194.1 58832 1150.8
## <none> 58638 1152.2
## - Rainfall 1 1081.7 59720 1153.8
## - Stagnant_water_index 1 2243.7 60882 1157.7
## - Bednet 1 5568.0 64206 1168.3
## - distance 1 6274.7 64913 1170.5
## - Poverty 1 8343.5 66981 1176.8
## - temperature 1 12284.3 70922 1188.2
##
## Step: AIC=1150.82
## incidence_rate ~ Rainfall + temperature + Stagnant_water_index +
## Bednet + distance + Poverty
##
## Df Sum of Sq RSS AIC
## <none> 58832 1150.8
## + Humidity 1 194.1 58638 1152.2
## - Rainfall 1 1095.9 59928 1152.5
## - Stagnant_water_index 1 2082.8 60915 1155.8
## - Bednet 1 5376.6 64208 1166.3
## - distance 1 6217.6 65050 1168.9
## - Poverty 1 8327.8 67160 1175.3
## - temperature 1 12869.4 71701 1188.4
summary(Model2)
##
## Call:
## lm(formula = incidence_rate ~ Rainfall + temperature + Stagnant_water_index +
## Bednet + distance + Poverty, data = Malaria)
##
## Residuals:
## Min 1Q Median 3Q Max
## -72.777 -9.213 0.338 9.302 52.399
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -58.09070 15.16544 -3.830 0.000173 ***
## Rainfall 0.07709 0.04065 1.896 0.059439 .
## temperature 3.16688 0.48739 6.498 6.78e-10 ***
## Stagnant_water_index 2.87877 1.10131 2.614 0.009656 **
## Bednet -0.23519 0.05600 -4.200 4.08e-05 ***
## distance 0.98908 0.21900 4.516 1.09e-05 ***
## Poverty 0.37575 0.07189 5.227 4.46e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 17.46 on 193 degrees of freedom
## Multiple R-squared: 0.4635, Adjusted R-squared: 0.4468
## F-statistic: 27.78 on 6 and 193 DF, p-value: < 2.2e-16
vif(Model2)
## Rainfall temperature Stagnant_water_index
## 2.545299 1.005276 2.535319
## Bednet distance Poverty
## 1.010973 1.021871 1.018593
anova(Model1, Model2)
## Analysis of Variance Table
##
## Model 1: incidence_rate ~ Rainfall + temperature + Humidity + Stagnant_water_index +
## Bednet + distance + Poverty
## Model 2: incidence_rate ~ Rainfall + temperature + Stagnant_water_index +
## Bednet + distance + Poverty
## Res.Df RSS Df Sum of Sq F Pr(>F)
## 1 192 58638
## 2 193 58832 -1 -194.1 0.6355 0.4263
Residuals <- residuals(Model1)
Fitted <- fitted(Model1)
H0: Residuals are normally distributed
H1: Residuals are not normally distributed
shap <- shapiro.test(Residuals)
par(mfrow = c(1,2))
hist(Residuals, main = "Histogram", probability = TRUE, ylim=c(0,0.03), lwd=1.5)
curve(dnorm(x, mean(Residuals),sd(Residuals)), col="red", add=TRUE,lwd=2)
legend("topright", legend = c("Bellshaped curve", "p-value = 0.0004942"), col=c("red","gray"), lwd =2)
qqnorm(Residuals)
qqline(Residuals, lty = 2, col = "blue",lwd=2)
H0: Homoscedasticity (constant variance of residuals)
H1: Heteroscedasticity
By inspecting the residuals vs the fitted values, we see that the values present a structure. Meaning the non constant variance. After checking for homoscedasticity test of Breusch-Pagan, we notice that the p-value is less that 0.05, this lead to an absence of homoscédasticity. So this condition didn’t holds.
plot(Fitted, Residuals, xlab = "Fitted values", ylab = "Residuals")
abline(h = 0, lty = 2)
text(Fitted, Residuals, labels = rownames(Malaria), pos = 4)
bptest(Model1)
##
## studentized Breusch-Pagan test
##
## data: Model1
## BP = 15.988, df = 7, p-value = 0.02522
H0: Autocorrelation = 0 (independent residuals)
H1: Positive autocorrelation (non-independent residuals)
dwtest(Model1)
##
## Durbin-Watson test
##
## data: Model1
## DW = 1.9127, p-value = 0.2688
## alternative hypothesis: true autocorrelation is greater than 0
H0: Mean of residuals = 0
H1: Mean of residuals ≠ 0
t.test(Residuals, mu = 0)
##
## One Sample t-test
##
## data: Residuals
## t = -5.748e-16, df = 199, p-value = 1
## alternative hypothesis: true mean is not equal to 0
## 95 percent confidence interval:
## -2.393562 2.393562
## sample estimates:
## mean of x
## -6.976936e-16
H0: Linearity (the model is correctly specified)
H1: Non-linearity (model misspecification)
resettest(Model1, power = 2:3, type = "regressor")
##
## RESET test
##
## data: Model1
## RESET = 0.97411, df1 = 14, df2 = 178, p-value = 0.4817
In this section, we evaluated the extreme values and their influences in the regression line. The following plot represents the influence plot. Which combines Leverage, studentized residuals, and Cook’s distance (the size of the circles). As we can see in this figure, the observations 171, 92, 42 and 50 have the biggest values for Leverage and Cook’s distances. But only the 171 and 50 values are out of the acceptance region for studentized residuals. The critical value of the Leverage was calculated using the following formula :
Leverage critique = 2(p+1) /n
Where :
p = 7 the number of explanatory variables
n = 200 the sample size
\(\approx\) 200
influencePlot(Model1,
id=TRUE,
main="Influence Plot", fill.col = carPalette()[2], fill.alpha = 0.5)
## StudRes Hat CookD
## 42 -1.085378 0.09227491 0.01495542
## 50 -4.445311 0.04252868 0.09994977
## 92 1.450012 0.10060282 0.02922974
## 171 3.292099 0.08225477 0.11550298
abline(v=0.08, col="red", lwd=2, lty=2)
abline(h=-2, col="blue", lwd=2, lty=2)
abline(h=2, col="blue", lwd=2, lty=2)
bcmodel <- boxCox(Model1,lambda = seq(-2,2,1/10), plotit=TRUE)
The Box–Cox profile log-likelihood indicated an optimal transformation parameter close to \(\lambda\) = 1, with the 95% confidence interval including 1. Therefore, no transformation of the response variable was considered necessary, and the original scale of the malaria incidence was retained for subsequent analyses.
The overall Box-Tidwell test indicated no evidence against the linearity assumption (χ² = 5.07, df = 7, p = 0.652). At the individual level, only the variable distance showed evidence of departure from linearity according to the Score test (p = 0.040), whereas the remaining predictors satisfied the linearity assumption.
btmodel <- bocotir::boxTidwell(Model1)
btmodel$boxtid$main$Overall
## Chi2(df=7) Pr(> Chi2)
## 5.0656723 0.6519491
btmodel$boxtid$main$Score
## Estimate z-value Pr(> |z|)
## Rainfall 0.2126226 -0.1012924 0.91931831
## temperature 1.5423375 0.1158761 0.90775073
## Humidity -7.9867857 -0.3869323 0.69880629
## Stagnant_water_index -0.7696041 -0.2715343 0.78598016
## Bednet 0.2039661 -0.5575119 0.57717773
## distance 2.2849314 2.0533993 0.04003387
## Poverty 0.7482078 -0.1994503 0.84191052
Model3 <- lm(incidence_rate~Rainfall+temperature+Humidity+Stagnant_water_index+Bednet+I(distance^2)+Poverty, data=Malaria)
summary(Model3)
##
## Call:
## lm(formula = incidence_rate ~ Rainfall + temperature + Humidity +
## Stagnant_water_index + Bednet + I(distance^2) + Poverty,
## data = Malaria)
##
## Residuals:
## Min 1Q Median 3Q Max
## -71.001 -9.018 1.085 9.374 49.058
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -42.430911 19.351432 -2.193 0.0295 *
## Rainfall 0.111971 0.058626 1.910 0.0576 .
## temperature 3.100998 0.487437 6.362 1.43e-09 ***
## Humidity -0.208484 0.265145 -0.786 0.4327
## Stagnant_water_index 3.054151 1.109983 2.752 0.0065 **
## Bednet -0.242894 0.056328 -4.312 2.58e-05 ***
## I(distance^2) 0.035623 0.007239 4.921 1.85e-06 ***
## Poverty 0.370814 0.071179 5.210 4.86e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 17.33 on 192 degrees of freedom
## Multiple R-squared: 0.4743, Adjusted R-squared: 0.4551
## F-statistic: 24.75 on 7 and 192 DF, p-value: < 2.2e-16
new_data <- read_excel("C:/Users/hp/Downloads/new_data.xlsx")
# 2. Vérifier les noms des variables
names(new_data)
## [1] "Year" "Health_zone_ID" "Department"
## [4] "Health_zone" "Rainfall_mm" "Average_temp_C"
## [7] "Humidity_%" "Stagnant_water_index" "Bednet_coverage_%"
## [10] "Healthcare_distance_km" "Poverty_rate_%" "Malaria_incidence_rate"
## [13] "Data_status" "Scientific_use_note"
new_data <- new_data %>%
rename(
"Rainfall" = "Rainfall_mm",
"temperature" = "Average_temp_C",
"Humidity"="Humidity_%",
"Bednet"="Bednet_coverage_%",
"distance" = "Healthcare_distance_km",
"Poverty" = "Poverty_rate_%",
"incidence_rate" = "Malaria_incidence_rate"
) %>%
set_variable_labels(
Rainfall = "Total rainfall in the district (mm)",
temperature = "Average monthly temperature (°C)",
Humidity = "Average relative humidity (%)",
Stagnant_water_index = "Index of stagnant water surface area (scale from 0 to 10)",
Bednet = "Percentage of the population regularly using insecticide-treated bed nets (%)",
distance = "Average distance to the nearest health center (km)",
Poverty = "Percentage of the population living below the poverty line (%)",
incidence_rate = "Number of confirmed malaria cases per 1,000 inhabitants"
)
prediction_data <- new_data[5:12,]
Here it’s clear that we don’t have enougth eveidence for predicting Malaria incidence. This model is not very correct event if it have R²a=0.4551. There remain others factor non mesured.
pred <- predict(
Model3,
newdata = prediction_data,
interval = "prediction",
level = 0.95
)
prediction_results <- prediction_data %>%
mutate(
predicted_incidence = pred[, "fit"],
lower_95 = pred[, "lwr"],
upper_95 = pred[, "upr"],
residual_prediction = incidence_rate - predicted_incidence
)
head(prediction_results[,c(1,4,15:17)],8)
## # A tibble: 8 × 5
## Year Health_zone predicted_incidence lower_95 upper_95
## <dbl> <chr> <dbl> <dbl> <dbl>
## 1 2023 Kouande-Pehunco-Kerou 168. 67.4 269.
## 2 2023 Tanguiéta-Materi-Cobly 136. 47.5 225.
## 3 2023 Abomey-Calavi-So-Ava 189. 52.2 325.
## 4 2023 Allada-Toffo-Ze 197. 59.3 334.
## 5 2023 Ouidah-Kpomasse-Tori-Bossito 193. 47.5 339.
## 6 2023 Bembereke-Sinende 156. 62.8 249.
## 7 2023 Nikki-Kalale-Perere 164. 65.0 264.
## 8 2023 Parakou-N'Dali 140. 55.8 225.