library(tidyverse)
library(readxl)
library(janitor)
library(here)
library(scales)
library(knitr)
library(forcats)

knitr::opts_chunk$set(
  message = FALSE,
  warning = FALSE,
  fig.width = 11,
  fig.height = 7,
  dpi = 300
)

theme_set(theme_minimal(base_size = 12))

1. Purpose

This analysis supports the Mozambique case study for the proposed manuscript “Operationalizing the Effectiveness of Fisheries Other Effective Area-based Conservation Measures: A Composite Coastal Resilience Framework”.

The aim is to use the Mozambique household survey data to show how the Composite Coastal Resilience Framework (CCRF) can diagnose relative strengths and weaknesses across resilience domains. Here, we analyse two HHS-based core components:

  1. Capacity for Collective Action
  2. Sustainable Livelihoods

The analysis is descriptive. The results can show patterns across places and years and can be connected to the package of initiatives implemented in each site, but they should not be interpreted as causal impacts without a stronger evaluation design.

# # Data paths
# 
# This R Markdown assumes the files are stored in `data/raw/` inside the R project.
# The path chunk below uses the same direct `here("data", "raw", ...)` structure you were already using.

# Put the input files in data/raw/ inside this R project, or edit the paths below.
hhs_file <- here("data", "raw", "all_hhs_moz.csv")

# Optional context files, used mainly for checking the project context.
# The core HHS analysis below uses the HHS file and the cleaned crosswalk created in the Rmd.
annex_file <- here(
  "data", "raw",
  "ANNEX III_BAF_EbA_Rare_Site Specific Information_2022-06-20(1).xlsx"
)

beneficiary_file <- here(
  "data", "raw",
  "Beneficiaries and overlaps(1).xlsx"
)



# Load data
hhs_raw <- read_csv(hhs_file, show_col_types = FALSE) %>%
  clean_names()

# The two Excel files are used mainly to define the program context and project-site crosswalk.
# The HHS analysis itself uses `all_hhs_moz.csv`.


# Inspect Excel sheets only if the optional files are present.
# This prevents the Rmd from failing when you only have the HHS CSV available.

if (file.exists(annex_file)) {
  annex_sheets <- excel_sheets(annex_file)
  annex_sheets
} else {
  annex_sheets <- character(0)
  message("Optional annex Excel file not found; continuing with HHS-only analysis.")
}

if (file.exists(beneficiary_file)) {
  beneficiary_sheets <- excel_sheets(beneficiary_file)
  beneficiary_sheets
} else {
  beneficiary_sheets <- character(0)
  message("Optional beneficiary Excel file not found; continuing with HHS-only analysis.")
}


# These objects are useful for checking the original context files.
# The actual analysis below uses a cleaned project-site crosswalk because the Excel files contain merged cells and notes.
# They are optional: if the files are not present, the Rmd still runs.

if (file.exists(annex_file)) {
  livelihoods_raw <- read_excel(annex_file, sheet = "3. Livelihoods") %>%
    clean_names()
} else {
  livelihoods_raw <- NULL
}

if (file.exists(beneficiary_file)) {
  beneficiaries_raw <- read_excel(beneficiary_file, sheet = "Target Reached Individuals") %>%
    clean_names()
} else {
  beneficiaries_raw <- NULL
}


# Helper functions
clean_place <- function(x) {
  x %>%
    as.character() %>%
    iconv(from = "UTF-8", to = "ASCII//TRANSLIT") %>%
    str_to_lower() %>%
    str_replace_all("\\s+", " ") %>%
    str_squish() %>%
    str_replace_all("\\s*-\\s*", "-")
}

mean_na <- function(x) {
  if (all(is.na(x))) {
    return(NA_real_)
  }

  mean(x, na.rm = TRUE)
}

median_na <- function(x) {
  if (all(is.na(x))) {
    return(NA_real_)
  }

  median(x, na.rm = TRUE)
}

p25_na <- function(x) {
  if (all(is.na(x))) {
    return(NA_real_)
  }

  as.numeric(quantile(x, 0.25, na.rm = TRUE))
}

p75_na <- function(x) {
  if (all(is.na(x))) {
    return(NA_real_)
  }

  as.numeric(quantile(x, 0.75, na.rm = TRUE))
}

2. Project-site context and crosswalk

Important harmonization decisions:

  • Memba and Memba-sede in the HHS were treated as Memba Sede.
  • Insular and Ilha Insular were treated as Ilha Insular.
  • Mahilene and Mahelene were treated as Mahelene.
  • Namige-sede / Namalungo in the livelihoods sheet is ambiguous because one row combines Namige-sede and Namalungo, while another row lists Namalungo separately.
program_context <- tribble(
  ~project_site,    ~district,              ~population_total, ~reached_individuals, ~program_maturity,            ~fmp_status,                                      ~main_livelihood_package,                                                                 ~site_specific_notes,
  "Sanculo",        "Ilha de Mocambique",    38195,             2022,                 "Former/older Rare site",    "FMP approved district/provincial; national review", "Microenterprises; regenerative agriculture; TVET; markets; poultry; nutrition; aquaculture", "Aquaculture active in Sanculo; two tanks completed and one group started fish farming",
  "Ilha Insular",   "Ilha de Mocambique",     9062,              728,                 "Former/older Rare site",    "FMP approved district/provincial; national review", "Microenterprises; regenerative agriculture; TVET; markets; poultry; nutrition",             "Full behavior adoption campaign implemented in Ilha district",
  "Quissanga",      "Ilha de Mocambique",    18553,              775,                 "Former/older Rare site",    "FMP approved district/provincial; national review", "Microenterprises; regenerative agriculture; TVET; markets; poultry; nutrition",             "Aquaculture group reportedly resettled and reallocated to agriculture",
  "Memba Sede",     "Memba",                 22572,             2026,                 "Former/older Rare site",    "FMP approved district/provincial; national review", "Microenterprises; regenerative agriculture; TVET; markets; poultry; nutrition",             "Watch tower handed over; mangrove and seagrass restoration relevant; mid-term HHS delayed due to security",
  "Baixo Pinda",    "Memba",                  7122,              998,                 "Former/older Rare site",    "FMP approved district/provincial; national review", "Microenterprises; regenerative agriculture; TVET; markets; poultry; nutrition",             "CCP room completed but handover delayed by security; mangrove/seagrass restoration relevant",
  "Namige Sede",    "Mogincual",             65890,             1735,                 "Newer expansion site",      "FMP under development / approval process",          "Microenterprises; regenerative agriculture; TVET; markets; poultry; nutrition; apiculture", "Mangrove restoration and hydrological restoration relevant",
  "Namalungo",      "Mogincual",              5000,             1793,                 "Newer expansion site",      "FMP under development / approval process",          "Microenterprises; regenerative agriculture; TVET; markets; poultry; nutrition; apiculture", "CCP room under construction; beekeeping kits replaced after protests/cyclone losses",
  "Meculuvelane",   "Mogincual",              5117,              856,                 "Newer expansion site",      "FMP under development / approval process",          "Microenterprises; regenerative agriculture; TVET; markets; poultry; nutrition; apiculture", "CCP room under construction; disaster-risk committees revitalized",
  "Quissimajulo",   "Nacala Porto",           9411,             1892,                 "Newer expansion site",      "FMP under development / approval process",          "Microenterprises; regenerative agriculture; TVET; markets; poultry; nutrition",             "CCVA results validated with Quissimajulo and Mahelene; CCP room under construction",
  "Mahelene",       "Nacala Porto",           4118,             1832,                 "Newer expansion site",      "FMP under development / approval process",          "Microenterprises; regenerative agriculture; TVET; markets; poultry; nutrition",             "CCVA results validated; land/permits secured for CCP room; some cooperative governance concerns noted"
) %>%
  mutate(
    project_site_clean = clean_place(project_site),
    district_clean = clean_place(district)
  )

program_context %>%
  select(project_site, district, population_total, reached_individuals, program_maturity, fmp_status, site_specific_notes) %>%
  kable(caption = "Project-site context")
Project-site context
project_site district population_total reached_individuals program_maturity fmp_status site_specific_notes
Sanculo Ilha de Mocambique 38195 2022 Former/older Rare site FMP approved district/provincial; national review Aquaculture active in Sanculo; two tanks completed and one group started fish farming
Ilha Insular Ilha de Mocambique 9062 728 Former/older Rare site FMP approved district/provincial; national review Full behavior adoption campaign implemented in Ilha district
Quissanga Ilha de Mocambique 18553 775 Former/older Rare site FMP approved district/provincial; national review Aquaculture group reportedly resettled and reallocated to agriculture
Memba Sede Memba 22572 2026 Former/older Rare site FMP approved district/provincial; national review Watch tower handed over; mangrove and seagrass restoration relevant; mid-term HHS delayed due to security
Baixo Pinda Memba 7122 998 Former/older Rare site FMP approved district/provincial; national review CCP room completed but handover delayed by security; mangrove/seagrass restoration relevant
Namige Sede Mogincual 65890 1735 Newer expansion site FMP under development / approval process Mangrove restoration and hydrological restoration relevant
Namalungo Mogincual 5000 1793 Newer expansion site FMP under development / approval process CCP room under construction; beekeeping kits replaced after protests/cyclone losses
Meculuvelane Mogincual 5117 856 Newer expansion site FMP under development / approval process CCP room under construction; disaster-risk committees revitalized
Quissimajulo Nacala Porto 9411 1892 Newer expansion site FMP under development / approval process CCVA results validated with Quissimajulo and Mahelene; CCP room under construction
Mahelene Nacala Porto 4118 1832 Newer expansion site FMP under development / approval process CCVA results validated; land/permits secured for CCP room; some cooperative governance concerns noted
hhs <- hhs_raw %>%
  mutate(
    year = as.integer(year),
    site_name = as.character(g1_community),
    community_clean = clean_place(g1_community),
    municipality_clean = clean_place(g1_municipality),
    province_clean = clean_place(g1_province),
    project_site = case_when(
      community_clean %in% c("sanculo") ~ "Sanculo",
      community_clean %in% c("ilha insular", "insular") ~ "Ilha Insular",
      community_clean %in% c("quissanga") ~ "Quissanga",
      community_clean %in% c("memba", "memba-sede", "memba sede") ~ "Memba Sede",
      community_clean %in% c("baixo pinda", "baixo  pinda") ~ "Baixo Pinda",
      community_clean %in% c("namige sede", "namige-sede") ~ "Namige Sede",
      community_clean %in% c("namalungo") ~ "Namalungo",
      community_clean %in% c("meculuvelane") ~ "Meculuvelane",
      community_clean %in% c("quissimajulo") ~ "Quissimajulo",
      community_clean %in% c("mahelene", "mahilene") ~ "Mahelene",
      TRUE ~ NA_character_
    ),
    is_project_site = !is.na(project_site)
  ) %>%
  left_join(program_context, by = "project_site")

3. General coverage analysis: complete Mozambique HHS

Overall coverage

coverage_summary <- tibble(
  records = nrow(hhs),
  years = paste(sort(unique(hhs$year)), collapse = ", "),
  n_years = n_distinct(hhs$year),
  n_provinces = n_distinct(hhs$g1_province),
  n_municipalities = n_distinct(hhs$g1_municipality),
  n_communities = n_distinct(hhs$g1_community),
  project_site_records = sum(hhs$is_project_site),
  non_project_site_records = sum(!hhs$is_project_site),
  pct_records_in_project_sites = project_site_records / records * 100
)

coverage_summary %>%
  mutate(across(where(is.numeric), ~round(.x, 1))) %>%
  kable(caption = "Overall HHS coverage")
Overall HHS coverage
records years n_years n_provinces n_municipalities n_communities project_site_records non_project_site_records pct_records_in_project_sites
7297 2019, 2021, 2023, 2024, 2025, 2026 6 4 9 27 3894 3403 53.4

Records by year

records_by_year <- hhs %>%
  count(year, name = "n_hhs") %>%
  arrange(year)

records_by_year %>%
  kable(caption = "HHS records by survey year")
HHS records by survey year
year n_hhs
2019 1460
2021 2493
2023 313
2024 711
2025 1865
2026 455
ggplot(records_by_year, aes(x = factor(year), y = n_hhs)) +
  geom_col(width = 0.7) +
  geom_text(aes(label = comma(n_hhs)), vjust = -0.25, size = 3.5) +
  scale_y_continuous(labels = comma, expand = expansion(mult = c(0, 0.08))) +
  labs(
    title = "Mozambique HHS coverage by year",
    x = "Survey year",
    y = "Number of HHS records"
  )

Records by province and year

records_by_province_year <- hhs %>%
  count(g1_province, year, name = "n_hhs") %>%
  arrange(g1_province, year)

records_by_province_year %>%
  kable(caption = "HHS records by province and year")
HHS records by province and year
g1_province year n_hhs
Inhambane 2019 560
Inhambane 2021 1288
Inhambane 2025 600
Maputo 2019 165
Maputo 2021 152
Nampula 2019 534
Nampula 2021 1024
Nampula 2023 313
Nampula 2024 711
Nampula 2025 1265
Nampula 2026 455
Sofala 2019 201
Sofala 2021 29
ggplot(records_by_province_year, aes(x = factor(year), y = n_hhs, fill = g1_province)) +
  geom_col(width = 0.75) +
  scale_y_continuous(labels = comma) +
  labs(
    title = "HHS coverage by province and year",
    x = "Survey year",
    y = "Number of HHS records",
    fill = "Province"
  ) +
  theme(legend.position = "bottom")

Records by municipality and year

records_by_municipality_year <- hhs %>%
  count(g1_province, g1_municipality, year, name = "n_hhs") %>%
  arrange(g1_province, g1_municipality, year)

records_by_municipality_year %>%
  kable(caption = "HHS records by municipality and year")
HHS records by municipality and year
g1_province g1_municipality year n_hhs
Inhambane Inharrime 2019 223
Inhambane Inharrime 2021 228
Inhambane Inhassoro 2019 197
Inhambane Inhassoro 2021 923
Inhambane Inhassoro 2025 600
Inhambane Massinga 2019 140
Inhambane Massinga 2021 137
Maputo Matutuíne 2019 165
Maputo Matutuíne 2021 152
Nampula Ilha de Mocambique 2019 327
Nampula Ilha de Mocambique 2021 322
Nampula Ilha de Mocambique 2023 313
Nampula Ilha de Mocambique 2025 957
Nampula Memba 2019 207
Nampula Memba 2021 702
Nampula Memba 2024 145
Nampula Memba 2026 274
Nampula Mogincual 2024 306
Nampula Mogincual 2025 308
Nampula Nacala Porto 2024 260
Nampula Nacala Porto 2026 181
Sofala Dondo 2019 201
Sofala Dondo 2021 29
ggplot(records_by_municipality_year,
       aes(x = factor(year), y = fct_reorder(g1_municipality, n_hhs, .fun = sum), fill = n_hhs)) +
  geom_tile(color = "white") +
  geom_text(aes(label = if_else(n_hhs == 0, "", as.character(n_hhs))), size = 3) +
  scale_fill_gradient(low = "grey95", high = "grey25", labels = comma) +
  labs(
    title = "HHS coverage by municipality and year",
    x = "Survey year",
    y = "Municipality",
    fill = "HHS records"
  )

