cassionAnalyse de données

Leçon 1 sur 8

Unité · Calculez-le avant d'y croire

Un score, deux seuils, huit fois la réponse

Avec les seuils 21/35, 0,9 % de ces ménages ont une consommation alimentaire pauvre. Avec les seuils 28/42, 7,3 % en ont une. Les deux sont des applications correctes d'un standard publié, et un rapport qui ne dit pas lequel il a employé ne l'est pas.

PythonR135 minCadre intégré de classification de la sécurité alimentaire (IPC)Standards Sphère

Le score, à partir de ses composantes

Le score de consommation alimentaire demande combien des sept derniers jours un ménage a consommé chacun des huit groupes alimentaires, et les pondère par leur densité nutritionnelle.

import pandas as pd

survey = pd.read_csv("food-security-survey-2024.v1.csv")

WEIGHTS = {
    "fcs_cereals_tubers": 2.0,
    "fcs_pulses": 3.0,
    "fcs_vegetables": 1.0,
    "fcs_fruit": 1.0,
    "fcs_meat_fish_eggs": 4.0,
    "fcs_dairy": 4.0,
    "fcs_oils_fats": 0.5,
    "fcs_sugar": 0.5,
}
library(dplyr)

weights <- c(fcs_cereals_tubers = 2, fcs_pulses = 3, fcs_vegetables = 1,
             fcs_fruit = 1, fcs_meat_fish_eggs = 4, fcs_dairy = 4,
             fcs_oils_fats = 0.5, fcs_sugar = 0.5)

La viande et les produits laitiers valent quatre points par jour et le sucre un demi-point, parce que le score est un proxy de la qualité du régime et non de la quantité. Un ménage qui mange céréales et huile chaque jour obtient 17,5 ; un ménage qui mange de la viande deux fois par semaine obtient 8 pour ces deux jours seuls.

Deux choses doivent avoir lieu avant la multiplication, et les deux sont couramment omises.

Contrôlez l’étendue d’abord

groups = list(WEIGHTS)
print(survey[groups].max())
print(f"values above 7: {(survey[groups] > 7).sum().sum()}")
survey |> summarise(across(all_of(names(weights)), max, .names = "{.col}"))

Vingt-trois cellules portent une valeur supérieure à sept jours, face à un rappel de sept jours. Elles sont impossibles, et un score calculé sans contrôle les reprend : le SCA le plus élevé de ce fichier vaut 114,5 sans correction contre 105,5 avec les valeurs plafonnées.

La correction est un jugement. Plafonner à sept suppose une faute de frappe sur le dernier chiffre ; vider suppose que la réponse est inconnue. Dites ce que vous avez fait, et notez que le plafonnement est ici le choix le plus prudent parce qu’il garde le ménage au dénominateur.

Un vide n’est pas un zéro

blanks = survey[groups].isna().sum()
print(blanks[blanks > 0])
print(f"households with any blank: {survey[groups].isna().any(axis=1).sum()}")
survey |> summarise(across(all_of(names(weights)), ~ sum(is.na(.x))))

127 cellules vides pour les produits laitiers, les fruits et la viande, dans 123 ménages. Ce sont les trois groupes les plus pondérés après les légumineuses, et ce n’est pas un hasard — ce sont les questions qu’un enquêteur saute quand la réponse est manifestement aucune et que le formulaire n’impose pas de réponse.

Traiter un vide comme zéro jour fait paraître le ménage moins bien nourri qu’il ne l’est. sum() en pandas le fait par défaut, et sum(na.rm = TRUE) en R le fait sur demande.

# The silent one: NaN treated as zero.
naive = sum(survey[group].fillna(0) * weight for group, weight in WEIGHTS.items())

# The honest one: a household missing any group has no score.
capped = survey[groups].clip(upper=7)
fcs = sum(capped[group] * weight for group, weight in WEIGHTS.items())
fcs = fcs.where(capped.notna().all(axis=1))

print(f"analysable: {fcs.notna().sum()} of {len(survey)}")
survey |>
  mutate(across(all_of(names(weights)), ~ pmin(.x, 7))) |>
  rowwise() |>
  mutate(fcs = if (anyNA(c_across(all_of(names(weights))))) NA_real_
               else sum(c_across(all_of(names(weights))) * weights))

