Malaria remains one of Kenya’s most important public health problems. The country’s transmission landscape is highly heterogeneous: transmission is intense and stable in the Lake Victoria basin (western Kenya) and the coastal lowlands, seasonal in the semi-arid regions, and epidemic-prone in the western highlands, while large areas such as Nairobi and the central highlands have little to no transmission. The dominant parasite species is Plasmodium falciparum, which causes the most severe disease burden (Hay & Snow, 2006; Snow et al., 2017).
Purpose of the project. This project applies a complete epidemiological data-analysis workflow — from raw survey data to a final written report — to community and school-based malaria surveys from the Malaria Atlas Project (MAP), a global database of geo-referenced malaria survey results. The aim is to describe where, when, and at what intensity P. falciparum was circulating in Kenya between 1985 and 2009, and to quantify the factors associated with higher prevalence.
Beyond the epidemiological findings, the project demonstrates the end-to-end process of reproducible public-health analysis: data acquisition and cleaning, exploratory data analysis, spatial visualization, statistical modeling, and clear communication of results — the core skills used daily in research institutions such as KEMRI.
The data were extracted from the Malaria Atlas Project via the
malariaAtlas R package and saved locally as
kenya_malaria_raw.csv for reproducible, offline analysis.
Each row is a single community or school-based cross-sectional survey
reporting the number of individuals examined, the
number positive for P. falciparum, and the
resulting parasite rate (pr = positive / examined),
together with the survey location, year, and diagnostic method.
# The dataset ships with this project
if (!file.exists("kenya_malaria_raw.csv")) {
stop("kenya_malaria_raw.csv not found. Set your working directory to the project folder.")
}
malaria_raw <- read_csv("kenya_malaria_raw.csv", show_col_types = FALSE)
malaria_raw %>%
select(site_name, year_start, latitude, longitude, examined,
positive, pr, method, rural_urban) %>%
slice(1:8) # first rows, to illustrate the structureThe analysis-ready dataset keeps only surveys with complete and valid parasitological data, and derives a few new variables used throughout the report.
malaria_clean <- malaria_raw %>%
drop_na(pr, examined, positive, year_start) %>%
filter(examined > 0, positive <= examined) %>%
mutate(
year = as.integer(year_start),
prevalence_pct = pr * 100,
high_prevalence = if_else(pr >= 0.20, "Yes", "No"),
setting = recode(rural_urban, UNKNOWN = "Not recorded"),
method_clean = if_else(is.na(method) | method == "",
"Not recorded", method)
)
# Key summary quantities used throughout the report
n_surveys <- nrow(malaria_clean)
year_min <- min(malaria_clean$year)
year_max <- max(malaria_clean$year)
n_examined <- sum(malaria_clean$examined)
n_positive <- sum(malaria_clean$positive)
overall_pr <- n_positive / n_examined * 100
# Rows removed during cleaning
n_dropped <- nrow(malaria_raw) - n_surveysAfter cleaning, 2149 of the 3607 raw surveys were retained (1458 were dropped for missing or invalid records).
Temporal trends are summarized as the mean prevalence per survey year. Spatial patterns are visualized by plotting each survey site on a map of Kenya using its recorded coordinates. A binomial logistic regression model is fitted to the number of positive cases out of the number examined, with survey year, setting, and diagnostic method as predictors, and results are reported as odds ratios with 95% confidence intervals.
What the data show. Between 1985 and 2020, the 2149 surveys in this dataset examined 186,765 individuals and detected 38,162.6 P. falciparum infections, an overall crude prevalence of 20.4%.
The analysis reveals three headline findings:
The full evidence, figures, and the statistical model follow in Sections 5-8.
malaria_clean %>%
select(year, examined, positive, prevalence_pct, setting, method_clean) %>%
tbl_summary(
statistic = list(all_continuous() ~ "{median} ({p25}, {p75})",
all_categorical() ~ "{n} ({p}%)"),
digits = list(all_continuous() ~ 1, all_categorical() ~ 0),
label = list(
year ~ "Survey year",
examined ~ "Number examined per survey",
positive ~ "Positive cases per survey",
prevalence_pct ~ "Prevalence (%)",
setting ~ "Setting",
method_clean ~ "Diagnostic method"
)
)| Characteristic | N = 2,1491 |
|---|---|
| Survey year | 2,009.0 (2,003.0, 2,015.0) |
| Number examined per survey | 60.0 (33.0, 109.0) |
| Positive cases per survey | 4.0 (0.0, 18.0) |
| Prevalence (%) | 6.4 (0.0, 28.3) |
| Setting | |
| Not recorded | 1,175 (55%) |
| PERI_URBAN | 1 (0%) |
| RURAL | 675 (31%) |
| URBAN | 298 (14%) |
| Diagnostic method | |
| Microscopy | 1,562 (73%) |
| RDT | 587 (27%) |
| 1 Median (Q1, Q3); n (%) | |
yearly_trend <- malaria_clean %>%
group_by(year) %>%
summarise(mean_prevalence = mean(prevalence_pct, na.rm = TRUE),
n_surveys = n(),
.groups = "drop")
ggplot(yearly_trend, aes(x = year, y = mean_prevalence)) +
geom_line(color = "steelblue", linewidth = 1) +
geom_point(aes(size = n_surveys), color = "darkorange", alpha = 0.8) +
scale_x_continuous(breaks = seq(1985, 2010, 5)) +
labs(title = "Mean malaria prevalence across survey years",
subtitle = "Point size reflects the number of surveys conducted that year",
x = "Year", y = "Mean prevalence (%)") +
theme_minimal()ggplot(yearly_trend, aes(x = year, y = n_surveys)) +
geom_col(fill = "darkorange", alpha = 0.85) +
scale_x_continuous(breaks = seq(1985, 2010, 5)) +
labs(title = "Number of surveys conducted per year",
x = "Year", y = "Survey count") +
theme_minimal()Interpretation: the mean-prevalence line should be read alongside the number of surveys supporting it — years with few surveys (e.g. the late 1980s) rest on very little data and should not be over-interpreted. The strong rise in survey count after 2005 reflects the national school-based malaria survey programme.
kenya_shape <- tryCatch(
ne_countries(scale = "medium", country = "Kenya", returnclass = "sf"),
error = function(e) NULL
)
p_map <- ggplot()
if (!is.null(kenya_shape)) {
p_map <- p_map +
geom_sf(data = kenya_shape, fill = "grey95", color = "grey40")
}
p_map <- p_map +
geom_point(data = malaria_clean,
aes(x = longitude, y = latitude, color = prevalence_pct),
size = 2.2, alpha = 0.8) +
scale_color_viridis_c(option = "plasma", name = "Prevalence (%)",
limits = c(0, 100)) +
labs(title = "Malaria prevalence at survey sites across Kenya",
caption = if (is.null(kenya_shape)) "Country outline unavailable - points only" else
"Source: Malaria Atlas Project survey data") +
theme_void() +
theme(plot.title = element_text(face = "bold", hjust = 0.5),
legend.position = "right")
p_mapInterpretation: the geographic gradient is striking. High prevalence clusters in western Kenya (around the Lake Victoria basin) and along the coast (Kilifi, Kwale, Malindi areas), while sites in central, eastern, and northern Kenya cluster near zero. This matches the well-documented ecology of malaria transmission in Kenya (Snow et al., 2017).
malaria_clean %>%
arrange(desc(prevalence_pct)) %>%
select(site_name, year, setting, examined, positive,
prevalence_pct, method_clean) %>%
slice_head(n = 10)ggplot(malaria_clean, aes(x = method_clean, y = prevalence_pct, fill = method_clean)) +
geom_boxplot(show.legend = FALSE, alpha = 0.85) +
geom_jitter(width = 0.15, alpha = 0.3, color = "grey40") +
labs(title = "Prevalence by diagnostic method",
x = "", y = "Prevalence (%)") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 20, hjust = 1))ggplot(malaria_clean, aes(x = setting, y = prevalence_pct, fill = setting)) +
geom_boxplot(show.legend = FALSE, alpha = 0.85) +
geom_jitter(width = 0.15, alpha = 0.3, color = "grey40") +
labs(title = "Prevalence by setting",
x = "", y = "Prevalence (%)") +
theme_minimal()ggplot(malaria_clean, aes(x = examined, y = prevalence_pct)) +
geom_point(alpha = 0.5, color = "steelblue") +
geom_smooth(method = "loess", color = "darkorange", se = TRUE) +
labs(title = "Survey sample size vs. estimated prevalence",
x = "Number examined", y = "Prevalence (%)") +
theme_minimal()Interpretation: surveys with very small samples (fewer than ~20 examined) produce both the highest and lowest extreme values — the least reliable estimates. This motivates weighting prevalence summaries by sample size, and caution when comparing individual sites.
To quantify the association between survey characteristics and the probability that an examined individual tests positive, a binomial logistic regression is fitted to the observed counts:
model_data <- malaria_clean %>%
filter(setting %in% c("RURAL", "URBAN")) %>%
mutate(setting = factor(setting, levels = c("RURAL", "URBAN")))
model_fit <- glm(cbind(positive, examined - positive) ~ year + setting + method_clean,
data = model_data, family = binomial)
tbl_regression(model_fit, exponentiate = TRUE) %>%
modify_header(label = "**Characteristic**", estimate = "**OR (95% CI)**")| Characteristic | OR (95% CI) | 95% CI | p-value |
|---|---|---|---|
| year | 0.93 | 0.93, 0.93 | <0.001 |
| setting | |||
| RURAL | — | — | |
| URBAN | 0.48 | 0.46, 0.51 | <0.001 |
| method_clean | |||
| Microscopy | — | — | |
| RDT | 0.50 | 0.48, 0.53 | <0.001 |
| Abbreviations: CI = Confidence Interval, OR = Odds Ratio | |||
Reading the table: each odds ratio (OR) is adjusted for the other variables in the model.
Note: survey sites are not a random sample of Kenyan communities, and locations within a region may be correlated (spatial clustering), so these are associational rather than causal estimates.
The results tell a coherent story. Geographically, the parasite rate is concentrated in western Kenya and along the coast, echoing the classic ecology of malaria in Kenya — perennial transmission where rainfall and temperature favor the Anopheles mosquito (Hay & Snow, 2006). Temporally, after adjusting for setting and diagnostic method, the odds of infection trended downward with each passing year, consistent with the scale-up of insecticide-treated nets, indoor residual spraying, and improved case management documented nationally through the 2000s (Noor et al., 2014).
Two methodological lessons emerge. First, sampling matters: the trend line is only as trustworthy as the number of surveys behind it, and the extreme values from tiny surveys warn against reading too much into individual sites. Second, measurement matters: microscopy and RDTs are not interchangeable, and mixing them across time can distort trends unless adjusted for.
For a country like Kenya, whose transmission ranges from intense to absent within a few hundred kilometres, spatially explicit surveillance data of the kind used here are essential for targeting interventions where they are most needed.
Using Malaria Atlas Project survey data spanning a quarter of a century, this project mapped the changing landscape of P. falciparum transmission in Kenya. Prevalence is concentrated in western Kenya and the coastal belt, and — after accounting for diagnostic method and setting — trended downward over time. The analysis also highlights the practical realities of working with survey surveillance datasets: sample sizes, diagnostic methods, and non-random sampling must all be taken into account before a single prevalence number can be trusted. These are precisely the considerations that shape evidence-based malaria control planning at institutions such as KEMRI.
## 1. Hay, S. I., & Snow, R. W. (2006). The Malaria Atlas Project: Developing global maps of malaria risk. PLoS Medicine, 3(12), e473.
## 2. Snow, R. W., Sartorius, B., Kyalo, D., Maina, J., Amratia, P., Mundia, C. W., et al. (2017). The prevalence of Plasmodium falciparum in sub-Saharan Africa since 1900. Nature, 550(7677), 515-518.
## 3. Noor, A. M., Kinyoki, D. K., Mundia, C. W., Kabaria, C. W., Mutua, J. W., Alegana, V. A., et al. (2014). The changing risk of Plasmodium falciparum malaria infection in Africa: 2000-10. The Lancet, 383(9924), 1739-1747.
## 4. Malaria Atlas Project. malariaAtlas: An R interface to the Malaria Atlas Project. Available at https://malariaatlas.org
## R version 4.6.1 (2026-06-24 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 10 x64 (build 19045)
##
## Matrix products: default
## LAPACK version 3.12.1
##
## locale:
## [1] LC_COLLATE=English_Kenya.utf8 LC_CTYPE=English_Kenya.utf8
## [3] LC_MONETARY=English_Kenya.utf8 LC_NUMERIC=C
## [5] LC_TIME=English_Kenya.utf8
##
## time zone: Africa/Nairobi
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] rnaturalearth_1.2.0 sf_1.1-1 gtsummary_2.5.1
## [4] lubridate_1.9.5 forcats_1.0.1 stringr_1.6.0
## [7] dplyr_1.2.1 purrr_1.2.2 readr_2.2.0
## [10] tidyr_1.3.2 tibble_3.3.1 ggplot2_4.0.3
## [13] tidyverse_2.0.0
##
## loaded via a namespace (and not attached):
## [1] gtable_0.3.6 xfun_0.60 bslib_0.11.0
## [4] lattice_0.22-9 tzdb_0.5.0 vctrs_0.7.3
## [7] tools_4.6.1 generics_0.1.4 parallel_4.6.1
## [10] proxy_0.4-29 pkgconfig_2.0.3 Matrix_1.7-5
## [13] KernSmooth_2.23-26 RColorBrewer_1.1-3 S7_0.2.2
## [16] gt_1.3.0 lifecycle_1.0.5 compiler_4.6.1
## [19] farver_2.1.2 litedown_0.10 htmltools_0.5.9
## [22] class_7.3-23 sass_0.4.10 yaml_2.3.12
## [25] pillar_1.11.1 crayon_1.5.3 jquerylib_0.1.4
## [28] broom.helpers_1.22.0 classInt_0.4-11 cachem_1.1.0
## [31] nlme_3.1-169 rnaturalearthdata_1.0.0 commonmark_2.0.0
## [34] tidyselect_1.2.1 digest_0.6.39 stringi_1.8.7
## [37] splines_4.6.1 labeling_0.4.3 labelled_2.16.0
## [40] fastmap_1.2.0 grid_4.6.1 cli_3.6.6
## [43] magrittr_2.0.5 cards_0.8.1 dichromat_2.0-1
## [46] broom_1.0.13 e1071_1.7-17 withr_3.0.3
## [49] backports_1.5.1 scales_1.4.0 bit64_4.8.2
## [52] timechange_0.4.0 rmarkdown_2.31 bit_4.6.0
## [55] otel_0.2.0 hms_1.1.4 evaluate_1.0.5
## [58] haven_2.5.5 knitr_1.51 viridisLite_0.4.3
## [61] mgcv_1.9-4 markdown_2.0 rlang_1.3.0
## [64] Rcpp_1.1.2 glue_1.8.1 DBI_1.3.0
## [67] xml2_1.6.0 rstudioapi_0.19.0 vroom_1.7.1
## [70] jsonlite_2.0.0 R6_2.6.1 fs_2.1.0
## [73] units_1.0-1