{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Ajuster la couverture pour la complétude du rapportage\n",
    "\n",
    "*Couverture vaccinale de routine, 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/routine-vaccination-coverage-2024/completeness-adjustment.r.fr.ipynb)"
   ],
   "id": "cell-000"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Le problème en une phrase\n",
    "\n",
    "En août, la couverture penta3 rapportée tombe sous le tiers. Rien n'est arrivé à\n",
    "la vaccination : 71 % des formations sanitaires n'ont simplement pas transmis de\n",
    "rapport, et leurs zéros sont entrés au numérateur pendant que leurs populations\n",
    "cibles restaient au dénominateur.\n",
    "\n",
    "Tous les jeux de données de cette plateforme sont synthétiques. La couverture\n",
    "présentée ne décrit aucun district réel.\n",
    "\n",
    "## Mise en place"
   ],
   "id": "cell-001"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| message: false\n",
    "library(readr)\n",
    "library(dplyr)\n",
    "library(tidyr)\n",
    "library(ggplot2)\n",
    "\n",
    "URL <- paste0(\n",
    "  \"https://data-analysis.cassion.dev/datasets/files/\",\n",
    "  \"vaccination-coverage-2024.v1.csv\"\n",
    ")\n",
    "\n",
    "epi <- read_csv(URL, col_types = cols(\n",
    "  facility_id = col_character(),\n",
    "  period      = col_date(),\n",
    "  .default    = col_guess()\n",
    ")) |>\n",
    "  mutate(mois = format(period, \"%Y-%m\"))\n",
    "\n",
    "MOIS_PAR_AN <- 12"
   ],
   "id": "cell-002"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## La complétude est un indicateur à part entière\n",
    "\n",
    "Rapportez-la avant tout ce qui se calcule à partir des données qu'elle\n",
    "conditionne. Un chiffre de couverture placé à côté d'une complétude de 29 % se\n",
    "lit très différemment du même chiffre présenté seul."
   ],
   "id": "cell-003"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "completude <- epi |>\n",
    "  distinct(facility_id, mois, report_submitted) |>\n",
    "  group_by(mois) |>\n",
    "  summarise(\n",
    "    formations = n(),\n",
    "    rapportees = sum(report_submitted),\n",
    "    completude = round(mean(report_submitted), 3),\n",
    "    .groups = \"drop\"\n",
    "  )\n",
    "\n",
    "completude"
   ],
   "id": "cell-004"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Août à 29 % et septembre à 45 %. Tout ce qui suit porte sur ce que ces deux mois\n",
    "font à une série de couverture.\n",
    "\n",
    "## Trois dénominateurs, trois récits différents"
   ],
   "id": "cell-005"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "penta3 <- epi |> filter(antigen == \"penta3\")\n",
    "\n",
    "serie <- penta3 |>\n",
    "  group_by(mois) |>\n",
    "  summarise(\n",
    "    doses            = sum(doses_administered),\n",
    "    cible_totale     = sum(target_population),\n",
    "    cible_rapportee  = sum(target_population[report_submitted]),\n",
    "    completude       = mean(report_submitted),\n",
    "    .groups = \"drop\"\n",
    "  ) |>\n",
    "  mutate(\n",
    "    non_corrigee       = doses / (cible_totale / MOIS_PAR_AN),\n",
    "    declarants_seuls   = doses / (cible_rapportee / MOIS_PAR_AN),\n",
    "    mise_a_l_echelle   = non_corrigee / completude\n",
    "  )\n",
    "\n",
    "serie |>\n",
    "  select(mois, completude, non_corrigee, declarants_seuls, mise_a_l_echelle) |>\n",
    "  mutate(across(where(is.numeric), ~ round(.x, 3)))"
   ],
   "id": "cell-006"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Non corrigée** compte la population cible de toutes les formations au\n",
    "dénominateur et les seules doses des formations déclarantes au numérateur. Les\n",
    "deux ne décrivent pas la même population, et le résultat suit le rapportage\n",
    "plutôt que la vaccination.\n",
    "\n",
    "**Déclarants seuls** restreint le dénominateur aux formations ayant rapporté.\n",
    "Numérateur et dénominateur décrivent désormais le même ensemble, et la série\n",
    "reste stable en août.\n",
    "\n",
    "**Mise à l'échelle** divise le chiffre non corrigé par la complétude. C'est\n",
    "l'ajustement vers lequel on se tourne en premier, et c'est celui dont il faut se\n",
    "méfier.\n",
    "\n",
    "## Pourquoi la mise à l'échelle sur-corrige"
   ],
   "id": "cell-007"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "serie |>\n",
    "  select(mois, completude, declarants_seuls, mise_a_l_echelle) |>\n",
    "  mutate(\n",
    "    ecart = round(mise_a_l_echelle - declarants_seuls, 3),\n",
    "    across(where(is.numeric), ~ round(.x, 3))\n",
    "  ) |>\n",
    "  arrange(desc(abs(ecart)))"
   ],
   "id": "cell-008"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En août, la mise à l'échelle produit une couverture proche de 99 % — supérieure à\n",
    "celle de tous les mois où presque tout le monde avait rapporté. Diviser par la\n",
    "complétude suppose que les formations silencieuses auraient obtenu exactement les\n",
    "mêmes résultats que les déclarantes. Ce n'est généralement pas le cas : une\n",
    "formation qui cesse de rapporter est souvent celle qui connaît une rupture de\n",
    "stock, un déficit de personnel ou un problème d'accès — autrement dit, celle qui\n",
    "obtient les moins bons résultats.\n",
    "\n",
    "L'hypothèse n'est pas toujours fausse, mais elle doit être énoncée, et elle ne\n",
    "doit pas être posée en silence par un analyste qui saisit la formule la plus\n",
    "simple.\n",
    "\n",
    "## Le graphique qui l'établit"
   ],
   "id": "cell-009"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| fig-width: 8\n",
    "#| fig-height: 5\n",
    "serie |>\n",
    "  select(mois, non_corrigee, declarants_seuls, mise_a_l_echelle) |>\n",
    "  pivot_longer(-mois, names_to = \"methode\", values_to = \"couverture\") |>\n",
    "  ggplot(aes(x = mois, y = 100 * couverture, group = methode, colour = methode)) +\n",
    "  geom_line(linewidth = 0.8) +\n",
    "  geom_point(size = 1.8) +\n",
    "  scale_colour_manual(values = c(\n",
    "    non_corrigee     = \"#B5533C\",\n",
    "    declarants_seuls = \"#2F5D50\",\n",
    "    mise_a_l_echelle = \"#9AA8A3\"\n",
    "  )) +\n",
    "  labs(\n",
    "    x = NULL, y = \"couverture penta3 (%)\", colour = NULL,\n",
    "    title = \"La chute d'aout est un artefact de rapportage, non un effondrement\"\n",
    "  ) +\n",
    "  theme_minimal(base_size = 11) +\n",
    "  theme(\n",
    "    axis.text.x = element_text(angle = 45, hjust = 1),\n",
    "    panel.grid.minor = element_blank()\n",
    "  )"
   ],
   "id": "cell-010"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La courbe non corrigée présente une falaise en août. Celle restreinte aux\n",
    "déclarants, non. La falaise est une propriété de qui a transmis un formulaire, et\n",
    "un responsable de programme à qui l'on montrerait le premier graphique passerait\n",
    "un mois à enquêter sur un effondrement qui n'a pas eu lieu.\n",
    "\n",
    "## Quelles formations se sont tues"
   ],
   "id": "cell-011"
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "silencieuses <- epi |>\n",
    "  distinct(facility_id, mois, report_submitted, facility_type) |>\n",
    "  filter(mois %in% c(\"2024-08\", \"2024-09\")) |>\n",
    "  group_by(facility_type) |>\n",
    "  summarise(\n",
    "    formations_mois = n(),\n",
    "    rapportees      = sum(report_submitted),\n",
    "    completude      = round(mean(report_submitted), 3),\n",
    "    .groups = \"drop\"\n",
    "  )\n",
    "\n",
    "silencieuses"
   ],
   "id": "cell-012"
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si la complétude diffère selon le type de formation, le dénominateur restreint\n",
    "aux déclarants reste biaisé : il décrit désormais les formations qui rapportent,\n",
    "lesquelles ne constituent pas un échantillon aléatoire de l'ensemble. Vérifiez-le\n",
    "avant de présenter la série corrigée comme si elle était la vérité.\n",
    "\n",
    "## Ce qu'il faut rapporter\n",
    "\n",
    "La complétude à côté de la couverture, systématiquement, dans le même tableau. Le\n",
    "dénominateur employé, nommé. Et si vous avez mis à l'échelle par la complétude,\n",
    "l'hypothèse que les formations silencieuses obtiennent les mêmes résultats que\n",
    "les déclarantes, écrite noir sur blanc — car c'est cette hypothèse qui fait le\n",
    "plus gros du travail, et non l'arithmétique."
   ],
   "id": "cell-013"
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "R",
   "language": "R",
   "name": "ir"
  },
  "language_info": {
   "name": "R",
   "file_extension": ".r"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