Records by community / site

community_coverage <- hhs %>%
  count(g1_province, g1_municipality, g1_community, year, name = "n_hhs") %>%
  arrange(g1_province, g1_municipality, g1_community, year)

community_totals <- hhs %>%
  count(g1_province, g1_municipality, g1_community, name = "n_hhs") %>%
  arrange(desc(n_hhs))

community_totals %>%
  kable(caption = "Total HHS records by community / site")
Total HHS records by community / site
g1_province g1_municipality g1_community n_hhs
Nampula Ilha de Mocambique Ilha Insular 1124
Inhambane Inhassoro Fequete 500
Inhambane Inharrime Zavora 451
Nampula Ilha de Mocambique Sanculo 430
Nampula Memba Memba 420
Nampula Memba Baixo Pinda 382
Nampula Ilha de Mocambique Quissanga 365
Inhambane Inhassoro Tsondzo 324
Nampula Mogincual Namige Sede 308
Inhambane Inhassoro Mucocuene 305
Inhambane Massinga Pomene 277
Nampula Mogincual Namalungo 263
Nampula Nacala Porto Mahelene 244
Inhambane Inhassoro Nhagondzo 223
Nampula Memba Serissa 207
Nampula Nacala Porto Quissimajulo 197
Inhambane Inhassoro Vuca 195
Maputo Matutuíne Santa Maria 192
Nampula Memba Simuco 180
Nampula Memba Memba-sede 139
Maputo Matutuíne Mabuluku 125
Sofala Dondo Sengo 123
Sofala Dondo Farol 107
Inhambane Inhassoro Petane 99
Inhambane Inhassoro Petane1 74
Nampula Mogincual Meculuvelane 22
Nampula Mogincual Maculuvelane 21
ggplot(community_totals,
       aes(x = n_hhs, y = fct_reorder(g1_community, n_hhs))) +
  geom_col(width = 0.7) +
  scale_x_continuous(labels = comma) +
  labs(
    title = "HHS coverage by community / site",
    x = "Number of HHS records",
    y = "Community / site"
  )

Community-year coverage heatmap

community_year_complete <- hhs %>%
  count(g1_community, year, name = "n_hhs") %>%
  complete(g1_community, year = sort(unique(hhs$year)), fill = list(n_hhs = 0))

ggplot(community_year_complete,
       aes(x = factor(year), y = fct_reorder(g1_community, n_hhs, .fun = sum), fill = n_hhs)) +
  geom_tile(color = "white") +
  geom_text(aes(label = if_else(n_hhs == 0, "", as.character(n_hhs))), size = 2.7) +
  scale_fill_gradient(low = "grey95", high = "grey25", labels = comma) +
  labs(
    title = "HHS coverage by community / site and year",
    subtitle = "Blank cells indicate no HHS records in that site-year",
    x = "Survey year",
    y = "Community / site",
    fill = "HHS records"
  )

4. Coverage analysis: BAF / Rare project sites

Project-site records by year

project_site_coverage <- hhs %>%
  filter(is_project_site) %>%
  count(project_site, district, program_maturity, year, name = "n_hhs") %>%
  complete(project_site, year = sort(unique(hhs$year)), fill = list(n_hhs = 0)) %>%
  group_by(project_site) %>%
  fill(district, program_maturity, .direction = "downup") %>%
  ungroup() %>%
  arrange(district, project_site, year)

project_site_coverage %>%
  kable(caption = "Project-site HHS records by year")
Project-site HHS records by year
project_site year district program_maturity n_hhs
Ilha Insular 2019 Ilha de Mocambique Former/older Rare site 327
Ilha Insular 2021 Ilha de Mocambique Former/older Rare site 125
Ilha Insular 2023 Ilha de Mocambique Former/older Rare site 106
Ilha Insular 2024 Ilha de Mocambique Former/older Rare site 0
Ilha Insular 2025 Ilha de Mocambique Former/older Rare site 566
Ilha Insular 2026 Ilha de Mocambique Former/older Rare site 0
Quissanga 2019 Ilha de Mocambique Former/older Rare site 0
Quissanga 2021 Ilha de Mocambique Former/older Rare site 91
Quissanga 2023 Ilha de Mocambique Former/older Rare site 103
Quissanga 2024 Ilha de Mocambique Former/older Rare site 0
Quissanga 2025 Ilha de Mocambique Former/older Rare site 171
Quissanga 2026 Ilha de Mocambique Former/older Rare site 0
Sanculo 2019 Ilha de Mocambique Former/older Rare site 0
Sanculo 2021 Ilha de Mocambique Former/older Rare site 106
Sanculo 2023 Ilha de Mocambique Former/older Rare site 104
Sanculo 2024 Ilha de Mocambique Former/older Rare site 0
Sanculo 2025 Ilha de Mocambique Former/older Rare site 220
Sanculo 2026 Ilha de Mocambique Former/older Rare site 0
Baixo Pinda 2019 Memba Former/older Rare site 1
Baixo Pinda 2021 Memba Former/older Rare site 101
Baixo Pinda 2023 Memba Former/older Rare site 0
Baixo Pinda 2024 Memba Former/older Rare site 145
Baixo Pinda 2025 Memba Former/older Rare site 0
Baixo Pinda 2026 Memba Former/older Rare site 135
Memba Sede 2019 Memba Former/older Rare site 206
Memba Sede 2021 Memba Former/older Rare site 214
Memba Sede 2023 Memba Former/older Rare site 0
Memba Sede 2024 Memba Former/older Rare site 0
Memba Sede 2025 Memba Former/older Rare site 0
Memba Sede 2026 Memba Former/older Rare site 139
Meculuvelane 2019 Mogincual Newer expansion site 0
Meculuvelane 2021 Mogincual Newer expansion site 0
Meculuvelane 2023 Mogincual Newer expansion site 0
Meculuvelane 2024 Mogincual Newer expansion site 0
Meculuvelane 2025 Mogincual Newer expansion site 22
Meculuvelane 2026 Mogincual Newer expansion site 0
Namalungo 2019 Mogincual Newer expansion site 0
Namalungo 2021 Mogincual Newer expansion site 0
Namalungo 2023 Mogincual Newer expansion site 0
Namalungo 2024 Mogincual Newer expansion site 134
Namalungo 2025 Mogincual Newer expansion site 129
Namalungo 2026 Mogincual Newer expansion site 0
Namige Sede 2019 Mogincual Newer expansion site 0
Namige Sede 2021 Mogincual Newer expansion site 0
Namige Sede 2023 Mogincual Newer expansion site 0
Namige Sede 2024 Mogincual Newer expansion site 151
Namige Sede 2025 Mogincual Newer expansion site 157
Namige Sede 2026 Mogincual Newer expansion site 0
Mahelene 2019 Nacala Porto Newer expansion site 0
Mahelene 2021 Nacala Porto Newer expansion site 0
Mahelene 2023 Nacala Porto Newer expansion site 0
Mahelene 2024 Nacala Porto Newer expansion site 134
Mahelene 2025 Nacala Porto Newer expansion site 0
Mahelene 2026 Nacala Porto Newer expansion site 110
Quissimajulo 2019 Nacala Porto Newer expansion site 0
Quissimajulo 2021 Nacala Porto Newer expansion site 0
Quissimajulo 2023 Nacala Porto Newer expansion site 0
Quissimajulo 2024 Nacala Porto Newer expansion site 126
Quissimajulo 2025 Nacala Porto Newer expansion site 0
Quissimajulo 2026 Nacala Porto Newer expansion site 71
ggplot(project_site_coverage,
       aes(x = factor(year), y = fct_reorder(project_site, n_hhs, .fun = sum), fill = n_hhs)) +
  geom_tile(color = "white") +
  geom_text(aes(label = if_else(n_hhs == 0, "", as.character(n_hhs))), size = 3) +
  scale_fill_gradient(low = "grey95", high = "grey25", labels = comma) +
  labs(
    title = "Project-site HHS coverage by year",
    subtitle = "This is the main sample-coverage check before comparing site-year scores",
    x = "Survey year",
    y = "Project site",
    fill = "HHS records"
  )

Project-site coverage summary

project_site_coverage_summary <- hhs %>%
  filter(is_project_site) %>%
  group_by(project_site, district, program_maturity) %>%
  summarise(
    n_hhs = n(),
    years_covered = paste(sort(unique(year)), collapse = ", "),
    n_years = n_distinct(year),
    first_year = min(year),
    last_year = max(year),
    .groups = "drop"
  ) %>%
  left_join(
    program_context %>%
      select(project_site, population_total, reached_individuals, fmp_status, site_specific_notes),
    by = "project_site"
  ) %>%
  arrange(district, project_site)

project_site_coverage_summary %>%
  kable(caption = "Project-site coverage summary")
Project-site coverage summary
project_site district program_maturity n_hhs years_covered n_years first_year last_year population_total reached_individuals fmp_status site_specific_notes
Ilha Insular Ilha de Mocambique Former/older Rare site 1124 2019, 2021, 2023, 2025 4 2019 2025 9062 728 FMP approved district/provincial; national review Full behavior adoption campaign implemented in Ilha district
Quissanga Ilha de Mocambique Former/older Rare site 365 2021, 2023, 2025 3 2021 2025 18553 775 FMP approved district/provincial; national review Aquaculture group reportedly resettled and reallocated to agriculture
Sanculo Ilha de Mocambique Former/older Rare site 430 2021, 2023, 2025 3 2021 2025 38195 2022 FMP approved district/provincial; national review Aquaculture active in Sanculo; two tanks completed and one group started fish farming
Baixo Pinda Memba Former/older Rare site 382 2019, 2021, 2024, 2026 4 2019 2026 7122 998 FMP approved district/provincial; national review CCP room completed but handover delayed by security; mangrove/seagrass restoration relevant
Memba Sede Memba Former/older Rare site 559 2019, 2021, 2026 3 2019 2026 22572 2026 FMP approved district/provincial; national review Watch tower handed over; mangrove and seagrass restoration relevant; mid-term HHS delayed due to security
Meculuvelane Mogincual Newer expansion site 22 2025 1 2025 2025 5117 856 FMP under development / approval process CCP room under construction; disaster-risk committees revitalized
Namalungo Mogincual Newer expansion site 263 2024, 2025 2 2024 2025 5000 1793 FMP under development / approval process CCP room under construction; beekeeping kits replaced after protests/cyclone losses
Namige Sede Mogincual Newer expansion site 308 2024, 2025 2 2024 2025 65890 1735 FMP under development / approval process Mangrove restoration and hydrological restoration relevant
Mahelene Nacala Porto Newer expansion site 244 2024, 2026 2 2024 2026 4118 1832 FMP under development / approval process CCVA results validated; land/permits secured for CCP room; some cooperative governance concerns noted
Quissimajulo Nacala Porto Newer expansion site 197 2024, 2026 2 2024 2026 9411 1892 FMP under development / approval process CCVA results validated with Quissimajulo and Mahelene; CCP room under construction

Low sample-size warnings for project-site-year estimates

low_sample_project_site_years <- project_site_coverage %>%
  filter(n_hhs > 0, n_hhs < 30) %>%
  arrange(n_hhs)

low_sample_project_site_years %>%
  kable(caption = "Project-site-years with fewer than 30 HHS records")
Project-site-years with fewer than 30 HHS records
project_site year district program_maturity n_hhs
Baixo Pinda 2019 Memba Former/older Rare site 1
Meculuvelane 2025 Mogincual Newer expansion site 22

5. Impact Framework indicators

Indicator interpretation

The analysis focuses on two Impact Framework components that are relevant to the Composite Coastal Resilience Framework case study: Capacity for Collective Action and Sustainable Livelihoods.

Capacity for Collective Action reflects whether community members perceive that they are part of a fair, trusted, capable, and participatory local fisheries governance system. In the context of Fisheries OECMs, this component is important because durable conservation and fisheries-management outcomes depend not only on rules or management plans, but also on whether communities believe they can act collectively to manage resources.

The component is summarized using four indicators:

  1. Social Equity in Fisheries Benefits: the share of fishery-dependent respondents who believe they benefit equally from the fishery as other members of the community.

  2. Trust in Local Leadership: the share of respondents who trust local decision-makers or local authorities to make decisions that benefit the community over their own personal interests.

  3. Collective Efficacy for Fisheries Management Score: the share of respondents who believe their community has the ability to manage the fishery effectively and maximize food and profits.

  4. Empowerment & Participation in Management: the share of respondents who believe that local community participation in management will help maintain or improve fish catch.

Together, these indicators provide a practical measure of whether local social and governance conditions are supportive of collective fisheries management.

Sustainable Livelihoods reflects whether households have sufficient economic and food-security resilience to support long-term engagement in fisheries management and conservation. In the context of Fisheries OECMs, this component matters because households facing severe financial stress or food insecurity may have less capacity to comply with management rules, participate in governance, or invest in alternative livelihood strategies.

The component is summarized using two indicators:

  1. Household can make ends meet: the share of respondents who report that their household can cover its needs fairly easily, easily, or very easily.

  2. Food security: the share of respondents who report that they never worried about not having enough food for everyone in the household during the last 12 months.

For interpretation, the analysis also presents diagnostic negative indicators for financial strain and food worry. These are useful for communicating the livelihood vulnerability behind the Sustainable Livelihoods score.

agree_values <- c("Agree", "Strongly agree")

hhs_if <- hhs %>%
  mutate(
    # Capacity for Collective Action
    social_equity = case_when(
      g8_fishery_benefit_equal == "Yes" ~ 1,
      g8_fishery_benefit_equal == "No" ~ 0,
      g8_fishery_benefit_equal == "I don’t depend on or benefit from the fishery" ~ NA_real_,
      TRUE ~ NA_real_
    ),
    social_equity_sensitivity_nonbenefit_0 = case_when(
      g8_fishery_benefit_equal == "Yes" ~ 1,
      g8_fishery_benefit_equal %in% c("No", "I don’t depend on or benefit from the fishery") ~ 0,
      TRUE ~ NA_real_
    ),
    leadership_trust_local = case_when(
      g8_trust_local_decision %in% agree_values ~ 1,
      !is.na(g8_trust_local_decision) ~ 0,
      TRUE ~ NA_real_
    ),
    collective_efficacy = case_when(
      g8_my_community_ability %in% agree_values ~ 1,
      !is.na(g8_my_community_ability) ~ 0,
      TRUE ~ NA_real_
    ),
    empowerment_participation = case_when(
      g12_agreement_community_participation %in% agree_values ~ 1,
      !is.na(g12_agreement_community_participation) ~ 0,
      TRUE ~ NA_real_
    ),

    # Sustainable Livelihoods
    ends_meet_positive = case_when(
      g13_hh_ends_meet %in% c("Fairly easy", "Easy", "Very easy") ~ 1,
      !is.na(g13_hh_ends_meet) ~ 0,
      TRUE ~ NA_real_
    ),
    food_security_positive = case_when(
      g11_food_worry == "Never" ~ 1,
      g11_food_worry %in% c("Sometimes", "Often") ~ 0,
      TRUE ~ NA_real_
    ),

    # Diagnostic negative indicators
    financial_strain = case_when(
      g13_hh_ends_meet %in% c("With difficulty", "With great difficulty") ~ 1,
      g13_hh_ends_meet %in% c("Fairly easy", "Easy", "Very easy") ~ 0,
      TRUE ~ NA_real_
    ),
    food_worry_any = case_when(
      g11_food_worry %in% c("Sometimes", "Often") ~ 1,
      g11_food_worry == "Never" ~ 0,
      TRUE ~ NA_real_
    ),
    food_worry_often = case_when(
      g11_food_worry == "Often" ~ 1,
      g11_food_worry %in% c("Sometimes", "Never") ~ 0,
      TRUE ~ NA_real_
    )
  )
