Leçon 7 sur 8
Unité · Ce que le rapport affirme
r = 0,039, et tout le monde attendait 0,5
La présence et la littératie de fin d'année corrèlent à 0,039 sur 659 élèves — un intervalle de −0,04 à +0,12, ce qui est aussi proche de rien qu'un jeu de données peut l'être. La corrélation que tout le monde suppose présente dans le registre n'y est pas, et le rapporter est le constat.
La corrélation qui n’y est pas
import pandas as pd
import numpy as np
assessment = pd.read_csv("learning-assessment-2024.v1.csv")
attendance = pd.read_csv("school-attendance-2024.v1.csv")
roster = pd.read_csv("school-roster-2024.v1.csv")
MARKS = {"true": True, "Y": True, "false": False, "N": False}
marked = attendance[attendance["present"].isin(MARKS)].copy()
marked["attended"] = marked["present"].map(MARKS)
rate = marked.groupby("student_id")["attended"].mean().rename("attendance")
endline = (assessment[assessment["assessment_round"] == "endline"]
.pivot_table(index="student_id", columns="domain", values="raw_score"))
joined = endline.join(rate, how="inner").join(roster.set_index("student_id"))
print(joined[["literacy", "numeracy", "attendance"]].corr().round(3))
library(dplyr)
joined |> select(literacy, numeracy, attendance) |> cor(use = "complete.obs")
| Paire | r | n |
|---|---|---|
| Présence et littératie de fin d’année | 0,039 | 659 |
| Présence et numératie de fin d’année | −0,006 | 659 |
| Littératie et numératie | 0,849 | 659 |
La présence et la littératie corrèlent à 0,039. Chaque document de programme éducatif suppose cette relation et la plupart en citent un chiffre. Dans ce registre elle est absente.
C’est un résultat, et il lui faut un intervalle avant d’en être un.
Un intervalle sur une corrélation
def fisher_ci(r, n, z=1.96):
zr = np.arctanh(r)
se = 1 / np.sqrt(n - 3)
return np.tanh(zr - z * se), np.tanh(zr + z * se)
for label, r, n in [("attendance vs literacy", 0.039, 659),
("literacy vs numeracy", 0.849, 659)]:
lo, hi = fisher_ci(r, n)
print(f"{label:24} r = {r:+.3f} 95% CI [{lo:+.3f}, {hi:+.3f}]")
cor.test(joined$attendance, joined$literacy)
r = 0,039, IC à 95 % −0,037 à +0,115. L’intervalle contient zéro et toute valeur qu’il contient est négligeable ; ce n’est donc pas un résultat nul par manque de puissance — 659 élèves suffisent à dire que si une relation existe, elle est trop petite pour compter.
Comparez le même intervalle sur la seconde paire : r = 0,849, IC 0,826 à 0,869, où l’intervalle entier est grand.
Une corrélation sans intervalle est le même défaut qu’une proportion sans le
sien, et cor.test le donne gratuitement.
La corrélation qui y est et ne dit rien
La littératie et la numératie corrèlent à 0,849, ce qui est le plus grand r de ce cours et le moins intéressant.
C’est un enfant, un jour d’épreuve, deux copies de quarante items. Un enfant malade, à jeun, incapable de lire les consignes, ou simplement bon élève, obtient des scores voisins aux deux. La corrélation est une propriété de la mesure, non une découverte sur la littératie et la numératie.
Demandez ce qu’il faudrait pour que la corrélation soit nulle. Ici la réponse est « le même enfant devrait réussir indépendamment deux copies passées à une demi-heure d’intervalle », ce que personne ne croit — donc un r élevé ne porte aucune information.
Une corrélation n’est informative que si son absence était plausible, et c’est un jugement sur le monde plutôt que sur les données.
Élevez-la au carré avant de la décrire
for label, r in [("attendance vs literacy", 0.039),
("attendance vs literacy, school level", 0.199),
("literacy vs numeracy", 0.849)]:
print(f"{label:38} r = {r:+.3f} r-squared = {r**2:.3f}")
# r^2 is the share of variance, and it is much smaller than r looks.
| Paire | r | r² | Se lit |
|---|---|---|---|
| Présence et littératie | 0,039 | 0,002 | Deux dixièmes de un pour cent |
| Présence et littératie, niveau école | 0,199 | 0,040 | Quatre pour cent |
| Littératie et numératie | 0,849 | 0,721 | Soixante-douze pour cent |
Un r de 0,199 sonne comme une relation et explique quatre pour cent de la variation. Élever au carré est la défense la moins chère contre la surlecture d’une corrélation moyenne, et le nombre à mettre dans un rapport est généralement r² plutôt que r.
Pearson, Spearman, et onze mauvaises lignes
La leçon 1 a trouvé onze ménages dont la consommation totale avait été saisie dans une colonne par personne. Regardez ce que ces onze lignes font à une corrélation.
wash = pd.read_csv("wash-household-survey-2024.v1.csv")
pair = wash.dropna(subset=["round_trip_minutes", "litres_per_person_day"])
clean = pair[pair["litres_per_person_day"] <= 80]
for label, frame in [("all rows", pair), ("11 rows removed", clean)]:
p = frame["round_trip_minutes"].corr(frame["litres_per_person_day"])
s = frame["round_trip_minutes"].corr(frame["litres_per_person_day"],
method="spearman")
print(f"{label:18} n = {len(frame):5} Pearson {p:+.3f} Spearman {s:+.3f}")
cor(pair$round_trip_minutes, pair$litres_per_person_day) # Pearson
cor(pair$round_trip_minutes, pair$litres_per_person_day, method = "spearman")
| Lignes | n | Pearson | Spearman |
|---|---|---|---|
| Toutes | 2 403 | −0,185 | −0,285 |
| Onze retirées | 2 392 | −0,293 | −0,287 |
Onze lignes sur 2 403 amputent Pearson de plus d’un tiers. Spearman n’a pas bougé.
Pearson emploie les valeurs, si bien qu’un ménage enregistré à 277 litres par personne tire fort sur le coefficient. Spearman emploie les rangs, si bien qu’une mauvaise valeur qui reste la plus grande ne change rien.
Rapportez Spearman quand l’une des deux variables est asymétrique ou porte des valeurs extrêmes que vous n’avez pas résolues, et Pearson quand les deux sont à peu près symétriques et propres. Un grand écart entre les deux est un signal pour aller regarder les extrêmes — le même usage qu’avait la statistique d’asymétrie à la leçon 1.
La réponse corrigée est celle qui sert : des trajets plus longs vont avec moins d’eau consommée, r = −0,29 sur 2 392 ménages, ce qui est la relation que le cours EAH prédisait et que le fichier non corrigé sous-estimait.
Corrélés parce qu’autre chose a bougé les deux
prices = pd.read_csv("market-prices-2024.v1.csv")
wide = prices.pivot_table(index="period", columns="commodity",
values="price_htg", aggfunc="mean").sort_index()
print(f"beans and maize, levels: {wide['beans-black'].corr(wide['maize']):.3f}")
changes = wide.diff().dropna()
print(f"beans and maize, month-on-month: {changes['beans-black'].corr(changes['maize']):.3f}")
cor(wide$`beans-black`, wide$maize)
cor(diff(wide$`beans-black`), diff(wide$maize))
Le haricot et le maïs corrèlent à 0,986 en niveaux et à 0,876 en variations mensuelles. Aucun des deux nombres ne signifie que le prix du haricot déplace celui du maïs. Les deux séries portent la même inflation annuelle et le même choc de soudure, et la corrélation mesure le choc.
Corréler deux séries dans le temps donnera presque toujours un grand nombre, parce que presque tout suit une tendance. Différencier retire la tendance partagée et ce qui survit est le choc partagé — qui est une cause commune, et toujours pas un lien causal entre les deux produits.
Le cours d’épidémiologie faisait ce point avec la confusion. Le coefficient de corrélation n’a aucun moyen de l’exprimer, et c’est pourquoi la phrase en dessous doit le faire.
Et le regroupement de la leçon précédente
by_school = joined.groupby("school_id")[["attendance", "literacy"]].mean()
print(f"student level r = {joined['attendance'].corr(joined['literacy']):.3f} n = {len(joined)}")
print(f"school level r = {by_school['attendance'].corr(by_school['literacy']):.3f} n = {len(by_school)}")
# Same two variables, two units of analysis, two answers.
0,039 sur 659 élèves et 0,199 sur 24 écoles. Le chiffre au niveau école a un intervalle de −0,22 à +0,56 sur 24 points, donc il n’établit rien non plus — mais il est cinq fois plus grand, et un analyste qui aurait agrégé d’abord et rapporté r sans n aurait écrit une autre phrase.
Dites entre quelles unités porte la corrélation, à chaque fois. « La présence corrèle avec la littératie à 0,20 » est illisible sans savoir si les lignes sont des enfants ou des écoles.
Écrivez la phrase
Le coefficient est un nombre et la phrase en dessous est l’endroit où l’analyse est honnête ou ne l’est pas.
Pas ceci : « La présence est corrélée aux résultats de littératie. » Vrai au niveau école, faux au niveau élève, et infalsifiable tel quel.
Ni ceci : « Il n’y a aucune relation entre présence et littératie. » Plus fort que ce que portent les données ; l’intervalle atteint +0,12.
Ceci : « Sur 659 élèves, la présence du trimestre février-avril ne montre aucune association avec la littératie de fin d’année (r = 0,04, IC à 95 % −0,04 à +0,12). Le registre ne peut pas soutenir l’hypothèse que la présence porte l’apprentissage dans cette cohorte. Notez que la présence varie peu — la moitié centrale des élèves se situe entre 86,7 % et 97,8 % — de sorte que cette analyse détecte mal une relation aux faibles présences, là où on l’attendrait. »
La troisième phrase est celle à copier. Elle énonce le constat, l’intervalle, ce qu’il signifie pour le programme, et la raison pour laquelle l’analyse pourrait avoir manqué quelque chose de réel — et elle tient en quatre lignes.
Rapportez-le en entier
Attendance and learning outcomes, endline 2024
Attendance vs endline literacy r = 0.04 95% CI -0.04 to +0.12 n = 659
Attendance vs endline numeracy r = -0.01 95% CI -0.08 to +0.07 n = 659
Between students within the term. No association at student level.
Aggregated to the 24 schools, r = 0.20 (95% CI -0.22 to +0.56), which is
also consistent with no relationship.
Attendance is compressed: the interquartile range is 86.7% to 97.8%, so
students with the low attendance that a learning effect would show up at
are rare in this cohort. This is a null result about the range observed,
not about attendance in general.
Le dernier paragraphe est ce qui empêche de surlire le résultat nul, et c’est le pendant de la phrase « sous-puissant, non nul » de la leçon 4. Une corrélation nulle sur une plage étroite ne dit rien de ce qui se passe en dehors.
La suite
La dernière leçon assemble tout cela en une section statistique de rapport — ce qui y entre, ce qui reste en annexe, et ce qu’un relecteur demandera et que ce cours a déjà calculé.