Retour à la leçon·Leçon 8 sur 8·La table que vous analysez
Une seule table, assemblée à dessein
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
- La table dont tout le reste se calcule
- Décidez l'unité d'analyse avant d'écrire une ligne
- Construisez d'abord l'ossature
- Attachez une mesure à la fois, et affirmez à chaque raccord
- Portez la provenance
- Affirmez ce qui doit être vrai de la table finie
- Ce qui n'a pas sa place dans la table
- Enregistrez-la avec sa granularité dans le nom
- Ce que ce cours vous laisse
Notes du présentateur
Partir d'une ossature des unités sur lesquelles vous devez rapporter, attacher une mesure à la fois, affirmer à chaque raccord, et porter des colonnes de provenance pour qu'un relecteur puisse remonter tout chiffre jusqu'à son fichier d'origine.La table dont tout le reste se calcule
- À l'issue d'un assemblage, vous voulez exactement une table : une ligne par unité d'analyse, une colonne par mesure, et rien d'autre.
Notes du présentateur
À l'issue d'un assemblage, vous voulez exactement une table : une ligne par unité d'analyse, une colonne par mesure, et rien d'autre. Chaque graphique, chaque résumé et chaque chiffre du rapport en sortent. Cette discipline compte parce que l'alternative est ce qui se produit d'ordinaire — un notebook de onze tableaux nommésdf,df2,merged,merged_finaletmerged_final_v2, où la table dont un graphique a été tiré est celle qui se trouvait en mémoire à ce moment-là. Personne ne peut reproduire cela, y compris la personne qui l'a écrit.Décidez l'unité d'analyse avant d'écrire une ligne — En Python
# unit of analysis: one school x month # rows: 24 schools x 3 months = 72Notes du présentateur
L'unité d'analyse est ce sur quoi votre rapport formule des affirmations. Écrivez-la d'abord, car elle détermine toutes les jointures qui suivent.Décidez l'unité d'analyse avant d'écrire une ligne — En R
# unit of analysis: one school x month # rows: 24 schools x 3 months = 72Notes du présentateur
Si vous ne pouvez pas l'énoncer en une ligne, l'analyse n'est pas prête à être assemblée. Et si deux chiffres de votre rapport ont des unités différentes — l'un par école, l'autre par enfant — ils appartiennent à deux tables, pas à une.Construisez d'abord l'ossature — En Python
schools = roster["school_id"].drop_duplicates() months = pd.Series(["2024-02", "2024-03", "2024-04"], name="month") analysis = ( pd.MultiIndex.from_product([schools, months], names=["school_id", "month"]) .to_frame(index=False) ) print(len(analysis), "rows in the spine")Notes du présentateur
L'ossature est l'ensemble complet des unités sur lesquelles vous devez rapporter, bâti à partir de l'autorité et non des données. Chaque mesure y est ensuite attachée.Construisez d'abord l'ossature — En R
analysis <- tidyr::expand_grid( school_id = unique(roster$school_id), month = c("2024-02", "2024-03", "2024-04") )Construisez d'abord l'ossature
- L'ossature d'abord est l'habitude la plus utile de cette leçon — Un couple école-mois sans données existe désormais…
Notes du présentateur
L'ossature d'abord est l'habitude la plus utile de cette leçon. Un couple école-mois sans données existe désormais comme une ligne aux mesures manquantes, ce qui est visible, au lieu de ne pas exister, ce qui ne l'est pas. C'est le même argument que la grille de périodes, appliqué à l'assemblage entier.Attachez une mesure à la fois, et affirmez à chaque raccord — En Python
def attach(base, addition, on, name): before = len(base) out = base.merge(addition, on=on, how="left", validate="one_to_one") if len(out) != before: raise ValueError(f"{name}: row count changed {before} -> {len(out)}") filled = out[addition.columns.difference(on)].notna().any(axis=1).mean() print(f"{name}: attached, {filled:.1%} of spine rows matched") return out analysis = attach(analysis, attendance_by_school_month, ["school_id", "month"], "attendance") analysis = attach(analysis, enrolment_by_school, ["school_id"], "enrolment") analysis = attach(analysis, feeding_by_school, ["school_id"], "feeding programme")Attachez une mesure à la fois, et affirmez à chaque raccord — En R
attach_measure <- function(base, addition, by, name) { before <- nrow(base) out <- dplyr::left_join(base, addition, by = by, relationship = "one-to-one") if (nrow(out) != before) { stop(sprintf("%s: row count changed %d -> %d", name, before, nrow(out))) } message(sprintf("%s: attached", name)) out } analysis <- analysis |> attach_measure(attendance_by_school_month, c("school_id", "month"), "attendance") |> attach_measure(enrolment_by_school, "school_id", "enrolment")Notes du présentateur
Deux propriétés qui valent d'être acquises. Le nombre de lignes ne peut pas changer, donc un gonflement est impossible par construction. Et le taux d'appariement est affiché à chaque attachement — une table d'inscriptions appariée à 100 % de l'ossature et une table de présence appariée à 92 % vous disent immédiatement où sont les trous, avant qu'aucun chiffre ne soit calculé.attach()a l'air d'un cérémonial jusqu'à la première fois où il se déclenche. Il se déclenchera.Portez la provenance — En Python
analysis["source_register"] = "school-attendance-2024.v1.csv" analysis["denominator_source"] = "roster 2024, enrolled students" analysis["marks_definition"] = "true/false only; Y/N excluded" analysis["extracted_on"] = "2026-07-27"Notes du présentateur
Ajoutez des colonnes disant d'où viennent les choses et sur quoi elles reposent.Portez la provenance — En R
analysis <- analysis |> mutate( source_register = "school-attendance-2024.v1.csv", denominator_source = "roster 2024, enrolled students", marks_definition = "true/false only; Y/N excluded", extracted_on = "2026-07-27" )Notes du présentateur
Cela paraît redondant tant que chaque ligne porte la même valeur. Cela cesse de l'être dès que deux extractions sont concaténées, qu'un deuxième district arrive, ou que quelqu'un ouvre le CSV un an plus tard sans savoir de quelle version du registre il provient. Les colonnes constantes sont bon marché ; les nombres sans étiquette ne le sont pas.Affirmez ce qui doit être vrai de la table finie — En Python
assert analysis.duplicated(subset=["school_id", "month"]).sum() == 0 assert analysis["attendance_rate"].between(0, 1).all() assert (analysis["days_present"] <= analysis["days_marked"]).all() assert len(analysis) == 24 * 3 print(f"analysis table: {len(analysis)} rows, " f"{analysis['attendance_rate'].isna().sum()} without an attendance rate")Affirmez ce qui doit être vrai de la table finie — En R
stopifnot( !any(duplicated(analysis[c("school_id", "month")])), all(dplyr::between(analysis$attendance_rate, 0, 1), na.rm = TRUE), all(analysis$days_present <= analysis$days_marked, na.rm = TRUE), nrow(analysis) == 24 * 3 )Notes du présentateur
Quatre assertions, correspondant chacune à une leçon de ce cours — la granularité est unique, le taux est un taux, le numérateur ne peut pas dépasser son dénominateur, et l'ossature est complète. Exécutez-les à la fin de l'assemblage, à chaque fois.Ce qui n'a pas sa place dans la table
- Les colonnes intermédiaires.
present_x,_merge,key_cleanet les six colonnes nécessaires à un seul calcul.… - Les lignes sous votre seuil de rapportage. Si vous supprimeriez une cellule de quatre cas dans un tableau publié,…
- Tout ce qui est au niveau de la personne. Une table d'analyse à la granularité école-mois n'a rien à faire d'un…
- Les colonnes intermédiaires.
Enregistrez-la avec sa granularité dans le nom — En Python
analysis.to_csv("outputs/tables/attendance_by_school_month.csv", index=False)Enregistrez-la avec sa granularité dans le nom — En R
readr::write_csv(analysis, here::here("outputs", "tables", "attendance_by_school_month.csv"))Enregistrez-la avec sa granularité dans le nom — Exemple
scripts/ 01_read_and_validate.R contract and validation (cleaning course) 02_clean.R rules and cleaning log (cleaning course) 03_assemble.R spine, joins, assertions (this course) 04_report.R tables and figuresNotes du présentateur
Et enregistrez le script d'assemblage à côté, car la table est le produit et le script est la méthode. Si régénérer la table demande plus d'une commande, la régénérer dans trois mois n'aura pas lieu. Quatre scripts, exécutés dans l'ordre, des fichiers bruts à la table finale. C'est l'organisation que Chaînes d'analyse reproductibles développera plus loin dans le programme ; l'adopter dès maintenant ne coûte rien et évite la reconstitution.Ce que ce cours vous laisse
- Vous savez prendre plusieurs fichiers décrivant le même programme et produire une table unique que vous défendriez colonne par colonne — jointures prouvées, granularité énoncée, dénominateur sourcé, calendrier complet, et l'écart avec ce qui a été remonté consigné plutôt que discuté.
Notes du présentateur
Vous savez prendre plusieurs fichiers décrivant le même programme et produire une table unique que vous défendriez colonne par colonne — jointures prouvées, granularité énoncée, dénominateur sourcé, calendrier complet, et l'écart avec ce qui a été remonté consigné plutôt que discuté. Le dernier cours de ce module, Évaluation de la qualité des données, prend ce dernier point et en fait une routine — facteurs de vérification sur un échantillon de formations sanitaires, les cinq dimensions de la qualité, et un rapport qui nomme l'action corrective, le responsable et l'échéance, ce qui transforme un écart trouvé en un écart qui se referme.