# Indicator metadata
core_indicator_vars <- c(
  "social_equity",
  "leadership_trust_local",
  "collective_efficacy",
  "empowerment_participation",
  "ends_meet_positive",
  "food_security_positive"
)

diagnostic_indicator_vars <- c(
  "financial_strain",
  "food_worry_any",
  "food_worry_often"
)

indicator_vars <- c(core_indicator_vars, diagnostic_indicator_vars)

indicator_meta <- tribble(
  ~indicator, ~domain, ~label, ~direction,
  "social_equity", "Capacity for Collective Action", "Social Equity in Fisheries Benefits", "positive_core",
  "leadership_trust_local", "Capacity for Collective Action", "Trust in Local Leadership", "positive_core",
  "collective_efficacy", "Capacity for Collective Action", "Collective Efficacy for Fisheries Management Score", "positive_core",
  "empowerment_participation", "Capacity for Collective Action", "Empowerment & Participation in Management", "positive_core",
  "ends_meet_positive", "Sustainable Livelihoods", "Household covers needs", "positive_core",
  "food_security_positive", "Sustainable Livelihoods", "Never worried about enough food", "positive_core",
  "financial_strain", "Sustainable Livelihoods", "Household covers needs with difficulty", "diagnostic_negative",
  "food_worry_any", "Sustainable Livelihoods", "Sometimes/often worried about food", "diagnostic_negative",
  "food_worry_often", "Sustainable Livelihoods", "Often worried about food", "diagnostic_negative"
)

Summary functions

Domain scores are calculated as the simple average of the positive indicator percentages within each domain. This means each indicator receives equal weight within its domain.

Error bars in the score plots show approximate 95% confidence intervals. For single indicators, the intervals are calculated from the respondent-level variation in the binary indicator. For composite domain scores, the intervals are approximated from the component-indicator standard errors under the same equal-weighting logic. Treat these as descriptive uncertainty intervals rather than formal causal inference intervals.

pct_percent <- function(x) {
  mean_na(x) * 100
}

se_percent <- function(x) {
  n_valid <- sum(!is.na(x))

  if (n_valid <= 1) {
    return(NA_real_)
  }

  sd(x, na.rm = TRUE) / sqrt(n_valid) * 100
}

ci_low_percent <- function(x) {
  pct <- pct_percent(x)
  se <- se_percent(x)

  if (is.na(pct) || is.na(se)) {
    return(NA_real_)
  }

  pmax(0, pct - 1.96 * se)
}

ci_high_percent <- function(x) {
  pct <- pct_percent(x)
  se <- se_percent(x)

  if (is.na(pct) || is.na(se)) {
    return(NA_real_)
  }

  pmin(100, pct + 1.96 * se)
}

summarise_indicators <- function(data, group_vars) {
  data %>%
    group_by(across(all_of(group_vars))) %>%
    summarise(
      n_hhs = n(),
      median_income = median_na(g13_hh_average_income),
      income_p25 = p25_na(g13_hh_average_income),
      income_p75 = p75_na(g13_hh_average_income),
      across(
        all_of(indicator_vars),
        list(
          n_valid = ~sum(!is.na(.x)),
          pct = ~pct_percent(.x),
          se = ~se_percent(.x),
          ci_low = ~ci_low_percent(.x),
          ci_high = ~ci_high_percent(.x)
        ),
        .names = "{.col}__{.fn}"
      ),
      .groups = "drop"
    ) %>%
    pivot_longer(
      cols = matches("__"),
      names_to = c("indicator", ".value"),
      names_sep = "__"
    ) %>%
    left_join(indicator_meta, by = "indicator")
}

make_domain_scores <- function(indicator_summary, group_vars) {
  indicator_summary %>%
    filter(direction == "positive_core") %>%
    group_by(across(all_of(group_vars)), domain) %>%
    summarise(
      n_hhs = first(n_hhs),
      domain_score = mean_na(pct),
      domain_se = {
        n_indicators <- sum(!is.na(pct))
        se_vals <- se[!is.na(se)]

        if (n_indicators == 0 || length(se_vals) == 0) {
          NA_real_
        } else {
          sqrt(sum(se_vals^2)) / n_indicators
        }
      },
      ci_low = pmax(0, domain_score - 1.96 * domain_se),
      ci_high = pmin(100, domain_score + 1.96 * domain_se),
      n_indicators_available = sum(!is.na(pct)),
      min_n_valid = min(n_valid, na.rm = TRUE),
      .groups = "drop"
    ) %>%
    mutate(
      min_n_valid = if_else(is.infinite(min_n_valid), NA_real_, as.numeric(min_n_valid))
    )
}

Missingness in selected Impact Framework variables

impact_vars_raw <- c(
  "g8_fishery_benefit_equal",
  "g8_trust_local_decision",
  "g8_my_community_ability",
  "g12_agreement_community_participation",
  "g11_food_worry",
  "g13_hh_ends_meet",
  "g13_hh_average_income"
)

missingness <- hhs_if %>%
  summarise(across(
    all_of(impact_vars_raw),
    list(
      n_missing = ~sum(is.na(.x)),
      pct_missing = ~mean(is.na(.x)) * 100
    ),
    .names = "{.col}__{.fn}"
  )) %>%
  pivot_longer(
    everything(),
    names_to = c("variable", ".value"),
    names_sep = "__"
  ) %>%
  arrange(desc(pct_missing))

missingness %>%
  mutate(pct_missing = round(pct_missing, 1)) %>%
  kable(caption = "Missingness in selected Impact Framework variables")
Missingness in selected Impact Framework variables
variable n_missing pct_missing
g12_agreement_community_participation 3004 41.2
g13_hh_average_income 420 5.8
g13_hh_ends_meet 124 1.7
g8_my_community_ability 100 1.4
g11_food_worry 91 1.2
g8_fishery_benefit_equal 90 1.2
g8_trust_local_decision 35 0.5
ggplot(missingness,
       aes(x = pct_missing, y = fct_reorder(variable, pct_missing))) +
  geom_col(width = 0.7) +
  scale_x_continuous(labels = label_percent(scale = 1), limits = c(0, 100)) +
  labs(
    title = "Missingness in selected Impact Framework variables",
    x = "Missing responses",
    y = NULL
  )

6. Whole-dataset CCRF analysis

This section uses the complete Mozambique HHS dataset, not only project sites.

Overall indicator scores

overall_indicators <- hhs_if %>%
  mutate(analysis_group = "All Mozambique HHS") %>%
  summarise_indicators(group_vars = "analysis_group")

overall_indicators %>%
  select(domain, label, direction, n_hhs, n_valid, pct, se, ci_low, ci_high) %>%
  mutate(across(c(pct, se, ci_low, ci_high), ~round(.x, 1))) %>%
  arrange(domain, direction, desc(pct)) %>%
  kable(caption = "Overall indicator scores in the complete Mozambique HHS")
Overall indicator scores in the complete Mozambique HHS
domain label direction n_hhs n_valid pct se ci_low ci_high
Capacity for Collective Action Empowerment & Participation in Management positive_core 7297 4293 88.4 0.5 87.4 89.3
Capacity for Collective Action Social Equity in Fisheries Benefits positive_core 7297 6811 83.4 0.5 82.6 84.3
Capacity for Collective Action Trust in Local Leadership positive_core 7297 7262 80.6 0.5 79.6 81.5
Capacity for Collective Action Collective Efficacy for Fisheries Management Score positive_core 7297 7197 71.3 0.5 70.2 72.3
Sustainable Livelihoods Sometimes/often worried about food diagnostic_negative 7297 7206 88.3 0.4 87.5 89.0
Sustainable Livelihoods Household covers needs with difficulty diagnostic_negative 7297 7173 73.5 0.5 72.4 74.5
Sustainable Livelihoods Often worried about food diagnostic_negative 7297 7206 22.0 0.5 21.0 22.9
Sustainable Livelihoods Household covers needs positive_core 7297 7173 26.5 0.5 25.5 27.6
Sustainable Livelihoods Never worried about enough food positive_core 7297 7206 11.7 0.4 11.0 12.5
overall_indicators %>%
  filter(direction == "positive_core") %>%
  ggplot(aes(x = fct_reorder(label, pct), y = pct)) +
  geom_col(width = 0.7) +
  geom_errorbar(aes(ymin = ci_low, ymax = ci_high), width = 0.18) +
  coord_flip() +
  facet_wrap(~ domain, scales = "free_y") +
  scale_y_continuous(limits = c(0, 100), labels = label_percent(scale = 1)) +
  labs(
    title = "Overall positive indicator scores in the complete Mozambique HHS",
    subtitle = "Percent of valid responses coded as positive; error bars show approximate 95% CIs",
    x = NULL,
    y = "% positive"
  )

Overall domain scores

overall_domains <- overall_indicators %>%
  make_domain_scores(group_vars = "analysis_group")

overall_domains %>%
  mutate(across(c(domain_score, domain_se, ci_low, ci_high), ~round(.x, 1))) %>%
  kable(caption = "Overall CCRF domain scores in the complete Mozambique HHS")
Overall CCRF domain scores in the complete Mozambique HHS
analysis_group domain n_hhs domain_score domain_se ci_low ci_high n_indicators_available min_n_valid
All Mozambique HHS Capacity for Collective Action 7297 80.9 0.2 80.4 81.4 4 4293
All Mozambique HHS Sustainable Livelihoods 7297 19.1 0.3 18.5 19.8 2 7173
ggplot(overall_domains, aes(x = fct_reorder(domain, domain_score), y = domain_score)) +
  geom_col(width = 0.65) +
  geom_errorbar(aes(ymin = ci_low, ymax = ci_high), width = 0.16) +
  geom_text(aes(label = round(domain_score, 1)), vjust = -0.7, size = 4) +
  scale_y_continuous(limits = c(0, 100)) +
  labs(
    title = "Overall CCRF diagnostic scores in the complete Mozambique HHS",
    subtitle = "Error bars show approximate 95% CIs",
    x = NULL,
    y = "Mean domain score, 0–100"
  )

Whole-dataset analysis by year

Domain scores by year

year_indicators <- hhs_if %>%
  summarise_indicators(group_vars = "year")

year_domains <- year_indicators %>%
  make_domain_scores(group_vars = "year")

year_domains %>%
  mutate(across(c(domain_score, domain_se, ci_low, ci_high), ~round(.x, 1))) %>%
  arrange(year, domain) %>%
  kable(caption = "Domain scores by year, complete Mozambique HHS")
Domain scores by year, complete Mozambique HHS
year domain n_hhs domain_score domain_se ci_low ci_high n_indicators_available min_n_valid
2019 Capacity for Collective Action 1460 81.6 0.6 80.5 82.7 4 757
2019 Sustainable Livelihoods 1460 26.2 0.8 24.6 27.8 2 1400
2021 Capacity for Collective Action 2493 85.6 0.4 84.8 86.3 4 1181
2021 Sustainable Livelihoods 2493 19.4 0.6 18.3 20.5 2 2425
2023 Capacity for Collective Action 313 95.8 0.6 94.7 96.9 4 261
2023 Sustainable Livelihoods 313 9.6 1.1 7.4 11.8 2 313
2024 Capacity for Collective Action 711 86.6 0.6 85.4 87.8 4 548
2024 Sustainable Livelihoods 711 21.7 1.1 19.6 23.9 2 711
2025 Capacity for Collective Action 1865 73.8 0.5 72.8 74.9 4 1250
2025 Sustainable Livelihoods 1865 13.4 0.5 12.4 14.5 2 1865
2026 Capacity for Collective Action 455 62.0 1.1 59.8 64.2 4 296
2026 Sustainable Livelihoods 455 21.9 1.3 19.4 24.4 2 455
ggplot(year_domains,
       aes(x = year, y = domain_score, group = domain, linetype = domain)) +
  geom_errorbar(aes(ymin = ci_low, ymax = ci_high), width = 0.12, alpha = 0.7) +
  geom_line(linewidth = 0.8) +
  geom_point(size = 2.4) +
  scale_y_continuous(limits = c(0, 100), breaks = seq(0, 100, 25)) +
  scale_x_continuous(breaks = sort(unique(year_domains$year))) +
  labs(
    title = "CCRF domain scores by year: complete Mozambique HHS",
    subtitle = "Use cautiously: site composition varies across years; error bars show approximate 95% CIs",
    x = "Survey year",
    y = "Mean domain score, 0–100",
    linetype = "Domain"
  ) +
  theme(legend.position = "bottom")

Indicator scores by year

year_indicators %>%
  filter(direction == "positive_core") %>%
  mutate(across(c(pct, se, ci_low, ci_high), ~round(.x, 1))) %>%
  select(year, domain, label, n_hhs, n_valid, pct, se, ci_low, ci_high) %>%
  arrange(year, domain, label) %>%
  kable(caption = "Positive indicator scores by year, complete Mozambique HHS")
