Retour à la leçon·Leçon 4 sur 8·Une ligne par quoi ?
Long, large, et le groupe répété entre les deux
Le même diaporama que les téléchargements, rendu sous forme de page. Lancez le diaporama pour le présenter en plein écran — les flèches ou un clic avancent d'une diapositive, Échap quitte.
Ce que couvre cette leçon
- Deux formes, et l'usage de chacune
- Le groupe répété aplati
- Les emplacements vides, et ceux qui veulent dire quelque chose
- Du long au large — le tableau croisé DHIS2
- La valeur de remplissage qui invente des données
- Le tableau large que vous ne pouvez pas filtrer
- Aller-retour, et son contrôle
- La suite
Notes du présentateur
Pivoter parce que l'analyse a besoin de la forme, pas parce que l'export est arrivé ainsi. Le groupe répété aplati, le tableau croisé DHIS2, et la valeur de remplissage qui transforme « non rapporté » en zéro.Deux formes, et l'usage de chacune
- Long — une ligne par observation, une colonne par variable
- Large — une ligne par unité, une colonne par mesure
Notes du présentateur
Les mêmes données, deux fois. Long — une ligne par observation, une colonne par variable. 2 736 lignes de formation sanitaire, période, antigène, doses. Large — une ligne par unité, une colonne par mesure. 456 lignes de formation sanitaire et période, avec six colonnes d'antigènes. Aucune n'est correcte dans l'absolu. La règle qui fonctionne vraiment :Deux formes, et l'usage de chacune
Le long sert à calculer. Le large sert à lire.
Notes du présentateur
Regroupez, filtrez, joignez et résumez sur des données longues, car chacun de ces verbes prend un nom de colonne et le format long a une colonne par concept. Pivotez vers le large en toute dernière étape avant qu'un humain le regarde, car une personne qui lit un tableau veut les six antigènes côte à côte. L'essentiel des ennuis de cette leçon vient de l'inverse — recevoir un export large et l'analyser large, ce qui transforme « calculer la couverture de chaque antigène » d'une ligne en six.Le groupe répété aplati — Exemple
household_id, member_1_age, member_1_sex, member_2_age, member_2_sex, member_3_age, ...Notes du présentateur
Une plateforme de collecte pose une question répétée — chaque enfant du ménage, chaque symptôme observé, chaque source d'eau utilisée — et l'exporte en colonnes numérotées.Le groupe répété aplati — En Python
long = members_wide.melt( id_vars="household_id", var_name="field", value_name="value", ) long[["slot", "attribute"]] = long["field"].str.extract(r"member_(\d+)_(\w+)") members = ( long.dropna(subset=["slot"]) .pivot(index=["household_id", "slot"], columns="attribute", values="value") .reset_index() )Notes du présentateur
C'est l'une des formes les plus répandues dans ce secteur et elle est inexploitable telle quelle. Vous ne pouvez pas compter les membres, ni filtrer les enfants de moins de cinq ans, ni joindre à quoi que ce soit, parce que ce sur quoi vous voulez travailler est étalé en colonnes au lieu de descendre en lignes.Le groupe répété aplati — En R
members <- members_wide |> tidyr::pivot_longer( cols = tidyr::starts_with("member_"), names_to = c("slot", "attribute"), names_pattern = "member_(\\d+)_(\\w+)", values_to = "value" ) |> tidyr::pivot_wider(names_from = attribute, values_from = value)Le groupe répété aplati
- Deux pivots, pas un — Le premier transforme chaque colonne numérotée en lignes ; le second retransforme les noms…
Notes du présentateur
Deux pivots, pas un. Le premier transforme chaque colonne numérotée en lignes ; le second retransforme les noms d'attributs en colonnes. C'est en essayant de le faire d'un seul geste qu'on se bloque, car le nom de colonne porte deux faits — quel membre et quel attribut — qu'il faut séparer avant de pouvoir utiliser l'un ou l'autre.names_patternen R et l'extraction par expression régulière en Python opèrent cette séparation. Mettez le motif au point face aux vrais noms de colonnes avant d'écrire quoi que ce soit d'autre ; unNAsilencieux ici devient un membre qui disparaît.Les emplacements vides, et ceux qui veulent dire quelque chose — En Python
print(members["age"].isna().sum(), "empty slots") members = members[members["age"].notna() | members["sex"].notna()]Notes du présentateur
Un formulaire prévu pour huit membres et un ménage de trois exporte cinq emplacements vides. La plupart ne sont rien. L'un d'eux ne l'est pas.Les emplacements vides, et ceux qui veulent dire quelque chose — En R
members |> summarise(empty = sum(is.na(age) & is.na(sex))) members <- members |> filter(!is.na(age) | !is.na(sex))Les emplacements vides, et ceux qui veulent dire quelque chose — En Python
counted = members.groupby("household_id").size().rename("members_found") check = households.join(counted, on="household_id") mismatch = check[check["members_found"] != check["household_size"]] print(len(mismatch), "households where the roster does not match household_size")Notes du présentateur
La distinction — un emplacement dont tous les attributs sont manquants est du remplissage et doit partir. Un emplacement dont l'âge manque mais dont le sexe est renseigné est un membre réel avec un âge manquant, et le supprimer retire une personne du ménage. Filtrez sur « toute la ligne est vide », jamais sur une seule colonne. Confrontez le résultat à quelque chose que le fichier sait déjà.Les emplacements vides, et ceux qui veulent dire quelque chose — En R
check <- households |> left_join(count(members, household_id, name = "members_found"), by = "household_id") |> filter(members_found != household_size)Notes du présentateur
La taille de ménage déclarée et le nombre de lignes de membres doivent concorder. Là où ce n'est pas le cas, soit le dépliage a perdu quelqu'un, soit l'entretien.Du long au large — le tableau croisé DHIS2 — En Python
wide = vax.pivot_table( index=["facility_id", "period"], columns="antigen", values="doses_administered", aggfunc="sum", ).reset_index() print(len(vax), "->", len(wide))Notes du présentateur
L'extraction vaccinale arrive en long — une ligne par formation sanitaire, période et antigène — et c'est la bonne forme pour calculer. Pour la poser devant un responsable sanitaire de district, pivotez.Du long au large — le tableau croisé DHIS2 — En R
wide <- vax |> tidyr::pivot_wider( id_cols = c(facility_id, period), names_from = antigen, values_from = doses_administered ) cat(nrow(vax), "->", nrow(wide), "\n")Du long au large — le tableau croisé DHIS2
- Le
pivotde pandas lève une erreur sur les doublons ;pivot_tableles agrège — Prenezpivotd'abord, précisément…
Notes du présentateur
2 736 lignes deviennent 456 — 38 formations sanitaires sur 12 mois — avec six colonnes d'antigènes. L'arithmétique mérite d'être vérifiée à chaque fois : 2 736 divisé par 6 antigènes fait 456, donc rien n'a été agrégé au passage. Quand la division ne tombe pas juste, la table longue avait des doublons sur la clé de pivot, etpivot_tableles aura silencieusement sommés. Lepivotde pandas lève une erreur sur les doublons ;pivot_tableles agrège. Prenezpivotd'abord, précisément parce que vous voulez l'erreur. Lepivot_widerde tidyr avertit et produit des colonnes-listes, ce qui est plus laid et tout aussi informatif.- Le
La valeur de remplissage qui invente des données — En Python
wide = vax.pivot_table( index=["facility_id", "period"], columns="antigen", values="doses_administered", aggfunc="sum", fill_value=0, )La valeur de remplissage qui invente des données — En R
wide <- vax |> tidyr::pivot_wider(names_from = antigen, values_from = doses_administered, values_fill = 0)La valeur de remplissage qui invente des données — En Python
wide = wide.assign(reported=wide.notna().sum(axis=1))Notes du présentateur
fill_value=0remplit les combinaisons absentes des données longues. Pour des doses administrées c'est souvent juste — une formation sanitaire sans ligne penta3 n'a réellement administré aucune dose de penta3. Pour un taux, c'est toujours faux. Une cellule de couverture manquante signifie « non calculée », et la remplir par zéro publie une formation sanitaire à 0 % de couverture qui n'a simplement pas rapporté. Le cours de nettoyage faisait ce constat à propos du rapportage ; ici il revient par une autre porte, comme un argument de pivot que personne ne perçoit comme une décision. Le réflexe sûr est de la laisser manquante et de la traiter explicitement.La valeur de remplissage qui invente des données — En R
wide <- wide |> mutate(reported = rowSums(!is.na(across(bcg:penta3))))Le tableau large que vous ne pouvez pas filtrer — En Python
low = vax[vax["doses_administered"] < 10]Notes du présentateur
Une fois en large, une question comme « quels couples formation-mois ont eu moins de dix doses d'un antigène quelconque » demande six comparaisons reliées parou, et ajouter un septième antigène oblige à toutes les modifier. La même question sur des données longues tient en une ligne.Le tableau large que vous ne pouvez pas filtrer — En R
low <- vax |> filter(doses_administered < 10)Notes du présentateur
C'est le test qui dit si vous avez pivoté trop tôt. Si votre opération suivante nomme plus d'une colonne contenant le même type de valeur, revenez au long.Aller-retour, et son contrôle — En Python
back = wide.melt(id_vars=["facility_id", "period"], var_name="antigen", value_name="doses_administered").dropna() assert len(back) == len(vax) assert back["doses_administered"].sum() == vax["doses_administered"].sum()Notes du présentateur
Toute restructuration digne de confiance est réversible. Affirmez-le une fois, pendant que vous écrivez le code.Aller-retour, et son contrôle — En R
back <- wide |> tidyr::pivot_longer(-c(facility_id, period), names_to = "antigen", values_to = "doses_administered") |> filter(!is.na(doses_administered)) stopifnot(nrow(back) == nrow(vax), sum(back$doses_administered) == sum(vax$doses_administered))Notes du présentateur
Nombre de lignes et total. Deux lignes, et elles attrapent un antigène silencieusement perdu, un doublon qui s'est fait sommer, et une valeur de remplissage qui a inventé des lignes.La suite
- Vous savez désormais donner à une table la forme qu'exige la question.
Notes du présentateur
Vous savez désormais donner à une table la forme qu'exige la question. L'unité suivante porte sur les tables qu'il faut faire venir de l'extérieur — la base de population qui fournit un dénominateur de couverture, et le calendrier qui dit quelles périodes auraient dû exister.