cassionAnalyse de données

Leçon 5 sur 8

Unité · Un modèle, plusieurs sorties

Un modèle, douze districts, aucune copie

Douze rapports de district produits par copier-coller sont douze documents qui divergeront d'ici mars. Un modèle paramétré rendu douze fois est un document qui ne le peut pas, et le mécanisme tient en une ligne de frontmatter.

PythonR135 minCritères d'évaluation du CAD de l'OCDEDéfinitions d'indicateurs de l'UNICEFNorme humanitaire fondamentale (CHS)

Le rapport qui devient douze rapports

Un cluster veut un rapport par district. L’approche évidente est d’en écrire un, de le copier onze fois et de changer le filtre en tête de chacun.

D’ici mars, ces douze fichiers divergent. Quelqu’un corrige la normalisation des noms de communes dans l’un. Quelqu’un ajoute une réserve dans trois. La définition de l’indicateur change et huit sont mis à jour. Rien dans aucune compilation ne peut détecter qu’ils ont dérivé, parce que ce sont douze documents sans lien.

Un modèle paramétré est un seul document. Le corriger corrige douze rapports, et une différence n’a nulle part où se cacher.

Quarto, que cette plateforme emploie déjà

---
title: "Nutrition surveillance: `r params$district`"
params:
  district: "Artibonite"
  as_of: "2025-03-31"
format: pdf
---
quarto render report.qmd -P district:Nord-Ouest -P as_of:2025-03-31

Le document déclare ses paramètres et le moteur de rendu les fournit. Le corps renvoie à params$district — en R — ou à district depuis la cellule parameters injectée en Python, et rien d’autre dans le fichier ne mentionne un district précis.

# | tags: [parameters]
district = "Artibonite"
as_of = "2025-03-31"
params$district

La valeur par défaut du frontmatter est ce qui rend le modèle prévisualisable. Vous l’ouvrez, il se rend pour Artibonite, et vous éditez quelque chose que vous voyez.

Rendre les douze

import subprocess, pathlib

DISTRICTS = ["Artibonite", "Centre", "Grande-Anse", "Nippes", "Nord",
             "Nord-Est", "Nord-Ouest", "Ouest", "Sud", "Sud-Est"]

for district in sorted(DISTRICTS):
    slug = district.lower().replace(" ", "-")
    subprocess.run([
        "quarto", "render", "report.qmd",
        "-P", f"district:{district}",
        "-P", f"as_of:{AS_OF}",
        "--output", f"outputs/reports/{slug}.pdf",
    ], check=True)
for (d in sort(districts)) {
  quarto::quarto_render(
    "report.qmd",
    execute_params = list(district = d, as_of = as_of),
    output_file = paste0(tolower(gsub(" ", "-", d)), ".pdf")
  )
}

check=True est l’argument important. Sans lui, un rendu échoué est un fichier manquant que la boucle enjambe silencieusement, et vous le découvrez quand quelqu’un demande le rapport des Nippes.

Triez la liste des districts, pour la raison qu’a donnée la leçon précédente : l’ordre de sortie de la boucle ne devrait pas dépendre de la façon dont la liste a été assemblée.

Écrire un modèle qui survit à ses propres paramètres

Quatre choses qu’un rapport copié se permet et qu’un modèle ne peut pas.

Aucun nombre codé en dur dans la prose. Chaque chiffre du texte vient des données.

Coverage in `r params$district` is
`r scales::percent(coverage, accuracy = 0.1)`, against a national figure of
`r scales::percent(national, accuracy = 0.1)`.

Gérez le district sans données. L’un des douze aura un tableau vide, et un modèle qui suppose des lignes produit une trace d’erreur ou, pire, une page de NaN.

if summary.empty:
    print(f"No screening was conducted in {district} during this period.")
else:
    ...
if (nrow(summary) == 0) cat("No screening was conducted in this period.")

Gérez le singulier. « 1 communes » dans onze rapports est le signe d’un modèle que personne n’a relu.

Gérez le petit dénominateur. Un district avec 40 dépistages obtient un intervalle trois fois plus large qu’un district avec 400, et le modèle doit le dire plutôt que d’imprimer les deux à une décimale comme s’ils étaient comparables.