Positive indicator scores by year, complete Mozambique HHS
year domain label n_hhs n_valid pct se ci_low ci_high
2019 Capacity for Collective Action Collective Efficacy for Fisheries Management Score 1460 1389 77.9 1.1 75.7 80.1
2019 Capacity for Collective Action Empowerment & Participation in Management 1460 757 89.0 1.1 86.8 91.3
2019 Capacity for Collective Action Social Equity in Fisheries Benefits 1460 1203 78.2 1.2 75.9 80.6
2019 Capacity for Collective Action Trust in Local Leadership 1460 1440 81.3 1.0 79.3 83.3
2019 Sustainable Livelihoods Household covers needs 1460 1404 33.8 1.3 31.4 36.3
2019 Sustainable Livelihoods Never worried about enough food 1460 1400 18.6 1.0 16.6 20.7
2021 Capacity for Collective Action Collective Efficacy for Fisheries Management Score 2493 2464 81.2 0.8 79.7 82.8
2021 Capacity for Collective Action Empowerment & Participation in Management 2493 1181 88.7 0.9 86.8 90.5
2021 Capacity for Collective Action Social Equity in Fisheries Benefits 2493 2366 87.9 0.7 86.6 89.2
2021 Capacity for Collective Action Trust in Local Leadership 2493 2478 84.5 0.7 83.1 86.0
2021 Sustainable Livelihoods Household covers needs 2493 2425 25.6 0.9 23.9 27.4
2021 Sustainable Livelihoods Never worried about enough food 2493 2462 13.1 0.7 11.7 14.4
2023 Capacity for Collective Action Collective Efficacy for Fisheries Management Score 313 313 90.1 1.7 86.8 93.4
2023 Capacity for Collective Action Empowerment & Participation in Management 313 261 98.5 0.8 97.0 100.0
2023 Capacity for Collective Action Social Equity in Fisheries Benefits 313 312 98.7 0.6 97.5 100.0
2023 Capacity for Collective Action Trust in Local Leadership 313 313 95.8 1.1 93.6 98.1
2023 Sustainable Livelihoods Household covers needs 313 313 19.2 2.2 14.8 23.5
2023 Sustainable Livelihoods Never worried about enough food 313 313 0.0 0.0 0.0 0.0
2024 Capacity for Collective Action Collective Efficacy for Fisheries Management Score 711 711 72.2 1.7 68.9 75.4
2024 Capacity for Collective Action Empowerment & Participation in Management 711 548 97.3 0.7 95.9 98.6
2024 Capacity for Collective Action Social Equity in Fisheries Benefits 711 711 88.3 1.2 86.0 90.7
2024 Capacity for Collective Action Trust in Local Leadership 711 711 88.6 1.2 86.3 90.9
2024 Sustainable Livelihoods Household covers needs 711 711 20.1 1.5 17.2 23.1
2024 Sustainable Livelihoods Never worried about enough food 711 711 23.3 1.6 20.2 26.5
2025 Capacity for Collective Action Collective Efficacy for Fisheries Management Score 1865 1865 54.7 1.2 52.4 57.0
2025 Capacity for Collective Action Empowerment & Participation in Management 1865 1250 81.1 1.1 78.9 83.3
2025 Capacity for Collective Action Social Equity in Fisheries Benefits 1865 1790 82.7 0.9 81.0 84.5
2025 Capacity for Collective Action Trust in Local Leadership 1865 1865 76.8 1.0 74.9 78.7
2025 Sustainable Livelihoods Household covers needs 1865 1865 23.4 1.0 21.5 25.3
2025 Sustainable Livelihoods Never worried about enough food 1865 1865 3.5 0.4 2.7 4.3
2026 Capacity for Collective Action Collective Efficacy for Fisheries Management Score 455 455 51.2 2.3 46.6 55.8
2026 Capacity for Collective Action Empowerment & Participation in Management 455 296 90.9 1.7 87.6 94.2
2026 Capacity for Collective Action Social Equity in Fisheries Benefits 455 429 57.1 2.4 52.4 61.8
2026 Capacity for Collective Action Trust in Local Leadership 455 455 48.8 2.3 44.2 53.4
2026 Sustainable Livelihoods Household covers needs 455 455 36.7 2.3 32.3 41.1
2026 Sustainable Livelihoods Never worried about enough food 455 455 7.0 1.2 4.7 9.4
ggplot(
  year_indicators %>% filter(direction == "positive_core"),
  aes(
    x = year,
    y = pct,
    group = label,
    color = label
  )
) +
  geom_errorbar(
    aes(ymin = ci_low, ymax = ci_high),
    width = 0.10,
    alpha = 0.45,
    linewidth = 0.5
  ) +
  geom_line(linewidth = 0.8) +
  geom_point(size = 2) +
  facet_wrap(~ domain, ncol = 1) +
  scale_y_continuous(
    limits = c(0, 100),
    labels = scales::label_percent(scale = 1)
  ) +
  scale_x_continuous(
    breaks = sort(unique(year_indicators$year))
  ) +
  scale_color_brewer(palette = "Dark2") +
  labs(
    title = "Positive indicator scores by year: complete Mozambique HHS",
    subtitle = "Each line is one Impact Framework indicator; error bars show approximate 95% CIs",
    x = "Survey year",
    y = "% positive",
    color = "Indicator"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    legend.position = "bottom",
    legend.title = element_text(size = 11),
    legend.text = element_text(size = 10),
    strip.text = element_text(face = "bold")
  )

Whole-dataset analysis by site

Domain scores by site

site_indicators <- hhs_if %>%
  summarise_indicators(group_vars = c("site_name", "g1_municipality", "g1_province"))

site_domains <- site_indicators %>%
  make_domain_scores(group_vars = c("site_name", "g1_municipality", "g1_province"))

site_domains %>%
  mutate(across(c(domain_score, domain_se, ci_low, ci_high), ~round(.x, 1))) %>%
  arrange(domain, domain_score) %>%
  kable(caption = "Domain scores by site, complete Mozambique HHS")
Domain scores by site, complete Mozambique HHS
site_name g1_municipality g1_province domain n_hhs domain_score domain_se ci_low ci_high n_indicators_available min_n_valid
Quissanga Ilha de Mocambique Nampula Capacity for Collective Action 365 66.9 1.2 64.6 69.2 4 324
Baixo Pinda Memba Nampula Capacity for Collective Action 382 68.6 1.2 66.3 70.9 4 350
Memba-sede Memba Nampula Capacity for Collective Action 139 70.0 1.8 66.5 73.4 4 88
Petane Inhassoro Inhambane Capacity for Collective Action 99 70.0 2.3 65.5 74.5 4 33
Namalungo Mogincual Nampula Capacity for Collective Action 263 71.6 1.1 69.3 73.8 4 239
Petane1 Inhassoro Inhambane Capacity for Collective Action 74 71.7 3.1 65.7 77.7 4 21
Vuca Inhassoro Inhambane Capacity for Collective Action 195 76.4 1.6 73.3 79.5 4 88
Quissimajulo Nacala Porto Nampula Capacity for Collective Action 197 76.6 1.5 73.6 79.6 4 117
Fequete Inhassoro Inhambane Capacity for Collective Action 500 78.0 1.0 76.1 80.0 4 198
Mahelene Nacala Porto Nampula Capacity for Collective Action 244 79.4 1.3 76.8 82.0 4 163
Simuco Memba Nampula Capacity for Collective Action 180 79.5 1.5 76.6 82.4 4 11
Memba Memba Nampula Capacity for Collective Action 420 79.7 1.2 77.4 82.0 4 170
Maculuvelane Mogincual Nampula Capacity for Collective Action 21 81.0 2.8 75.5 86.4 4 21
Ilha Insular Ilha de Mocambique Nampula Capacity for Collective Action 1124 81.3 0.6 80.1 82.4 4 572
Pomene Massinga Inhambane Capacity for Collective Action 277 83.0 1.2 80.7 85.4 4 155
Nhagondzo Inhassoro Inhambane Capacity for Collective Action 223 84.2 1.2 81.8 86.6 4 152
Mucocuene Inhassoro Inhambane Capacity for Collective Action 305 85.1 1.1 83.1 87.2 4 118
Santa Maria Matutuíne Maputo Capacity for Collective Action 192 87.3 1.2 84.9 89.6 4 167
Serissa Memba Nampula Capacity for Collective Action 207 87.7 1.3 85.1 90.3 4 68
Tsondzo Inhassoro Inhambane Capacity for Collective Action 324 88.1 1.0 86.3 90.0 4 174
Namige Sede Mogincual Nampula Capacity for Collective Action 308 88.3 1.0 86.4 90.2 4 195
Mabuluku Matutuíne Maputo Capacity for Collective Action 125 88.5 1.4 85.7 91.3 4 115
Sanculo Ilha de Mocambique Nampula Capacity for Collective Action 430 89.2 0.7 87.8 90.7 4 395
Zavora Inharrime Inhambane Capacity for Collective Action 451 90.1 0.7 88.7 91.4 4 205
Meculuvelane Mogincual Nampula Capacity for Collective Action 22 90.9 2.9 85.2 96.6 4 22
Farol Dondo Sofala Capacity for Collective Action 107 91.7 1.3 89.1 94.2 4 79
Sengo Dondo Sofala Capacity for Collective Action 123 95.1 1.1 92.9 97.3 4 53
Maculuvelane Mogincual Nampula Sustainable Livelihoods 21 0.0 0.0 0.0 0.0 2 21
Baixo Pinda Memba Nampula Sustainable Livelihoods 382 2.5 0.6 1.4 3.6 2 380
Farol Dondo Sofala Sustainable Livelihoods 107 3.3 1.2 0.9 5.7 2 105
Sanculo Ilha de Mocambique Nampula Sustainable Livelihoods 430 4.8 0.7 3.4 6.3 2 424
Serissa Memba Nampula Sustainable Livelihoods 207 7.0 1.3 4.6 9.5 2 199
Petane1 Inhassoro Inhambane Sustainable Livelihoods 74 8.8 2.3 4.3 13.3 2 72
Sengo Dondo Sofala Sustainable Livelihoods 123 8.9 1.9 5.2 12.5 2 107
Mucocuene Inhassoro Inhambane Sustainable Livelihoods 305 9.5 1.2 7.2 11.9 2 304
Tsondzo Inhassoro Inhambane Sustainable Livelihoods 324 10.5 1.2 8.1 12.8 2 320
Quissanga Ilha de Mocambique Nampula Sustainable Livelihoods 365 11.0 1.1 8.8 13.2 2 363
Petane Inhassoro Inhambane Sustainable Livelihoods 99 11.1 2.2 6.8 15.4 2 99
Nhagondzo Inhassoro Inhambane Sustainable Livelihoods 223 12.5 1.6 9.5 15.6 2 219
Namalungo Mogincual Nampula Sustainable Livelihoods 263 13.3 1.4 10.6 16.0 2 263
Ilha Insular Ilha de Mocambique Nampula Sustainable Livelihoods 1124 15.3 0.7 13.9 16.7 2 1121
Fequete Inhassoro Inhambane Sustainable Livelihoods 500 16.7 1.1 14.5 19.0 2 492
Memba-sede Memba Nampula Sustainable Livelihoods 139 19.8 2.2 15.5 24.1 2 139
Meculuvelane Mogincual Nampula Sustainable Livelihoods 22 20.5 5.4 9.9 31.0 2 22
Vuca Inhassoro Inhambane Sustainable Livelihoods 195 22.9 2.1 18.8 27.0 2 194
Simuco Memba Nampula Sustainable Livelihoods 180 24.4 2.3 19.9 28.8 2 176
Mahelene Nacala Porto Nampula Sustainable Livelihoods 244 25.4 1.9 21.7 29.1 2 244
Quissimajulo Nacala Porto Nampula Sustainable Livelihoods 197 26.4 2.2 22.1 30.7 2 197
Pomene Massinga Inhambane Sustainable Livelihoods 277 29.5 1.9 25.8 33.3 2 270
Zavora Inharrime Inhambane Sustainable Livelihoods 451 32.3 1.6 29.2 35.3 2 410
Memba Memba Nampula Sustainable Livelihoods 420 37.6 1.7 34.3 40.9 2 408
Mabuluku Matutuíne Maputo Sustainable Livelihoods 125 42.7 3.2 36.5 49.0 2 119
Namige Sede Mogincual Nampula Sustainable Livelihoods 308 42.9 2.0 39.0 46.7 2 308
Santa Maria Matutuíne Maputo Sustainable Livelihoods 192 51.3 2.6 46.2 56.5 2 183
ggplot(site_domains,
       aes(x = fct_reorder(site_name, domain_score), y = domain_score)) +
  geom_col(width = 0.7) +
  geom_errorbar(aes(ymin = ci_low, ymax = ci_high), width = 0.18) +
  coord_flip() +
  facet_wrap(~ domain, scales = "free_y") +
  scale_y_continuous(limits = c(0, 100), breaks = seq(0, 100, 25)) +
  labs(
    title = "CCRF domain scores by site: complete Mozambique HHS",
    subtitle = "Error bars show approximate 95% CIs",
    x = "Site / community",
    y = "Mean domain score, 0–100"
  )

Gap between Collective Action and Sustainable Livelihoods by site

site_domain_wide <- site_domains %>%
  select(site_name, g1_municipality, g1_province, n_hhs, domain, domain_score) %>%
  pivot_wider(names_from = domain, values_from = domain_score) %>%
  mutate(
    domain_gap_collective_action_minus_livelihoods =
      `Capacity for Collective Action` - `Sustainable Livelihoods`
  ) %>%
  arrange(desc(domain_gap_collective_action_minus_livelihoods))

site_domain_wide %>%
  mutate(across(where(is.numeric), ~round(.x, 1))) %>%
  kable(caption = "Site-level gap between collective action and sustainable livelihoods")
Site-level gap between collective action and sustainable livelihoods
site_name g1_municipality g1_province n_hhs Capacity for Collective Action Sustainable Livelihoods domain_gap_collective_action_minus_livelihoods
Farol Dondo Sofala 107 91.7 3.3 88.4
Sengo Dondo Sofala 123 95.1 8.9 86.2
Sanculo Ilha de Mocambique Nampula 430 89.2 4.8 84.4
Maculuvelane Mogincual Nampula 21 81.0 0.0 81.0
Serissa Memba Nampula 207 87.7 7.0 80.6
Tsondzo Inhassoro Inhambane 324 88.1 10.5 77.7
Mucocuene Inhassoro Inhambane 305 85.1 9.5 75.6
Nhagondzo Inhassoro Inhambane 223 84.2 12.5 71.7
Meculuvelane Mogincual Nampula 22 90.9 20.5 70.5
Baixo Pinda Memba Nampula 382 68.6 2.5 66.1
Ilha Insular Ilha de Mocambique Nampula 1124 81.3 15.3 66.0
Petane1 Inhassoro Inhambane 74 71.7 8.8 62.9
Fequete Inhassoro Inhambane 500 78.0 16.7 61.3
Petane Inhassoro Inhambane 99 70.0 11.1 58.9
Namalungo Mogincual Nampula 263 71.6 13.3 58.3
Zavora Inharrime Inhambane 451 90.1 32.3 57.8
Quissanga Ilha de Mocambique Nampula 365 66.9 11.0 55.9
Simuco Memba Nampula 180 79.5 24.4 55.1
Mahelene Nacala Porto Nampula 244 79.4 25.4 54.0
Pomene Massinga Inhambane 277 83.0 29.5 53.5
Vuca Inhassoro Inhambane 195 76.4 22.9 53.5
Quissimajulo Nacala Porto Nampula 197 76.6 26.4 50.2
Memba-sede Memba Nampula 139 70.0 19.8 50.2
Mabuluku Matutuíne Maputo 125 88.5 42.7 45.8
Namige Sede Mogincual Nampula 308 88.3 42.9 45.5
Memba Memba Nampula 420 79.7 37.6 42.1
Santa Maria Matutuíne Maputo 192 87.3 51.3 35.9
ggplot(site_domain_wide,
       aes(x = domain_gap_collective_action_minus_livelihoods,
           y = fct_reorder(site_name, domain_gap_collective_action_minus_livelihoods))) +
  geom_col(width = 0.7) +
  labs(
    title = "Diagnostic gap by site",
    subtitle = "Positive values mean Capacity for Collective Action scores higher than Sustainable Livelihoods",
    x = "Capacity for Collective Action minus Sustainable Livelihoods",
    y = "Site / community"
  )

