Retour à la leçon·Leçon 7 sur 8·La table que vous analysez
Quand le registre et le rapport divergent
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 chiffres pour la même chose
- Recalculer depuis le registre
- La décision de codage qui déplace un dénominateur
- Mettez les deux sources dans une seule table
- Décomposez, ne moyennez pas
- Pourquoi deux sources divergent
- Rapportez l'écart, ne le faites pas disparaître
- La suite
Notes du présentateur
Recalculer l'indicateur depuis le registre ligne à ligne, le poser à côté de ce qui a été remonté, et décomposer l'écart par unité de rapportage au lieu de le noyer dans une moyenne.Deux chiffres pour la même chose
- Quelque part se trouve un registre ligne à ligne — marques de présence journalières, cahier de dépistage, registre de patients.
Notes du présentateur
Quelque part se trouve un registre ligne à ligne — marques de présence journalières, cahier de dépistage, registre de patients. Ailleurs se trouve un chiffre mensuel qui en a été remonté. Les deux divergent, et cet écart est le nombre le plus instructif de ce cours. Il est instructif parce qu'il est borné. Contrairement à la plupart des questions de qualité des données, celle-ci a une bonne réponse rangée dans un tiroir : le registre est la source, le rapport est une affirmation sur le registre, et la différence entre les deux est une quantité mesurable et non une opinion. Les audits bailleurs appellent leur rapport le facteur de vérification — recompter les documents sources, diviser le recomptage par ce qui a été rapporté. Un facteur de vérification de 1,0 signifie que le rapportage est exact. Toute autre valeur est un nombre qui a une cause.Recalculer depuis le registre — En Python
attendance["marked"] = attendance["present"].isin(["true", "false"]) attendance["is_present"] = attendance["present"] == "true" from_register = ( attendance.merge(roster[["student_id", "school_id"]], on="student_id", how="left", validate="many_to_one") .groupby("school_id") .agg(marks=("marked", "sum"), present=("is_present", "sum")) ) from_register["rate"] = from_register["present"] / from_register["marks"]Notes du présentateur
Agrégez le registre à la granularité de rapportage, exactement comme dans la leçon précédente.Recalculer depuis le registre — En R
from_register <- attendance |> left_join(select(roster, student_id, school_id), by = "student_id", relationship = "many-to-one") |> summarise( marks = sum(present %in% c("true", "false")), present = sum(present == "true", na.rm = TRUE), .by = school_id ) |> mutate(rate = present / marks)Notes du présentateur
Notez ce que comptemarked. Une marque existe là où la valeur vauttrueoufalse. Les valeursY,Net vide ne sont pas des marques selon cette définition — et cette définition est une décision, ce qui fait toute la section suivante.La décision de codage qui déplace un dénominateur — En Python
mapping = {"true": True, "false": False, "Y": True, "N": False} attendance["is_present_mapped"] = attendance["present"].map(mapping) both_ways = ( attendance.merge(roster[["student_id", "school_id"]], on="student_id") .groupby("school_id") .agg( marks_strict=("is_present", lambda s: s.notna().sum()), rate_strict=("is_present", "mean"), marks_mapped=("is_present_mapped", lambda s: s.notna().sum()), rate_mapped=("is_present_mapped", "mean"), ) ) print(both_ways.loc["SCH09"])Notes du présentateur
Une école a enregistré la présence avecYetNau lieu detrueetfalse. Recalculez des deux façons.La décision de codage qui déplace un dénominateur — En R
both_ways <- attendance |> left_join(select(roster, student_id, school_id), by = "student_id") |> mutate(mapped = dplyr::recode(present, "Y" = "true", "N" = "false")) |> summarise( marks_strict = sum(present %in% c("true", "false")), rate_strict = mean(present[present %in% c("true", "false")] == "true"), marks_mapped = sum(mapped %in% c("true", "false")), rate_mapped = mean(mapped[mapped %in% c("true", "false")] == "true"), .by = school_id )La décision de codage qui déplace un dénominateur
SCH09 Strict Y/N recodés Marques au dénominateur 2 015 2 865 Taux de présence 86,05 % 85,72 % Notes du présentateur
Lisez les deux colonnes ensemble, car la leçon est dans le contraste. Le dénominateur croît de 42 %. Le taux bouge d'un tiers de point. C'est la forme la plus courante de ce problème et la raison pour laquelle il franchit si aisément les relectures. Quiconque contrôle le taux conclut que le codage n'avait pas d'importance. Quiconque rapporte ensuite « nombre de journées de présence enregistrées » se trompe de 850. Un défaut qui touche à peine un rapport peut être énorme sur un effectif, et le rapportage de programme est plein d'effectifs. Sur l'ensemble du fichier, la même opération fait passer le taux du district de 88,47 % à 88,42 % et le dénominateur de 69 101 à 69 974.Mettez les deux sources dans une seule table — En Python
comparison = ( from_register.rename(columns={"present": "register_present"}) .join(reported.rename(columns={"present": "reported_present"}), how="outer") ) comparison["difference"] = comparison["register_present"] - comparison["reported_present"] comparison["verification_factor"] = ( comparison["register_present"] / comparison["reported_present"] ) print(comparison.sort_values("difference").head(10))Notes du présentateur
Ne comparez jamais deux chiffres en regardant deux affichages. Joignez-les, avec l'unité de rapportage et la période pour clé, et calculez l'écart comme une colonne.Mettez les deux sources dans une seule table — En R
comparison <- from_register |> full_join(reported, by = c("school_id", "month"), suffix = c("_register", "_reported")) |> mutate( difference = present_register - present_reported, verification_factor = present_register / present_reported ) |> arrange(difference)Notes du présentateur
Une jointure complète, délibérément. Une unité présente dans le registre et absente du rapport a cessé de rapporter ; une unité présente dans le rapport sans rien dans le registre remonte des chiffres que personne ne peut retrouver. Les deux sont des constats et une jointure interne supprime les deux.Décomposez, ne moyennez pas — En Python
print(comparison["verification_factor"].describe()) print(comparison[comparison["verification_factor"].sub(1).abs() > 0.05])Notes du présentateur
L'erreur la plus fréquente ici est de calculer un facteur de vérification unique pour tout le district. Un facteur de 1,02 pour le district peut être vingt écoles à 1,00 et une à 1,40, et la moyenne a masqué la seule chose sur laquelle agir.Décomposez, ne moyennez pas — En R
comparison |> summarise(across(verification_factor, list(min = min, median = median, max = max))) comparison |> filter(abs(verification_factor - 1) > 0.05)Décomposez, ne moyennez pas
- Rapportez la distribution et les valeurs extrêmes, jamais la moyenne seule — Une bande de tolérance — tout ce qui sort…
Notes du présentateur
Rapportez la distribution et les valeurs extrêmes, jamais la moyenne seule. Une bande de tolérance — tout ce qui sort de 0,95 à 1,05 est examiné — transforme ceci d'un nombre en une liste de travail, ce dont un superviseur peut effectivement se servir.Pourquoi deux sources divergent
- Des définitions différentes. Le registre compte des marques ; le rapport compte des enfants inscrits. De loin la…
- Des périodes différentes. Le rapport couvre un mois calendaire, le registre a été extrait le 28. Vérifiez les…
- Des couvertures différentes. Le rapport inclut une école que l'extraction du registre excluait, ou l'inverse.…
- La transcription. Quelqu'un a additionné la colonne à la main. C'est la cause que tout le monde suppose et la moins…
Notes du présentateur
Quatre causes, appelant des réponses totalement différentes. Parcourez-les dans cet ordre. Seule la dernière est une erreur au sens ordinaire. Les trois premières sont des problèmes de rapprochement, et les rapporter comme des « problèmes de qualité des données » fait porter à un superviseur la responsabilité de ce qui a été décidé dans une réunion de conception de formulaire.Rapportez l'écart, ne le faites pas disparaître — En Python
summary = pd.DataFrame({ "source": ["Line-level register", "Reported monthly returns"], "attendance days recorded": [69101, 68240], "attendance rate": ["88.5%", "89.1%"], })Notes du présentateur
La tentation est d'ajuster le chiffre du registre jusqu'à ce qu'il corresponde au rapport, ou de ne publier que celui qui a la meilleure allure. Publiez les deux, avec l'écart.Rapportez l'écart, ne le faites pas disparaître — En R
summary <- tibble::tibble( source = c("Line-level register", "Reported monthly returns"), days = c(69101, 68240), rate = c("88.5%", "89.1%") )Rapportez l'écart, ne le faites pas disparaître
Un écart que vous rapportez est un constat. Un écart que vous refermez en silence est un chiffre que personne ne peut reproduire, vous compris, dans six mois.
Notes du présentateur
Deux lignes et une phrase nommant le principal contributeur à l'écart. C'est le matériau d'une évaluation de la qualité des données, et le cours Évaluation de la qualité des données — le suivant et le dernier de ce module — en fait une routine avec une stratégie d'échantillonnage et un plan d'action corrective.La suite
- Toutes les pièces sont en place — des jointures démontrables, une granularité énoncée, la bonne forme, un dénominateur, un calendrier complet et une source rapprochée.
Notes du présentateur
Toutes les pièces sont en place — des jointures démontrables, une granularité énoncée, la bonne forme, un dénominateur, un calendrier complet et une source rapprochée. La dernière leçon les assemble en une table d'analyse unique : une ligne par unité d'analyse, chaque colonne traçable jusqu'à sa provenance, construite par un script qui va des fichiers bruts à la table finale en une seule commande.