1 989 ménages sur 2 112 ont un module de consommation complet. Rapportez ce nombre. Une prévalence calculée sur 1 989 imprimée à côté d’un chiffre démographique calculé sur 2 112 invite à une soustraction qui ne signifie rien.

Les deux jeux de seuils

for poor, borderline in [(21, 35), (28, 42)]:
    bands = pd.cut(fcs, [-1, poor, borderline, 200],
                   labels=["poor", "borderline", "acceptable"])
    print(f"{poor}/{borderline}: ",
          (bands.value_counts(normalize=True) * 100).round(1).to_dict())
classify <- function(x, poor, borderline) {
  cut(x, c(-1, poor, borderline, Inf), labels = c("poor", "borderline", "acceptable"))
}
Seuils Pauvre Limite Acceptable
21 / 35 0,9 % 22,7 % 76,4 %
28 / 42 7,3 % 38,5 % 54,1 %

Le chiffre annoncé de consommation pauvre est huit fois plus grand avec un jeu qu’avec l’autre, et les deux sont le standard publié. Le jeu 28/42 existe pour les contextes où huile et sucre sont consommés de façon quasi universelle : ces deux groupes ajoutent des points à presque tous les ménages, ce qui décale toute la distribution vers la droite et rend les seuils bas trop généreux.

Le SCA médian vaut ici 43,5, l’huile est consommée en moyenne 4,7 jours par semaine et le sucre 3,8 — assez fréquemment pour que les deux groupes ajoutent environ 4 points à un ménage typique avant tout aliment à densité nutritionnelle.

staples = survey[["fcs_oils_fats", "fcs_sugar"]].mean()
print(f"oil eaten {staples['fcs_oils_fats']:.1f} days a week on average")
print(f"sugar eaten {staples['fcs_sugar']:.1f} days")
print("→ 28/42 is the defensible set here; say so in the methodology note")
survey |> summarise(oil = mean(fcs_oils_fats), sugar = mean(fcs_sugar))

Choisissez sur les données, énoncez le choix, et ne le changez jamais d’un passage à l’autre. Un programme qui rapporte 0,9 % une année et 7,3 % la suivante parce que quelqu’un a changé de jeu de seuils a rapporté une dégradation d’un facteur quatre qui n’a pas eu lieu.

Ce que le score n’est pas

Trois choses qu’on demande régulièrement au SCA et qu’il ne peut pas faire.

Ce n’est pas une mesure de quantité. Un ménage qui mange de petites portions de huit groupes obtient un bon score. Le score porte sur la diversité et la fréquence alimentaires, et les standards caloriques de Sphère sont un autre instrument.

Il n’est pas comparable entre contextes aux régimes différents. Les pondérations sont fixées mondialement et les groupes ne sont pas consommés dans les mêmes proportions partout, ce qui est précisément pourquoi deux jeux de seuils existent.

Ce n’est pas une phase IPC. C’est un indicateur de résultat qui alimente une ligne d’un tableau de preuves parmi plusieurs, et la dernière unité de ce cours porte sur la différence.

Rapportez-le avec ses règles

Food consumption, lean season 2024

  Analysable                  1,989 of 2,112 households (94.2%)
  Poor consumption               7.3%   146 households
  Borderline                    38.5%   766
  Acceptable                    54.1%  1,077

  FCS computed on the 28/42 threshold set: oil is eaten a mean 4.7 days
  a week and sugar 3.8, so the standard 21/35 cut-offs would classify
  0.9% as poor and understate the caseload.
  23 impossible values capped at 7 days; 123 households excluded for an
  incomplete consumption module.

Les seuils nommés, les exclusions comptées, et la raison du choix en une phrase. C’est ce paragraphe qui rend les 7,3 % vérifiables, et il est trois lignes plus long que la version que porte la plupart des rapports.

La suite

La consommation alimentaire dit ce qu’un ménage a mangé. La leçon suivante dit ce dont il s’est privé et ce qu’il a fait pour éviter de s’en priver — deux instruments de plus sur les mêmes ménages, chacun avec sa propre règle d’exclusion.

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.