Whole-dataset analysis by site and year

This is the broadest site-year view. It includes all Mozambique HHS sites, not only BAF / Rare project sites. Use this mainly as a coverage and pattern-exploration plot.

Domain scores by site and year: heatmap

site_year_indicators <- hhs_if %>%
  summarise_indicators(group_vars = c("site_name", "g1_municipality", "g1_province", "year"))

site_year_domains <- site_year_indicators %>%
  make_domain_scores(group_vars = c("site_name", "g1_municipality", "g1_province", "year"))

site_year_domains %>%
  mutate(across(c(domain_score, domain_se, ci_low, ci_high), ~round(.x, 1))) %>%
  arrange(site_name, year, domain) %>%
  kable(caption = "Domain scores by site and year, complete Mozambique HHS")
Domain scores by site and year, complete Mozambique HHS
site_name g1_municipality g1_province year domain n_hhs domain_score domain_se ci_low ci_high n_indicators_available min_n_valid
Baixo Pinda Memba Nampula 2019 Capacity for Collective Action 1 50.0 NA NA NA 4 1
Baixo Pinda Memba Nampula 2019 Sustainable Livelihoods 1 0.0 NA NA NA 2 1
Baixo Pinda Memba Nampula 2021 Capacity for Collective Action 101 52.1 1.6 49.0 55.2 4 96
Baixo Pinda Memba Nampula 2021 Sustainable Livelihoods 101 0.0 0.0 0.0 0.0 2 99
Baixo Pinda Memba Nampula 2024 Capacity for Collective Action 145 96.9 0.7 95.5 98.3 4 143
Baixo Pinda Memba Nampula 2024 Sustainable Livelihoods 145 1.0 0.6 0.0 2.2 2 145
Baixo Pinda Memba Nampula 2026 Capacity for Collective Action 135 51.4 1.7 48.0 54.8 4 110
Baixo Pinda Memba Nampula 2026 Sustainable Livelihoods 135 5.9 1.4 3.2 8.7 2 135
Farol Dondo Sofala 2019 Capacity for Collective Action 107 91.7 1.3 89.1 94.2 4 79
Farol Dondo Sofala 2019 Sustainable Livelihoods 107 3.3 1.2 0.9 5.7 2 105
Fequete Inhassoro Inhambane 2019 Capacity for Collective Action 196 80.9 1.5 78.0 83.8 4 90
Fequete Inhassoro Inhambane 2019 Sustainable Livelihoods 196 19.7 2.0 15.9 23.6 2 189
Fequete Inhassoro Inhambane 2021 Capacity for Collective Action 200 78.7 1.7 75.5 82.0 4 62
Fequete Inhassoro Inhambane 2021 Sustainable Livelihoods 200 15.1 1.7 11.7 18.4 2 198
Fequete Inhassoro Inhambane 2025 Capacity for Collective Action 104 71.7 2.3 67.2 76.1 4 46
Fequete Inhassoro Inhambane 2025 Sustainable Livelihoods 104 14.4 2.4 9.7 19.2 2 104
Ilha Insular Ilha de Mocambique Nampula 2019 Capacity for Collective Action 327 75.5 1.3 72.9 78.1 4 139
Ilha Insular Ilha de Mocambique Nampula 2019 Sustainable Livelihoods 327 25.7 1.6 22.5 28.8 2 325
Ilha Insular Ilha de Mocambique Nampula 2021 Capacity for Collective Action 125 95.3 1.1 93.1 97.4 4 65
Ilha Insular Ilha de Mocambique Nampula 2021 Sustainable Livelihoods 125 8.9 1.8 5.4 12.4 2 124
Ilha Insular Ilha de Mocambique Nampula 2023 Capacity for Collective Action 106 98.8 0.5 97.8 99.9 4 55
Ilha Insular Ilha de Mocambique Nampula 2023 Sustainable Livelihoods 106 3.3 1.2 0.9 5.7 2 106
Ilha Insular Ilha de Mocambique Nampula 2025 Capacity for Collective Action 566 77.8 0.8 76.2 79.5 4 313
Ilha Insular Ilha de Mocambique Nampula 2025 Sustainable Livelihoods 566 13.0 0.9 11.2 14.8 2 566
Mabuluku Matutuíne Maputo 2019 Capacity for Collective Action 61 87.5 2.1 83.5 91.6 4 59
Mabuluku Matutuíne Maputo 2019 Sustainable Livelihoods 61 48.6 4.1 40.5 56.6 2 56
Mabuluku Matutuíne Maputo 2021 Capacity for Collective Action 64 89.4 1.9 85.7 93.1 4 56
Mabuluku Matutuíne Maputo 2021 Sustainable Livelihoods 64 36.5 4.1 28.5 44.5 2 63
Maculuvelane Mogincual Nampula 2024 Capacity for Collective Action 21 81.0 2.8 75.5 86.4 4 21
Maculuvelane Mogincual Nampula 2024 Sustainable Livelihoods 21 0.0 0.0 0.0 0.0 2 21
Mahelene Nacala Porto Nampula 2024 Capacity for Collective Action 134 91.9 1.1 89.7 94.1 4 96
Mahelene Nacala Porto Nampula 2024 Sustainable Livelihoods 134 19.0 2.4 14.3 23.7 2 134
Mahelene Nacala Porto Nampula 2026 Capacity for Collective Action 110 63.2 2.5 58.4 68.1 4 67
Mahelene Nacala Porto Nampula 2026 Sustainable Livelihoods 110 33.2 2.7 27.9 38.5 2 110
Meculuvelane Mogincual Nampula 2025 Capacity for Collective Action 22 90.9 2.9 85.2 96.6 4 22
Meculuvelane Mogincual Nampula 2025 Sustainable Livelihoods 22 20.5 5.4 9.9 31.0 2 22
Memba Memba Nampula 2019 Capacity for Collective Action 206 61.9 2.0 57.8 65.9 4 77
Memba Memba Nampula 2019 Sustainable Livelihoods 206 19.2 1.8 15.6 22.8 2 200
Memba Memba Nampula 2021 Capacity for Collective Action 214 95.1 0.8 93.5 96.7 4 93
Memba Memba Nampula 2021 Sustainable Livelihoods 214 55.1 2.4 50.4 59.9 2 207
Memba-sede Memba Nampula 2026 Capacity for Collective Action 139 70.0 1.8 66.5 73.4 4 88
Memba-sede Memba Nampula 2026 Sustainable Livelihoods 139 19.8 2.2 15.5 24.1 2 139
Mucocuene Inhassoro Inhambane 2019 Capacity for Collective Action 1 100.0 NA NA NA 3 0
Mucocuene Inhassoro Inhambane 2019 Sustainable Livelihoods 1 0.0 NA NA NA 2 1
Mucocuene Inhassoro Inhambane 2021 Capacity for Collective Action 205 89.4 1.0 87.4 91.4 4 77
Mucocuene Inhassoro Inhambane 2021 Sustainable Livelihoods 205 4.9 1.1 2.8 7.0 2 204
Mucocuene Inhassoro Inhambane 2025 Capacity for Collective Action 99 76.3 2.3 71.8 80.7 4 41
Mucocuene Inhassoro Inhambane 2025 Sustainable Livelihoods 99 19.2 2.7 13.9 24.5 2 99
Namalungo Mogincual Nampula 2024 Capacity for Collective Action 134 64.7 1.2 62.4 67.1 4 134
Namalungo Mogincual Nampula 2024 Sustainable Livelihoods 134 6.3 1.4 3.5 9.2 2 134
Namalungo Mogincual Nampula 2025 Capacity for Collective Action 129 78.3 1.8 74.9 81.8 4 105
Namalungo Mogincual Nampula 2025 Sustainable Livelihoods 129 20.5 2.2 16.3 24.8 2 129
Namige Sede Mogincual Nampula 2024 Capacity for Collective Action 151 93.5 1.2 91.1 95.8 4 68
Namige Sede Mogincual Nampula 2024 Sustainable Livelihoods 151 62.6 2.7 57.3 67.9 2 151
Namige Sede Mogincual Nampula 2025 Capacity for Collective Action 157 83.2 1.5 80.3 86.1 4 127
Namige Sede Mogincual Nampula 2025 Sustainable Livelihoods 157 23.9 2.0 19.9 27.9 2 157
Nhagondzo Inhassoro Inhambane 2021 Capacity for Collective Action 127 89.6 1.4 86.9 92.3 4 76
Nhagondzo Inhassoro Inhambane 2021 Sustainable Livelihoods 127 8.5 1.8 5.1 12.0 2 123
Nhagondzo Inhassoro Inhambane 2025 Capacity for Collective Action 96 77.5 2.1 73.4 81.6 4 76
Nhagondzo Inhassoro Inhambane 2025 Sustainable Livelihoods 96 17.7 2.7 12.4 23.1 2 96
Petane Inhassoro Inhambane 2025 Capacity for Collective Action 99 70.0 2.3 65.5 74.5 4 33
Petane Inhassoro Inhambane 2025 Sustainable Livelihoods 99 11.1 2.2 6.8 15.4 2 99
Petane1 Inhassoro Inhambane 2021 Capacity for Collective Action 74 71.7 3.1 65.7 77.7 4 21
Petane1 Inhassoro Inhambane 2021 Sustainable Livelihoods 74 8.8 2.3 4.3 13.3 2 72
Pomene Massinga Inhambane 2019 Capacity for Collective Action 140 81.8 1.7 78.5 85.1 4 90
Pomene Massinga Inhambane 2019 Sustainable Livelihoods 140 33.1 2.9 27.5 38.8 2 133
Pomene Massinga Inhambane 2021 Capacity for Collective Action 137 84.1 1.7 80.7 87.5 4 65
Pomene Massinga Inhambane 2021 Sustainable Livelihoods 137 25.9 2.4 21.2 30.6 2 137
Quissanga Ilha de Mocambique Nampula 2021 Capacity for Collective Action 91 89.3 1.7 85.9 92.7 4 57
Quissanga Ilha de Mocambique Nampula 2021 Sustainable Livelihoods 91 14.6 2.6 9.5 19.7 2 89
Quissanga Ilha de Mocambique Nampula 2023 Capacity for Collective Action 103 98.3 0.6 97.1 99.5 4 102
Quissanga Ilha de Mocambique Nampula 2023 Sustainable Livelihoods 103 24.8 2.5 19.9 29.6 2 103
Quissanga Ilha de Mocambique Nampula 2025 Capacity for Collective Action 171 37.6 1.4 34.8 40.4 4 165
Quissanga Ilha de Mocambique Nampula 2025 Sustainable Livelihoods 171 0.9 0.5 0.0 1.9 2 171
Quissimajulo Nacala Porto Nampula 2024 Capacity for Collective Action 126 83.9 1.6 80.9 87.0 4 86
Quissimajulo Nacala Porto Nampula 2024 Sustainable Livelihoods 126 19.4 2.5 14.5 24.3 2 126
Quissimajulo Nacala Porto Nampula 2026 Capacity for Collective Action 71 61.0 3.3 54.5 67.5 4 31
Quissimajulo Nacala Porto Nampula 2026 Sustainable Livelihoods 71 38.7 3.9 31.1 46.3 2 71
Sanculo Ilha de Mocambique Nampula 2021 Capacity for Collective Action 106 94.0 1.2 91.6 96.3 4 90
Sanculo Ilha de Mocambique Nampula 2021 Sustainable Livelihoods 106 10.4 2.1 6.2 14.6 2 100
Sanculo Ilha de Mocambique Nampula 2023 Capacity for Collective Action 104 90.4 1.3 87.8 93.0 4 104
Sanculo Ilha de Mocambique Nampula 2023 Sustainable Livelihoods 104 1.0 0.7 0.0 2.3 2 104
Sanculo Ilha de Mocambique Nampula 2025 Capacity for Collective Action 220 86.3 1.1 84.2 88.5 4 201
Sanculo Ilha de Mocambique Nampula 2025 Sustainable Livelihoods 220 4.1 0.9 2.3 5.9 2 220
Santa Maria Matutuíne Maputo 2019 Capacity for Collective Action 104 93.6 1.2 91.2 96.0 4 95
Santa Maria Matutuíne Maputo 2019 Sustainable Livelihoods 104 65.2 3.4 58.7 71.8 2 96
Santa Maria Matutuíne Maputo 2021 Capacity for Collective Action 88 80.1 2.1 76.0 84.2 4 72
Santa Maria Matutuíne Maputo 2021 Sustainable Livelihoods 88 36.0 3.6 28.9 43.1 2 87
Sengo Dondo Sofala 2019 Capacity for Collective Action 94 93.6 1.5 90.6 96.5 4 38
Sengo Dondo Sofala 2019 Sustainable Livelihoods 94 6.5 1.8 3.1 10.0 2 92
Sengo Dondo Sofala 2021 Capacity for Collective Action 29 100.0 0.0 100.0 100.0 4 15
Sengo Dondo Sofala 2021 Sustainable Livelihoods 29 23.3 6.7 10.3 36.4 2 15
Serissa Memba Nampula 2021 Capacity for Collective Action 207 87.7 1.3 85.1 90.3 4 68
Serissa Memba Nampula 2021 Sustainable Livelihoods 207 7.0 1.3 4.6 9.5 2 199
Simuco Memba Nampula 2021 Capacity for Collective Action 180 79.5 1.5 76.6 82.4 4 11
Simuco Memba Nampula 2021 Sustainable Livelihoods 180 24.4 2.3 19.9 28.8 2 176
Tsondzo Inhassoro Inhambane 2021 Capacity for Collective Action 221 92.0 0.9 90.3 93.8 4 109
Tsondzo Inhassoro Inhambane 2021 Sustainable Livelihoods 221 7.8 1.3 5.4 10.3 2 217
Tsondzo Inhassoro Inhambane 2025 Capacity for Collective Action 103 80.8 2.1 76.7 84.8 4 65
Tsondzo Inhassoro Inhambane 2025 Sustainable Livelihoods 103 16.0 2.5 11.1 21.0 2 103
Vuca Inhassoro Inhambane 2021 Capacity for Collective Action 96 86.1 1.6 82.9 89.3 4 32
Vuca Inhassoro Inhambane 2021 Sustainable Livelihoods 96 26.2 3.1 20.1 32.4 2 95
Vuca Inhassoro Inhambane 2025 Capacity for Collective Action 99 68.0 2.4 63.4 72.7 4 56
Vuca Inhassoro Inhambane 2025 Sustainable Livelihoods 99 19.7 2.8 14.2 25.2 2 99
Zavora Inharrime Inhambane 2019 Capacity for Collective Action 223 89.2 1.1 87.0 91.3 4 89
Zavora Inharrime Inhambane 2019 Sustainable Livelihoods 223 31.6 2.3 27.1 36.2 2 185
Zavora Inharrime Inhambane 2021 Capacity for Collective Action 228 90.8 0.9 89.0 92.6 4 116
Zavora Inharrime Inhambane 2021 Sustainable Livelihoods 228 32.8 2.2 28.5 37.1 2 219
ggplot(site_year_domains,
       aes(x = factor(year), y = fct_reorder(site_name, domain_score, .fun = mean_na), fill = domain_score)) +
  geom_tile(color = "white") +
  facet_wrap(~ domain, ncol = 2) +
  scale_fill_gradient(low = "grey95", high = "grey20", limits = c(0, 100), na.value = "white") +
  labs(
    title = "CCRF domain scores by site and year: complete Mozambique HHS",
    subtitle = "Blank/missing combinations reflect no data or insufficient valid indicator responses. See table for confidence intervals.",
    x = "Survey year",
    y = "Site / community",
    fill = "Score
0–100"
  )

