{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# JMP service ladders for water, sanitation and hygiene\n",
    "\n",
    "*WASH household survey, 2024 · R*\n",
    "\n",
    "Cassion · data-analysis.cassion.dev\n",
    "\n",
    "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/thecassion/cassion-learning-platform/blob/main/apps/data-analysis/public/datasets/examples/wash-household-survey-2024/jmp-ladders.r.en.ipynb)"
   ],
   "id": "cell-000"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## What this produces\n",
    "\n",
    "The same three ladder classifications as the Python example, built with dplyr\n",
    "`case_when`. Python and R are peers here; you will inherit whichever your\n",
    "predecessor used.\n",
    "\n",
    "Every dataset on this platform is synthetic. No real household is described.\n",
    "\n",
    "## Setup"
   ],
   "id": "cell-001"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| message: false\n",
    "library(readr)\n",
    "library(dplyr)\n",
    "library(tidyr)\n",
    "\n",
    "URL <- paste0(\n",
    "  \"https://data-analysis.cassion.dev/datasets/files/\",\n",
    "  \"wash-household-survey-2024.v1.csv\"\n",
    ")\n",
    "\n",
    "wash <- read_csv(URL, col_types = cols(\n",
    "  household_id = col_character(),\n",
    "  district     = col_character(),\n",
    "  community    = col_character(),\n",
    "  .default     = col_guess()\n",
    "))\n",
    "\n",
    "glimpse(wash)"
   ],
   "id": "cell-002"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Normalise the district before grouping by it\n",
    "\n",
    "One team wrote Nord-Ouest four different ways. Six districts instead of three,\n",
    "and the worst-performing one split into pieces too small to notice."
   ],
   "id": "cell-003"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "count(wash, district)"
   ],
   "id": "cell-004"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "wash <- wash |>\n",
    "  mutate(district = gsub(\" \", \"-\", tolower(trimws(district)), fixed = TRUE))\n",
    "\n",
    "count(wash, district)"
   ],
   "id": "cell-005"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The three ladders\n",
    "\n",
    "`case_when` evaluates top to bottom and stops at the first match, which is\n",
    "exactly the shape of a ladder definition — put the overriding conditions first."
   ],
   "id": "cell-006"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "IMPROVED <- c(\n",
    "  \"piped-into-dwelling\", \"piped-into-yard\", \"public-tap\", \"borehole\",\n",
    "  \"protected-well\", \"protected-spring\", \"tanker-truck\"\n",
    ")\n",
    "IMPROVED_SANITATION <- c(\n",
    "  \"flush-to-sewer\", \"flush-to-septic\", \"vip-latrine\", \"pit-latrine-with-slab\"\n",
    ")\n",
    "\n",
    "wash <- wash |>\n",
    "  mutate(\n",
    "    water_service = case_when(\n",
    "      water_source == \"surface-water\"                      ~ \"surface water\",\n",
    "      !water_source %in% IMPROVED                          ~ \"unimproved\",\n",
    "      is.na(round_trip_minutes)                            ~ \"improved, time unknown\",\n",
    "      round_trip_minutes <= 30                             ~ \"basic\",\n",
    "      TRUE                                                 ~ \"limited\"\n",
    "    ),\n",
    "    sanitation_service = case_when(\n",
    "      sanitation_facility == \"open-defecation\"             ~ \"open defecation\",\n",
    "      !sanitation_facility %in% IMPROVED_SANITATION        ~ \"unimproved\",\n",
    "      shared_sanitation                                    ~ \"limited\",\n",
    "      TRUE                                                 ~ \"basic\"\n",
    "    ),\n",
    "    hygiene_service = case_when(\n",
    "      handwashing_facility == \"no-facility\"                ~ \"no facility\",\n",
    "      soap_observed                                        ~ \"basic\",\n",
    "      TRUE                                                 ~ \"limited\"\n",
    "    )\n",
    "  )\n",
    "\n",
    "wash |> count(water_service) |> mutate(pct = round(100 * n / sum(n), 1))"
   ],
   "id": "cell-007"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note the ordering in the water ladder. An improved source with an unrecorded\n",
    "collection time cannot be called basic, so that clause sits above the time\n",
    "comparison — otherwise `NA <= 30` returns `NA`, falls through to `TRUE`, and the\n",
    "household is silently reported as limited service on no evidence at all."
   ],
   "id": "cell-008"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "wash |> count(sanitation_service) |> mutate(pct = round(100 * n / sum(n), 1))\n",
    "wash |> count(hygiene_service) |> mutate(pct = round(100 * n / sum(n), 1))"
   ],
   "id": "cell-009"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Coverage by district"
   ],
   "id": "cell-010"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "coverage <- wash |>\n",
    "  group_by(district) |>\n",
    "  summarise(\n",
    "    households        = n(),\n",
    "    basic_water       = round(100 * mean(water_service == \"basic\"), 1),\n",
    "    basic_sanitation  = round(100 * mean(sanitation_service == \"basic\"), 1),\n",
    "    basic_hygiene     = round(100 * mean(hygiene_service == \"basic\"), 1),\n",
    "    open_defecation   = round(100 * mean(sanitation_service == \"open defecation\"), 1),\n",
    "    .groups = \"drop\"\n",
    "  ) |>\n",
    "  arrange(basic_water)\n",
    "\n",
    "coverage"
   ],
   "id": "cell-011"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Quantity is a separate indicator"
   ],
   "id": "cell-012"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "SPHERE_MINIMUM <- 15\n",
    "\n",
    "wash |>\n",
    "  summarise(\n",
    "    below_sphere = round(100 * mean(litres_per_person_day < SPHERE_MINIMUM, na.rm = TRUE), 1),\n",
    "    over_30_min  = round(100 * mean(round_trip_minutes > 30, na.rm = TRUE), 1)\n",
    "  )"
   ],
   "id": "cell-013"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "wash |>\n",
    "  count(water_service, below_sphere = litres_per_person_day < SPHERE_MINIMUM) |>\n",
    "  group_by(water_service) |>\n",
    "  mutate(pct = round(100 * n / sum(n), 1)) |>\n",
    "  ungroup()"
   ],
   "id": "cell-014"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Households on basic service still fall below the Sphere minimum. Access and\n",
    "quantity answer different questions and neither stands in for the other.\n",
    "\n",
    "## What to report\n",
    "\n",
    "The rung, the denominator it rests on, and the quantity indicator separately —\n",
    "plus the households that could not be classified at all. A household with no\n",
    "collection time recorded is not basic service, it is unknown, and folding it into\n",
    "the basic count is how a coverage figure drifts upward with nobody deciding that\n",
    "it should."
   ],
   "id": "cell-015"
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "R",
   "language": "R",
   "name": "ir"
  },
  "language_info": {
   "name": "R",
   "file_extension": ".r"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