Ce qui va dans le modèle et ce qui va dans la chaîne

Dans le modèle Dans la chaîne
Mise en page, prose, forme de l’argumentation La lecture des données brutes
Un tableau de synthèse calculé depuis des données préparées Nettoyage et normalisation
Les figures, tracées depuis des données préparées Toute transformation de plus d’une ligne ou deux
Réserves, seuils et définitions Tout ce que deux rapports partageraient

Le modèle devrait lire un fichier préparé, non l’export brut. Si douze rendus refont chacun le nettoyage, le nettoyage tourne douze fois, prend douze fois plus de temps, et — pire — peut être édité dans le modèle pour un seul district.

# run.py
clean()                       # once
build_figures()               # once
for district in DISTRICTS:    # twelve times, from prepared data
    render(district)
# run.R, same shape

Le paramètre qu’il faut toujours avoir

params:
  district: "Artibonite"
  as_of: "2025-03-31"
  data_version: "v1"

Un paramètre de version de données fait dire au rapport quel fichier il a lu, ce qui est la question de provenance qu’un lecteur pose six mois plus tard. Imprimez-le dans le colophon.

Et un paramètre as_of plutôt que l’horloge, pour la raison qu’a donnée la leçon 4 : un rapport qui dit « produit le 14 mars » change à chaque reconstruction, et un rapport qui dit « données au 31 mars » non.

Là où cette plateforme fait la même chose

Chaque leçon de ce site produit un diaporama en deux langues et trois formats — un PDF Beamer, un fichier PowerPoint et la source LaTeX, six fichiers par leçon — et aucun n’a été composé à la main.

slide-plan.mjs planifie le diaporama une fois depuis le corps de la leçon, et le PDF Beamer, le fichier PowerPoint et le lecteur de diapositives dans le navigateur sont trois rendus de ce plan unique.

Le raisonnement est celui des douze districts, à une autre échelle. Un tableau slides: dans le frontmatter produirait de meilleures diapositives en principe et de pires en pratique : il signifie que chaque nouvelle leçon exige une seconde passe de rédaction en deux langues, et c’est cette passe qui saute.

La conséquence pour les auteurs est réelle et vaut d’être nommée. Parce que le diaporama est dérivé, les leçons sont écrites avec des listes, des amorces en gras, des tableaux et des encadrés — parce que c’est de cela qu’un diaporama est fait. Un système paramétré façonne ses entrées, et prétendre le contraire produit des modèles qui se battent contre leur propre contenu.

Rapportez-le en entier

District reports

  Twelve district reports are rendered from one template, report.qmd, by
  run.py. No district-specific text exists outside the data.

  Parameters: district, as_of (2025-03-31), data_version (v1). Each report
  prints all three in its colophon.

  Cleaning and figure generation run once, before rendering; the template
  reads prepared data and performs no transformation.

  Districts with no screening in the period render a stated "no data"
  section rather than an empty table.

  Rendering is verified by `--check`: a failed render stops the run rather
  than leaving a missing file.

La dernière ligne est ce qui transforme douze fichiers rendus en douze rapports qu’on peut envoyer. Une boucle qui avale les échecs produit onze rapports et une question que personne ne pose jusqu’à ce que la mauvaise personne le remarque.

La suite

Une chaîne qui rend douze rapports depuis des données amont cassées rend douze rapports cassés. La leçon suivante la fait s’arrêter à la place — bruyamment, au point de défaillance, plutôt que de produire un nombre plausible que personne ne peut vérifier.

Animer cette leçon

La leçon en diaporama, la prose étant reléguée dans les notes du présentateur plutôt que projetée. Produit à partir de cette page, dont il ne peut donc pas s'écarter.

Lancer le diaporamaLire les diapositives

Le PDF ne requiert aucun logiciel et se projette depuis n'importe quel poste. Le fichier PowerPoint est fait pour être modifié : appliquez la charte de votre organisation, retirez une section pour une séance plus courte, ou fusionnez deux leçons en atelier.