Domain scores by site and year: small multiples

ggplot(site_year_domains,
       aes(x = year, y = domain_score, group = domain, linetype = domain)) +
  geom_errorbar(aes(ymin = ci_low, ymax = ci_high), width = 0.10, alpha = 0.45) +
  geom_line(linewidth = 0.6) +
  geom_point(size = 1.8) +
  facet_wrap(~ site_name, ncol = 4) +
  scale_y_continuous(limits = c(0, 100), breaks = c(0, 50, 100)) +
  scale_x_continuous(breaks = sort(unique(site_year_domains$year))) +
  labs(
    title = "CCRF domain scores by site and year: complete Mozambique HHS",
    subtitle = "Use cautiously: some site-year estimates are based on small samples; error bars show approximate 95% CIs",
    x = "Survey year",
    y = "Mean domain score, 0–100",
    linetype = "Domain"
  ) +
  theme(
    legend.position = "bottom",
    axis.text.x = element_text(angle = 45, hjust = 1),
    strip.text = element_text(face = "bold")
  )

7. Project-site CCRF analysis

This section focuses only on the BAF / Rare project sites. This is the main case-study subset.

hhs_project <- hhs_if %>%
  filter(is_project_site)

Project-site overall domain scores

project_site_indicators <- hhs_project %>%
  summarise_indicators(group_vars = c("project_site", "district", "program_maturity"))

project_site_domains <- project_site_indicators %>%
  make_domain_scores(group_vars = c("project_site", "district", "program_maturity"))

project_site_domains %>%
  mutate(across(c(domain_score, domain_se, ci_low, ci_high), ~round(.x, 1))) %>%
  arrange(district, project_site, domain) %>%
  kable(caption = "Domain scores by Wilipihera / Rare project site")
Domain scores by Wilipihera / Rare project site
project_site district program_maturity domain n_hhs domain_score domain_se ci_low ci_high n_indicators_available min_n_valid
Ilha Insular Ilha de Mocambique Former/older Rare site Capacity for Collective Action 1124 81.3 0.6 80.1 82.4 4 572
Ilha Insular Ilha de Mocambique Former/older Rare site Sustainable Livelihoods 1124 15.3 0.7 13.9 16.7 2 1121
Quissanga Ilha de Mocambique Former/older Rare site Capacity for Collective Action 365 66.9 1.2 64.6 69.2 4 324
Quissanga Ilha de Mocambique Former/older Rare site Sustainable Livelihoods 365 11.0 1.1 8.8 13.2 2 363
Sanculo Ilha de Mocambique Former/older Rare site Capacity for Collective Action 430 89.2 0.7 87.8 90.7 4 395
Sanculo Ilha de Mocambique Former/older Rare site Sustainable Livelihoods 430 4.8 0.7 3.4 6.3 2 424
Baixo Pinda Memba Former/older Rare site Capacity for Collective Action 382 68.6 1.2 66.3 70.9 4 350
Baixo Pinda Memba Former/older Rare site Sustainable Livelihoods 382 2.5 0.6 1.4 3.6 2 380
Memba Sede Memba Former/older Rare site Capacity for Collective Action 559 77.8 1.0 75.8 79.7 4 258
Memba Sede Memba Former/older Rare site Sustainable Livelihoods 559 33.1 1.4 30.4 35.8 2 547
Meculuvelane Mogincual Newer expansion site Capacity for Collective Action 22 90.9 2.9 85.2 96.6 4 22
Meculuvelane Mogincual Newer expansion site Sustainable Livelihoods 22 20.5 5.4 9.9 31.0 2 22
Namalungo Mogincual Newer expansion site Capacity for Collective Action 263 71.6 1.1 69.3 73.8 4 239
Namalungo Mogincual Newer expansion site Sustainable Livelihoods 263 13.3 1.4 10.6 16.0 2 263
Namige Sede Mogincual Newer expansion site Capacity for Collective Action 308 88.3 1.0 86.4 90.2 4 195
Namige Sede Mogincual Newer expansion site Sustainable Livelihoods 308 42.9 2.0 39.0 46.7 2 308
Mahelene Nacala Porto Newer expansion site Capacity for Collective Action 244 79.4 1.3 76.8 82.0 4 163
Mahelene Nacala Porto Newer expansion site Sustainable Livelihoods 244 25.4 1.9 21.7 29.1 2 244
Quissimajulo Nacala Porto Newer expansion site Capacity for Collective Action 197 76.6 1.5 73.6 79.6 4 117
Quissimajulo Nacala Porto Newer expansion site Sustainable Livelihoods 197 26.4 2.2 22.1 30.7 2 197
ggplot(project_site_domains,
       aes(x = fct_reorder(project_site, domain_score), y = domain_score, fill = domain)) +
  geom_col(position = position_dodge(width = 0.75), width = 0.7) +
  geom_errorbar(
    aes(ymin = ci_low, ymax = ci_high),
    position = position_dodge(width = 0.75),
    width = 0.18
  ) +
  coord_flip() +
  facet_wrap(~ district, scales = "free_y") +
  scale_y_continuous(limits = c(0, 100), breaks = seq(0, 100, 25)) +
  labs(
    title = "Project-site CCRF domain scores",
    subtitle = "Error bars show approximate 95% CIs",
    x = "Project site",
    y = "Mean domain score, 0–100",
    fill = "Domain"
  ) +
  theme(legend.position = "bottom")

Project-site indicator scores

project_site_indicators %>%
  filter(direction == "positive_core") %>%
  mutate(across(c(pct, se, ci_low, ci_high), ~round(.x, 1))) %>%
  select(project_site, district, domain, label, n_hhs, n_valid, pct, se, ci_low, ci_high) %>%
  arrange(district, project_site, domain, label) %>%
  kable(caption = "Positive indicator scores by project site")
Positive indicator scores by project site
project_site district domain label n_hhs n_valid pct se ci_low ci_high
Ilha Insular Ilha de Mocambique Capacity for Collective Action Collective Efficacy for Fisheries Management Score 1124 1124 67.2 1.4 64.4 69.9
Ilha Insular Ilha de Mocambique Capacity for Collective Action Empowerment & Participation in Management 1124 572 93.2 1.1 91.1 95.2
Ilha Insular Ilha de Mocambique Capacity for Collective Action Social Equity in Fisheries Benefits 1124 1071 83.3 1.1 81.1 85.5
Ilha Insular Ilha de Mocambique Capacity for Collective Action Trust in Local Leadership 1124 1124 81.4 1.2 79.1 83.7
Ilha Insular Ilha de Mocambique Sustainable Livelihoods Household covers needs 1124 1121 26.9 1.3 24.3 29.5
Ilha Insular Ilha de Mocambique Sustainable Livelihoods Never worried about enough food 1124 1123 3.7 0.6 2.6 4.7
Quissanga Ilha de Mocambique Capacity for Collective Action Collective Efficacy for Fisheries Management Score 365 364 57.1 2.6 52.1 62.2
Quissanga Ilha de Mocambique Capacity for Collective Action Empowerment & Participation in Management 365 324 54.9 2.8 49.5 60.4
Quissanga Ilha de Mocambique Capacity for Collective Action Social Equity in Fisheries Benefits 365 362 95.0 1.1 92.8 97.3
Quissanga Ilha de Mocambique Capacity for Collective Action Trust in Local Leadership 365 364 60.4 2.6 55.4 65.5
Quissanga Ilha de Mocambique Sustainable Livelihoods Household covers needs 365 363 19.3 2.1 15.2 23.3
Quissanga Ilha de Mocambique Sustainable Livelihoods Never worried about enough food 365 364 2.7 0.9 1.1 4.4
Sanculo Ilha de Mocambique Capacity for Collective Action Collective Efficacy for Fisheries Management Score 430 429 85.5 1.7 82.2 88.9
Sanculo Ilha de Mocambique Capacity for Collective Action Empowerment & Participation in Management 430 395 94.4 1.2 92.2 96.7
Sanculo Ilha de Mocambique Capacity for Collective Action Social Equity in Fisheries Benefits 430 428 81.1 1.9 77.4 84.8
Sanculo Ilha de Mocambique Capacity for Collective Action Trust in Local Leadership 430 429 95.8 1.0 93.9 97.7
Sanculo Ilha de Mocambique Sustainable Livelihoods Household covers needs 430 424 7.8 1.3 5.2 10.3
Sanculo Ilha de Mocambique Sustainable Livelihoods Never worried about enough food 430 428 1.9 0.7 0.6 3.2
Baixo Pinda Memba Capacity for Collective Action Collective Efficacy for Fisheries Management Score 382 381 73.0 2.3 68.5 77.4
Baixo Pinda Memba Capacity for Collective Action Empowerment & Participation in Management 382 350 74.9 2.3 70.3 79.4
Baixo Pinda Memba Capacity for Collective Action Social Equity in Fisheries Benefits 382 379 80.7 2.0 76.8 84.7
Baixo Pinda Memba Capacity for Collective Action Trust in Local Leadership 382 382 45.8 2.6 40.8 50.8
Baixo Pinda Memba Sustainable Livelihoods Household covers needs 382 380 4.5 1.1 2.4 6.6
Baixo Pinda Memba Sustainable Livelihoods Never worried about enough food 382 381 0.5 0.4 0.0 1.3
Memba Sede Memba Capacity for Collective Action Collective Efficacy for Fisheries Management Score 559 551 78.0 1.8 74.6 81.5
Memba Sede Memba Capacity for Collective Action Empowerment & Participation in Management 559 258 84.1 2.3 79.6 88.6
Memba Sede Memba Capacity for Collective Action Social Equity in Fisheries Benefits 559 529 74.9 1.9 71.2 78.6
Memba Sede Memba Capacity for Collective Action Trust in Local Leadership 559 554 74.0 1.9 70.4 77.7
Memba Sede Memba Sustainable Livelihoods Household covers needs 559 547 42.0 2.1 37.9 46.2
Memba Sede Memba Sustainable Livelihoods Never worried about enough food 559 551 24.1 1.8 20.6 27.7
Meculuvelane Mogincual Capacity for Collective Action Collective Efficacy for Fisheries Management Score 22 22 72.7 9.7 53.7 91.8
Meculuvelane Mogincual Capacity for Collective Action Empowerment & Participation in Management 22 22 95.5 4.5 86.5 100.0
Meculuvelane Mogincual Capacity for Collective Action Social Equity in Fisheries Benefits 22 22 100.0 0.0 100.0 100.0
Meculuvelane Mogincual Capacity for Collective Action Trust in Local Leadership 22 22 95.5 4.5 86.5 100.0
Meculuvelane Mogincual Sustainable Livelihoods Household covers needs 22 22 40.9 10.7 19.9 61.9
Meculuvelane Mogincual Sustainable Livelihoods Never worried about enough food 22 22 0.0 0.0 0.0 0.0
Namalungo Mogincual Capacity for Collective Action Collective Efficacy for Fisheries Management Score 263 263 31.6 2.9 25.9 37.2
Namalungo Mogincual Capacity for Collective Action Empowerment & Participation in Management 263 239 95.0 1.4 92.2 97.8
Namalungo Mogincual Capacity for Collective Action Social Equity in Fisheries Benefits 263 263 95.4 1.3 92.9 98.0
Namalungo Mogincual Capacity for Collective Action Trust in Local Leadership 263 263 64.3 3.0 58.5 70.1
Namalungo Mogincual Sustainable Livelihoods Household covers needs 263 263 26.6 2.7 21.3 32.0
Namalungo Mogincual Sustainable Livelihoods Never worried about enough food 263 263 0.0 0.0 0.0 0.0
Namige Sede Mogincual Capacity for Collective Action Collective Efficacy for Fisheries Management Score 308 308 80.8 2.2 76.4 85.2
Namige Sede Mogincual Capacity for Collective Action Empowerment & Participation in Management 308 195 91.3 2.0 87.3 95.3
Namige Sede Mogincual Capacity for Collective Action Social Equity in Fisheries Benefits 308 307 90.9 1.6 87.7 94.1
Namige Sede Mogincual Capacity for Collective Action Trust in Local Leadership 308 308 90.3 1.7 86.9 93.6
Namige Sede Mogincual Sustainable Livelihoods Household covers needs 308 308 48.4 2.9 42.8 54.0
Namige Sede Mogincual Sustainable Livelihoods Never worried about enough food 308 308 37.3 2.8 31.9 42.7
Mahelene Nacala Porto Capacity for Collective Action Collective Efficacy for Fisheries Management Score 244 244 74.6 2.8 69.1 80.1
Mahelene Nacala Porto Capacity for Collective Action Empowerment & Participation in Management 244 163 89.0 2.5 84.1 93.8
Mahelene Nacala Porto Capacity for Collective Action Social Equity in Fisheries Benefits 244 230 68.7 3.1 62.7 74.7
Mahelene Nacala Porto Capacity for Collective Action Trust in Local Leadership 244 244 85.2 2.3 80.8 89.7
Mahelene Nacala Porto Sustainable Livelihoods Household covers needs 244 244 36.9 3.1 30.8 43.0
Mahelene Nacala Porto Sustainable Livelihoods Never worried about enough food 244 244 13.9 2.2 9.6 18.3
Quissimajulo Nacala Porto Capacity for Collective Action Collective Efficacy for Fisheries Management Score 197 197 64.5 3.4 57.8 71.2
Quissimajulo Nacala Porto Capacity for Collective Action Empowerment & Participation in Management 197 117 89.7 2.8 84.2 95.3
Quissimajulo Nacala Porto Capacity for Collective Action Social Equity in Fisheries Benefits 197 186 63.4 3.5 56.5 70.4
Quissimajulo Nacala Porto Capacity for Collective Action Trust in Local Leadership 197 197 88.8 2.2 84.4 93.2
Quissimajulo Nacala Porto Sustainable Livelihoods Household covers needs 197 197 31.0 3.3 24.5 37.4
Quissimajulo Nacala Porto Sustainable Livelihoods Never worried about enough food 197 197 21.8 3.0 16.0 27.6
project_site_indicators_clean <- project_site_indicators %>%
  filter(direction == "positive_core") %>%
  mutate(
    label = stringr::str_wrap(label, width = 28),
    project_site = stringr::str_wrap(project_site, width = 16)
  )

project_site_indicators_clean <- project_site_indicators %>%
  filter(direction == "positive_core") %>%
  mutate(
    label = stringr::str_wrap(label, width = 28),
    project_site = stringr::str_wrap(project_site, width = 16)
  )

# Define site order
site_levels <- project_site_indicators_clean %>%
  distinct(project_site) %>%
  arrange(project_site) %>%
  mutate(site_id = row_number())

project_site_indicators_clean <- project_site_indicators_clean %>%
  left_join(site_levels, by = "project_site")

# Alternating background bands
site_bands <- site_levels %>%
  mutate(
    xmin = site_id - 0.5,
    xmax = site_id + 0.5,
    shade = site_id %% 2 == 0
  ) %>%
  filter(shade)

ggplot(
  project_site_indicators_clean,
  aes(
    x = site_id,
    y = pct,
    color = label,
    group = label
  )
) +
  geom_rect(
    data = site_bands,
    aes(
      xmin = xmin,
      xmax = xmax,
      ymin = -Inf,
      ymax = Inf
    ),
    inherit.aes = FALSE,
    fill = "grey95",
    color = NA
  ) +
  geom_errorbar(
    aes(ymin = ci_low, ymax = ci_high),
    position = position_dodge(width = 0.55),
    width = 0.25,
    linewidth = 0.5,
    alpha = 0.6
  ) +
  geom_point(
    position = position_dodge(width = 0.55),
    size = 2.4
  ) +
  coord_flip() +
  facet_wrap(
    ~ domain,
    ncol = 1
  ) +
  scale_x_continuous(
    breaks = site_levels$site_id,
    labels = site_levels$project_site,
    expand = expansion(add = 0.3)
  ) +
  scale_y_continuous(
    limits = c(0, 100),
    breaks = seq(0, 100, 25),
    labels = scales::label_percent(scale = 1)
  ) +
  scale_color_brewer(palette = "Dark2") +
  labs(
    title = "Positive indicator scores by project site",
    subtitle = "Points show mean indicator scores; error bars show approximate 95% CIs",
    x = NULL,
    y = "% positive",
    color = "Indicator"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    legend.position = "bottom",
    legend.title = element_text(size = 10),
    legend.text = element_text(size = 9),
    strip.text = element_text(face = "bold"),
    panel.grid.minor = element_blank(),
    panel.grid.major.y = element_blank()
  )

Project-site domain scores by year

project_site_year_indicators <- hhs_project %>%
  summarise_indicators(group_vars = c("project_site", "district", "program_maturity", "year"))

project_site_year_domains <- project_site_year_indicators %>%
  make_domain_scores(group_vars = c("project_site", "district", "program_maturity", "year"))

project_site_year_domains %>%
  mutate(across(c(domain_score, domain_se, ci_low, ci_high), ~round(.x, 1))) %>%
  arrange(district, project_site, year, domain) %>%
  kable(caption = "Project-site domain scores by year")
Project-site domain scores by year
project_site district program_maturity year domain n_hhs domain_score domain_se ci_low ci_high n_indicators_available min_n_valid
Ilha Insular Ilha de Mocambique Former/older Rare site 2019 Capacity for Collective Action 327 75.5 1.3 72.9 78.1 4 139
Ilha Insular Ilha de Mocambique Former/older Rare site 2019 Sustainable Livelihoods 327 25.7 1.6 22.5 28.8 2 325
Ilha Insular Ilha de Mocambique Former/older Rare site 2021 Capacity for Collective Action 125 95.3 1.1 93.1 97.4 4 65
Ilha Insular Ilha de Mocambique Former/older Rare site 2021 Sustainable Livelihoods 125 8.9 1.8 5.4 12.4 2 124
Ilha Insular Ilha de Mocambique Former/older Rare site 2023 Capacity for Collective Action 106 98.8 0.5 97.8 99.9 4 55
Ilha Insular Ilha de Mocambique Former/older Rare site 2023 Sustainable Livelihoods 106 3.3 1.2 0.9 5.7 2 106
Ilha Insular Ilha de Mocambique Former/older Rare site 2025 Capacity for Collective Action 566 77.8 0.8 76.2 79.5 4 313
Ilha Insular Ilha de Mocambique Former/older Rare site 2025 Sustainable Livelihoods 566 13.0 0.9 11.2 14.8 2 566
Quissanga Ilha de Mocambique Former/older Rare site 2021 Capacity for Collective Action 91 89.3 1.7 85.9 92.7 4 57
Quissanga Ilha de Mocambique Former/older Rare site 2021 Sustainable Livelihoods 91 14.6 2.6 9.5 19.7 2 89
Quissanga Ilha de Mocambique Former/older Rare site 2023 Capacity for Collective Action 103 98.3 0.6 97.1 99.5 4 102
Quissanga Ilha de Mocambique Former/older Rare site 2023 Sustainable Livelihoods 103 24.8 2.5 19.9 29.6 2 103
Quissanga Ilha de Mocambique Former/older Rare site 2025 Capacity for Collective Action 171 37.6 1.4 34.8 40.4 4 165
Quissanga Ilha de Mocambique Former/older Rare site 2025 Sustainable Livelihoods 171 0.9 0.5 0.0 1.9 2 171
Sanculo Ilha de Mocambique Former/older Rare site 2021 Capacity for Collective Action 106 94.0 1.2 91.6 96.3 4 90
Sanculo Ilha de Mocambique Former/older Rare site 2021 Sustainable Livelihoods 106 10.4 2.1 6.2 14.6 2 100
Sanculo Ilha de Mocambique Former/older Rare site 2023 Capacity for Collective Action 104 90.4 1.3 87.8 93.0 4 104
Sanculo Ilha de Mocambique Former/older Rare site 2023 Sustainable Livelihoods 104 1.0 0.7 0.0 2.3 2 104
Sanculo Ilha de Mocambique Former/older Rare site 2025 Capacity for Collective Action 220 86.3 1.1 84.2 88.5 4 201
Sanculo Ilha de Mocambique Former/older Rare site 2025 Sustainable Livelihoods 220 4.1 0.9 2.3 5.9 2 220
Baixo Pinda Memba Former/older Rare site 2019 Capacity for Collective Action 1 50.0 NA NA NA 4 1
Baixo Pinda Memba Former/older Rare site 2019 Sustainable Livelihoods 1 0.0 NA NA NA 2 1
Baixo Pinda Memba Former/older Rare site 2021 Capacity for Collective Action 101 52.1 1.6 49.0 55.2 4 96
Baixo Pinda Memba Former/older Rare site 2021 Sustainable Livelihoods 101 0.0 0.0 0.0 0.0 2 99
Baixo Pinda Memba Former/older Rare site 2024 Capacity for Collective Action 145 96.9 0.7 95.5 98.3 4 143
Baixo Pinda Memba Former/older Rare site 2024 Sustainable Livelihoods 145 1.0 0.6 0.0 2.2 2 145
Baixo Pinda Memba Former/older Rare site 2026 Capacity for Collective Action 135 51.4 1.7 48.0 54.8 4 110
Baixo Pinda Memba Former/older Rare site 2026 Sustainable Livelihoods 135 5.9 1.4 3.2 8.7 2 135
Memba Sede Memba Former/older Rare site 2019 Capacity for Collective Action 206 61.9 2.0 57.8 65.9 4 77
Memba Sede Memba Former/older Rare site 2019 Sustainable Livelihoods 206 19.2 1.8 15.6 22.8 2 200
Memba Sede Memba Former/older Rare site 2021 Capacity for Collective Action 214 95.1 0.8 93.5 96.7 4 93
Memba Sede Memba Former/older Rare site 2021 Sustainable Livelihoods 214 55.1 2.4 50.4 59.9 2 207
Memba Sede Memba Former/older Rare site 2026 Capacity for Collective Action 139 70.0 1.8 66.5 73.4 4 88
Memba Sede Memba Former/older Rare site 2026 Sustainable Livelihoods 139 19.8 2.2 15.5 24.1 2 139
Meculuvelane Mogincual Newer expansion site 2025 Capacity for Collective Action 22 90.9 2.9 85.2 96.6 4 22
Meculuvelane Mogincual Newer expansion site 2025 Sustainable Livelihoods 22 20.5 5.4 9.9 31.0 2 22
Namalungo Mogincual Newer expansion site 2024 Capacity for Collective Action 134 64.7 1.2 62.4 67.1 4 134
Namalungo Mogincual Newer expansion site 2024 Sustainable Livelihoods 134 6.3 1.4 3.5 9.2 2 134
Namalungo Mogincual Newer expansion site 2025 Capacity for Collective Action 129 78.3 1.8 74.9 81.8 4 105
Namalungo Mogincual Newer expansion site 2025 Sustainable Livelihoods 129 20.5 2.2 16.3 24.8 2 129
Namige Sede Mogincual Newer expansion site 2024 Capacity for Collective Action 151 93.5 1.2 91.1 95.8 4 68
Namige Sede Mogincual Newer expansion site 2024 Sustainable Livelihoods 151 62.6 2.7 57.3 67.9 2 151
Namige Sede Mogincual Newer expansion site 2025 Capacity for Collective Action 157 83.2 1.5 80.3 86.1 4 127
Namige Sede Mogincual Newer expansion site 2025 Sustainable Livelihoods 157 23.9 2.0 19.9 27.9 2 157
Mahelene Nacala Porto Newer expansion site 2024 Capacity for Collective Action 134 91.9 1.1 89.7 94.1 4 96
Mahelene Nacala Porto Newer expansion site 2024 Sustainable Livelihoods 134 19.0 2.4 14.3 23.7 2 134
Mahelene Nacala Porto Newer expansion site 2026 Capacity for Collective Action 110 63.2 2.5 58.4 68.1 4 67
Mahelene Nacala Porto Newer expansion site 2026 Sustainable Livelihoods 110 33.2 2.7 27.9 38.5 2 110
Quissimajulo Nacala Porto Newer expansion site 2024 Capacity for Collective Action 126 83.9 1.6 80.9 87.0 4 86
Quissimajulo Nacala Porto Newer expansion site 2024 Sustainable Livelihoods 126 19.4 2.5 14.5 24.3 2 126
Quissimajulo Nacala Porto Newer expansion site 2026 Capacity for Collective Action 71 61.0 3.3 54.5 67.5 4 31
Quissimajulo Nacala Porto Newer expansion site 2026 Sustainable Livelihoods 71 38.7 3.9 31.1 46.3 2 71
ggplot(project_site_year_domains,
       aes(x = year, y = domain_score, group = domain, linetype = domain)) +
  geom_errorbar(aes(ymin = ci_low, ymax = ci_high), width = 0.10, alpha = 0.55) +
  geom_line(linewidth = 0.8) +
  geom_point(size = 2.1) +
  facet_wrap(~ project_site, ncol = 2) +
  scale_y_continuous(limits = c(0, 100), breaks = seq(0, 100, 25)) +
  scale_x_continuous(breaks = sort(unique(project_site_year_domains$year))) +
  labs(
    title = "Project-site domain scores by year",
    subtitle = "Use cautiously: sample sizes and implementation context vary across site-years; error bars show approximate 95% CIs",
    x = "Survey year",
    y = "Mean domain score, 0–100",
    linetype = "Domain"
  ) +
  theme(
    legend.position = "bottom",
    strip.text = element_text(face = "bold"),
    axis.text.x = element_text(angle = 45, hjust = 1)
  )

Project-site domain scores by year: heatmap version

ggplot(project_site_year_domains,
       aes(x = factor(year), y = fct_reorder(project_site, domain_score, .fun = mean_na), fill = domain_score)) +
  geom_tile(color = "white") +
  geom_text(aes(label = if_else(is.na(domain_score), "", round(domain_score, 0) %>% as.character())), size = 3) +
  facet_wrap(~ domain, ncol = 2) +
  scale_fill_gradient(low = "grey95", high = "grey20", limits = c(0, 100), na.value = "white") +
  labs(
    title = "Project-site domain scores by year",
    subtitle = "Numbers are mean scores on a 0–100 scale",
    x = "Survey year",
    y = "Project site",
    fill = "Score\n0–100"
  )

8. Sustainable Livelihoods (negative)

These plots show the negative livelihood conditions directly.

project_livelihood_diagnostics <- project_site_indicators %>%
  filter(indicator %in% c("financial_strain", "food_worry_any", "food_worry_often"))

project_livelihood_diagnostics %>%
  mutate(across(c(pct, se, ci_low, ci_high), ~round(.x, 1))) %>%
  select(project_site, district, label, n_hhs, n_valid, pct, se, ci_low, ci_high) %>%
  arrange(district, project_site, label) %>%
  kable(caption = "Livelihood diagnostic indicators by project site")
Livelihood diagnostic indicators by project site
project_site district label n_hhs n_valid pct se ci_low ci_high
Ilha Insular Ilha de Mocambique Household covers needs with difficulty 1124 1121 73.1 1.3 70.5 75.7
Ilha Insular Ilha de Mocambique Often worried about food 1124 1123 36.1 1.4 33.3 38.9
Ilha Insular Ilha de Mocambique Sometimes/often worried about food 1124 1123 96.3 0.6 95.3 97.4
Quissanga Ilha de Mocambique Household covers needs with difficulty 365 363 80.7 2.1 76.7 84.8
Quissanga Ilha de Mocambique Often worried about food 365 364 46.4 2.6 41.3 51.6
Quissanga Ilha de Mocambique Sometimes/often worried about food 365 364 97.3 0.9 95.6 98.9
Sanculo Ilha de Mocambique Household covers needs with difficulty 430 424 92.2 1.3 89.7 94.8
Sanculo Ilha de Mocambique Often worried about food 430 428 40.7 2.4 36.0 45.3
Sanculo Ilha de Mocambique Sometimes/often worried about food 430 428 98.1 0.7 96.8 99.4
Baixo Pinda Memba Household covers needs with difficulty 382 380 95.5 1.1 93.4 97.6
Baixo Pinda Memba Often worried about food 382 381 24.9 2.2 20.6 29.3
Baixo Pinda Memba Sometimes/often worried about food 382 381 99.5 0.4 98.7 100.0
Memba Sede Memba Household covers needs with difficulty 559 547 58.0 2.1 53.8 62.1
Memba Sede Memba Often worried about food 559 551 4.9 0.9 3.1 6.7
Memba Sede Memba Sometimes/often worried about food 559 551 75.9 1.8 72.3 79.4
Meculuvelane Mogincual Household covers needs with difficulty 22 22 59.1 10.7 38.1 80.1
Meculuvelane Mogincual Often worried about food 22 22 4.5 4.5 0.0 13.5
Meculuvelane Mogincual Sometimes/often worried about food 22 22 100.0 0.0 100.0 100.0
Namalungo Mogincual Household covers needs with difficulty 263 263 73.4 2.7 68.0 78.7
Namalungo Mogincual Often worried about food 263 263 14.8 2.2 10.5 19.1
Namalungo Mogincual Sometimes/often worried about food 263 263 100.0 0.0 100.0 100.0
Namige Sede Mogincual Household covers needs with difficulty 308 308 51.6 2.9 46.0 57.2
Namige Sede Mogincual Often worried about food 308 308 6.5 1.4 3.7 9.2
Namige Sede Mogincual Sometimes/often worried about food 308 308 62.7 2.8 57.3 68.1
Mahelene Nacala Porto Household covers needs with difficulty 244 244 63.1 3.1 57.0 69.2
Mahelene Nacala Porto Often worried about food 244 244 34.4 3.0 28.5 40.4
Mahelene Nacala Porto Sometimes/often worried about food 244 244 86.1 2.2 81.7 90.4
Quissimajulo Nacala Porto Household covers needs with difficulty 197 197 69.0 3.3 62.6 75.5
Quissimajulo Nacala Porto Often worried about food 197 197 36.0 3.4 29.3 42.8
Quissimajulo Nacala Porto Sometimes/often worried about food 197 197 78.2 3.0 72.4 84.0
ggplot(project_livelihood_diagnostics,
       aes(x = fct_reorder(project_site, pct), y = pct, fill = label)) +
  geom_col(position = position_dodge(width = 0.75), width = 0.7) +
  geom_errorbar(
    aes(ymin = ci_low, ymax = ci_high),
    position = position_dodge(width = 0.75),
    width = 0.18
  ) +
  coord_flip() +
  facet_wrap(~ district, scales = "free_y") +
  scale_y_continuous(limits = c(0, 100), labels = label_percent(scale = 1)) +
  labs(
    title = "Sustainable Livelihoods diagnostic indicators by project site",
    subtitle = "Higher values here indicate more livelihood stress; error bars show approximate 95% CIs",
    x = "Project site",
    y = "% of valid responses",
    fill = "Diagnostic indicator"
  ) +
  theme(legend.position = "bottom")

9. Income context

Income is useful context but still requires more cleaning and checks for outliers, currency consistency, recall period, and inflation. Prefer medians and IQRs.

income_summary <- hhs_if %>%
  summarise(
    n_valid_income = sum(!is.na(g13_hh_average_income)),
    min = min(g13_hh_average_income, na.rm = TRUE),
    p25 = p25_na(g13_hh_average_income),
    median = median_na(g13_hh_average_income),
    mean = mean_na(g13_hh_average_income),
    p75 = p75_na(g13_hh_average_income),
    p95 = as.numeric(quantile(g13_hh_average_income, 0.95, na.rm = TRUE)),
    max = max(g13_hh_average_income, na.rm = TRUE)
  )

income_summary %>%
  mutate(across(where(is.numeric), ~round(.x, 1))) %>%
  kable(caption = "Raw average monthly household income summary")
Raw average monthly household income summary
n_valid_income min p25 median mean p75 p95 max
6877 0 2000 5000 9420.7 9800 25000 9e+05
# Clean income variable for plotting only --------------------------------

income_plot_data <- hhs_if %>%
  mutate(
    income_raw = g13_hh_average_income,
    income_mzn = readr::parse_number(as.character(g13_hh_average_income))
  ) %>%
  filter(
    !is.na(income_mzn),
    income_mzn >= 0
  )

# Use the 99th percentile as a conservative plotting cutoff.
# This removes extreme values that dominate the plot but keeps most observations.
income_cutoff_99 <- quantile(
  income_plot_data$income_mzn,
  probs = 0.99,
  na.rm = TRUE
)

income_plot_data_clean <- income_plot_data %>%
  filter(income_mzn <= income_cutoff_99)

income_outlier_summary <- tibble::tibble(
  records_with_income = nrow(income_plot_data),
  records_kept_for_plots = nrow(income_plot_data_clean),
  records_removed_from_income_plots = nrow(income_plot_data) - nrow(income_plot_data_clean),
  income_cutoff_99 = income_cutoff_99
)

income_outlier_summary %>%
  knitr::kable(
    caption = "Income values excluded from income plots using the 99th percentile cutoff"
  )
Income values excluded from income plots using the 99th percentile cutoff
records_with_income records_kept_for_plots records_removed_from_income_plots income_cutoff_99
6877 6811 66 75000
ggplot(
  income_plot_data_clean,
  aes(x = income_mzn)
) +
  geom_histogram(
    bins = 40,
    fill = "grey40",
    color = "white"
  ) +
  scale_x_continuous(
    labels = scales::comma
  ) +
  labs(
    title = "Reported average monthly household income",
    subtitle = paste0(
      "Values above the 99th percentile excluded for readability; cutoff = ",
      scales::comma(round(income_cutoff_99, 0))
    ),
    x = "Average monthly household income",
    y = "HHS records"
  ) +
  theme_minimal(base_size = 12)

project_income <- hhs_project %>%
  group_by(project_site, district) %>%
  summarise(
    n_valid_income = sum(!is.na(g13_hh_average_income)),
    median_income = median_na(g13_hh_average_income),
    income_p25 = p25_na(g13_hh_average_income),
    income_p75 = p75_na(g13_hh_average_income),
    .groups = "drop"
  )

project_income %>%
  mutate(across(where(is.numeric), ~round(.x, 1))) %>%
  kable(caption = "Income context by project site")
Income context by project site
project_site district n_valid_income median_income income_p25 income_p75
Baixo Pinda Memba 380 1500 800 4850
Ilha Insular Ilha de Mocambique 1117 8000 5000 12000
Mahelene Nacala Porto 244 7000 3000 13000
Meculuvelane Mogincual 22 2820 2040 3550
Memba Sede Memba 482 500 200 2000
Namalungo Mogincual 263 6000 3800 7000
Namige Sede Mogincual 308 10000 4475 36250
Quissanga Ilha de Mocambique 359 2500 1900 8825
Quissimajulo Nacala Porto 197 5000 2500 8007
Sanculo Ilha de Mocambique 418 6000 3800 14475
income_plot_data_clean %>%
  filter(!is.na(project_site)) %>%
  ggplot(
    aes(
      x = reorder(project_site, income_mzn, median, na.rm = TRUE),
      y = income_mzn
    )
  ) +
  geom_boxplot(outlier.alpha = 0.25) +
  coord_flip() +
  scale_y_continuous(
    labels = scales::comma
  ) +
  labs(
    title = "Reported average monthly household income by project site",
    subtitle = paste0(
      "Values above the 99th percentile excluded for readability; cutoff = ",
      scales::comma(round(income_cutoff_99, 0))
    ),
    x = NULL,
    y = "Average monthly household income"
  ) +
  theme_minimal(base_size = 12)

income_plot_data_clean %>%
  filter(!is.na(year)) %>%
  ggplot(
    aes(
      x = factor(year),
      y = income_mzn
    )
  ) +
  geom_boxplot(outlier.alpha = 0.25) +
  scale_y_continuous(
    labels = scales::comma
  ) +
  labs(
    title = "Reported average monthly household income by year",
    subtitle = paste0(
      "Values above the 99th percentile excluded for readability; cutoff = ",
      scales::comma(round(income_cutoff_99, 0))
    ),
    x = "Survey year",
    y = "Average monthly household income"
  ) +
  theme_minimal(base_size = 12)

project_site_income_year <- income_plot_data_clean %>%
  filter(
    !is.na(project_site),
    !is.na(year),
    !is.na(income_mzn)
  ) %>%
  group_by(project_site, year) %>%
  summarise(
    n = n(),
    median_income = median(income_mzn, na.rm = TRUE),
    mean_income = mean(income_mzn, na.rm = TRUE),
    q25 = quantile(income_mzn, 0.25, na.rm = TRUE),
    q75 = quantile(income_mzn, 0.75, na.rm = TRUE),
    .groups = "drop"
  )

project_site_income_year %>%
  mutate(
    median_income = round(median_income, 0),
    mean_income = round(mean_income, 0),
    q25 = round(q25, 0),
    q75 = round(q75, 0)
  ) %>%
  arrange(project_site, year) %>%
  knitr::kable(
    caption = "Reported average monthly household income by project site and year"
  )
Reported average monthly household income by project site and year
project_site year n median_income mean_income q25 q75
Baixo Pinda 2019 1 5000 5000 5000 5000
Baixo Pinda 2021 99 1000 994 700 1300
Baixo Pinda 2024 145 5000 4163 3000 6000
Baixo Pinda 2026 135 1500 1972 650 2450
Ilha Insular 2019 325 5500 7105 3500 9000
Ilha Insular 2021 120 5000 4966 3000 6000
Ilha Insular 2023 104 9900 17176 8225 24300
Ilha Insular 2025 557 9500 11506 6500 13000
Mahelene 2024 134 4500 6451 1500 8450
Mahelene 2026 110 10125 13330 6000 18000
Meculuvelane 2025 22 2820 2967 2040 3550
Memba Sede 2019 153 1200 2210 200 3500
Memba Sede 2021 190 400 566 200 600
Memba Sede 2026 139 1200 2322 400 2700
Namalungo 2024 133 6000 8130 6000 8000
Namalungo 2025 129 3900 5375 2000 6000
Namige Sede 2024 109 25000 29954 15000 40000
Namige Sede 2025 157 4600 5471 3000 6300
Quissanga 2021 85 3000 3331 1000 5000
Quissanga 2023 102 14000 13578 8562 18000
Quissanga 2025 171 2000 3206 1500 2500
Quissimajulo 2024 126 4000 4739 2000 6950
Quissimajulo 2026 71 8000 11642 5250 12500
Sanculo 2021 94 3500 3471 1850 5000
Sanculo 2023 102 16305 18022 10000 21450
Sanculo 2025 213 5200 9589 4000 10000
income_plot_data_clean %>%
  filter(
    !is.na(project_site),
    !is.na(year),
    !is.na(income_mzn)
  ) %>%
  mutate(
    project_site = stringr::str_wrap(project_site, width = 18)
  ) %>%
  ggplot(
    aes(
      x = factor(year),
      y = income_mzn
    )
  ) +
  geom_boxplot(
    outlier.alpha = 0.15,
    width = 0.65
  ) +
  facet_wrap(~ project_site, ncol = 3, scales = "free_y") +
  scale_y_continuous(labels = scales::comma) +
  labs(
    title = "Reported average monthly household income by project site and year",
    subtitle = paste0(
      "Values above the 99th percentile excluded for readability; cutoff = ",
      scales::comma(round(income_cutoff_99, 0))
    ),
    x = "Survey year",
    y = "Average monthly household income"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    strip.text = element_text(face = "bold"),
    panel.grid.minor = element_blank()
  )

bootstrap_median_ci <- function(x, n_boot = 1000, conf = 0.95) {
  
  x <- x[!is.na(x)]
  
  if (length(x) < 5) {
    return(
      tibble::tibble(
        median_income = median(x, na.rm = TRUE),
        ci_low = NA_real_,
        ci_high = NA_real_
      )
    )
  }
  
  boot_medians <- replicate(
    n_boot,
    median(sample(x, size = length(x), replace = TRUE), na.rm = TRUE)
  )
  
  alpha <- 1 - conf
  
  tibble::tibble(
    median_income = median(x, na.rm = TRUE),
    ci_low = quantile(boot_medians, probs = alpha / 2, na.rm = TRUE),
    ci_high = quantile(boot_medians, probs = 1 - alpha / 2, na.rm = TRUE)
  )
}

# Project-site income summary by year ------------------------------------

project_site_income_year <- income_plot_data_clean %>%
  filter(
    !is.na(project_site),
    !is.na(year),
    !is.na(income_mzn)
  ) %>%
  group_by(project_site, year) %>%
  summarise(
    n = n(),
    mean_income = mean(income_mzn, na.rm = TRUE),
    q25 = quantile(income_mzn, 0.25, na.rm = TRUE),
    q75 = quantile(income_mzn, 0.75, na.rm = TRUE),
    median_ci = list(bootstrap_median_ci(income_mzn)),
    .groups = "drop"
  ) %>%
  tidyr::unnest(median_ci)


ggplot(
  project_site_income_year,
  aes(
    x = year,
    y = median_income,
    group = project_site,
    color = project_site
  )
) +
  geom_errorbar(
    aes(
      ymin = ci_low,
      ymax = ci_high
    ),
    width = 0.12,
    alpha = 0.45,
    linewidth = 0.5
  ) +
  geom_line(linewidth = 0.8) +
  geom_point(aes(size = n), alpha = 0.85) +
  scale_y_continuous(labels = scales::comma) +
  scale_x_continuous(
    breaks = sort(unique(project_site_income_year$year))
  ) +
  scale_color_brewer(palette = "Paired") +
  labs(
    title = "Median reported monthly household income by project site and year",
    subtitle = "Error bars show bootstrap 95% CIs for the median; point size reflects number of HHS records with non-missing income",
    x = "Survey year",
    y = "Median monthly household income",
    color = "Project site",
    size = "N"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    legend.position = "bottom",
    panel.grid.minor = element_blank()
  )

income_heatmap_data <- project_site_income_year %>%
  mutate(
    project_site = forcats::fct_reorder(
      project_site,
      median_income,
      .fun = median,
      na.rm = TRUE
    ),
    label = paste0(
      scales::comma(round(median_income, 0)),
      "\n",
      "n=",
      n
    ),
    income_scaled = scales::rescale(
      median_income,
      to = c(0, 1),
      from = range(median_income, na.rm = TRUE)
    ),
    label_color = if_else(income_scaled < 0.45, "white", "black")
  )

ggplot(
  income_heatmap_data,
  aes(
    x = factor(year),
    y = project_site,
    fill = median_income
  )
) +
  geom_tile(color = "white") +
  geom_text(
    aes(
      label = label,
      color = label_color
    ),
    size = 3
  ) +
  scale_color_identity() +
  scale_fill_viridis_c(
    labels = scales::comma,
    option = "C"
  ) +
  labs(
    title = "Median reported monthly household income by project site and year",
    subtitle = "Cell labels show median income and number of HHS records",
    x = "Survey year",
    y = NULL,
    fill = "Median income"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    panel.grid = element_blank()
  )

10. Suggested interpretation for a CCRF case study

The Mozambique HHS provides a useful demonstration of the CCRF as a diagnostic framework. The likely core finding is an imbalance between the two measurable components:

  • Capacity for Collective Action tends to score higher. This suggests that many respondents express positive views on social equity, trust in local decision-making, community management ability, and the value of participation in management.
  • Sustainable Livelihoods tends to score lower. Many households report difficulty covering basic needs and concern about having enough food.
  • The CCRF therefore helps identify Sustainable Livelihoods as a priority resilience weakness, even in places where collective-action foundations appear stronger.

Suggested manuscript language:

In the Mozambique case study, HHS results showed a marked imbalance across CCRF domains. Indicators linked to collective action and co-management were comparatively stronger, suggesting a foundation for local fisheries stewardship. In contrast, Sustainable Livelihoods indicators were consistently weaker: many households reported difficulty covering basic needs and concern about food availability. The CCRF therefore identified livelihoods resilience as a priority management gap, helping justify targeted responses such as fisheries-based microenterprises, Farmer Field Schools, TVET, poultry, aquaculture, apiculture, market linkages, savings groups, and nutrition/WASH interventions.