Leçon 5 sur 8
Unité · La cohorte
Trois issues qui doivent sommer à un
70,4 % de passages, 9,8 % de redoublements, 10,2 % d'abandons, 5,7 % d'achèvements et 2,9 % de transferts. Chacune exige le même dénominateur, et les transferts sont la raison pour laquelle ce n'est pas l'effectif inscrit.
L’année, comme un ensemble de sorties
import pandas as pd
enrolment = pd.read_csv("school-enrolment-2024.v1.csv")
clean = enrolment[enrolment["age_years"] <= 20]
cohort = clean[clean["school_year"] == 2023]
outcomes = cohort["end_of_year_status"].value_counts(normalize=True)
print((outcomes * 100).round(1))
print(f"\ncohort: {len(cohort)} students")
library(dplyr)
enrolment |>
filter(age_years <= 20, school_year == 2023) |>
count(end_of_year_status) |>
mutate(share = n / sum(n))
| Issue | Élèves | Part |
|---|---|---|
| Passés | 910 | 70,4 % |
| Abandonné | 132 | 10,2 % |
| Redoublé | 127 | 9,8 % |
| Achevé la dernière année | 74 | 5,7 % |
| Transféré | 37 | 2,9 % |
| Encore inscrits à la clôture | 12 | 0,9 % |
Les trois taux principaux sont le passage, le redoublement et l’abandon, et ils ne signifient quelque chose que s’ils partagent un dénominateur. Cela paraît évident et c’est l’étape la plus souvent sautée.
Le dénominateur n’est pas l’effectif inscrit
Deux catégories le compliquent, et dans des sens opposés.
Les transferts ne sont pas des abandons. Un enfant parti dans une autre école est toujours scolarisé. Les compter comme abandons surestime l’abandon de près de trois points et — pire — attribue une réussite du système à un échec d’école.
Les transferts ne sont pas non plus des réussites. Ils ne peuvent pas être comptés comme passages, car ce registre ne sait pas s’ils se sont réinscrits.
def rates(frame, exclude_transfers):
base = frame[frame["end_of_year_status"] != "transferred-out"] \
if exclude_transfers else frame
return {
"n": len(base),
"promotion": (base["end_of_year_status"]
.isin(["promoted", "completed-final-grade"]).mean()),
"repetition": base["end_of_year_status"].eq("repeated").mean(),
"dropout": base["end_of_year_status"].eq("dropped-out").mean(),
}
for exclude in (False, True):
result = rates(cohort, exclude)
label = "excluding transfers" if exclude else "all students"
print(f"{label:20} n={result['n']} "
f"promotion {result['promotion']:.1%} "
f"repetition {result['repetition']:.1%} "
f"dropout {result['dropout']:.1%}")
cohort |>
filter(end_of_year_status != "transferred-out") |>
summarise(n = n(),
promotion = mean(end_of_year_status %in% c("promoted", "completed-final-grade")),
repetition = mean(end_of_year_status == "repeated"),
dropout = mean(end_of_year_status == "dropped-out"))
C’est le dénominateur du taux de guérison PCIMA rencontré plus tôt dans ce module, dans un autre secteur. La convention y était d’exclure les transferts parce que le programme ne peut pas revendiquer leur issue ; la convention est ici la même, pour la même raison. Dites laquelle vous avez employée, et les trois taux sommeront à quelque chose de défendable.
Le redoublement est une décision, non un événement
by_district = clean.groupby("admin2")["repeating"].agg(["mean", "size"])
print((by_district * [100, 1]).round(1))
enrolment |> filter(age_years <= 20) |>
summarise(repeating = mean(repeating), n = n(), .by = admin2)
| District | Taux de redoublement | n |
|---|---|---|
| Sud | 12,3 % | 583 |
| Artibonite | 12,1 % | 618 |
| Nord-Ouest | 12,0 % | 625 |
| Centre | 7,4 % | 624 |
Centre rapporte un redoublement inférieur de près de cinq points à tous les autres districts. C’est soit le district le plus performant de la région, soit une habitude d’enregistrement, et le registre seul ne peut pas trancher.
C’est une habitude d’enregistrement : Centre signale une partie de ses vrais redoublants comme nouveaux entrants. La façon de le soupçonner sans qu’on vous le dise est que le redoublement devrait suivre le retard d’âge, et à Centre il ne le suit pas — 8,7 % de redoublement contre 11,8 % ailleurs, alors que la part de retard d’âge de Centre vaut 40,6 % contre 35,7 %. Le district qui rapporte le moins de redoublement a le plus d’enfants en retard d’âge, ce qui est à l’envers pour toute explication autre que le codage.
centre = clean[(clean["admin2"] == "Centre") & (clean["school_year"] == 2023)]
elsewhere = clean[(clean["admin2"] != "Centre") & (clean["school_year"] == 2023)]
for name, frame in [("Centre", centre), ("elsewhere", elsewhere)]:
over = frame["age_years"] > frame["grade"] + 5
print(f"{name}: repetition {frame['repeating'].mean():.1%}, "
f"over-age {over.mean():.1%}")
# Repetition should predict over-age. Where it does not, suspect the coding.
Un taux en décalage avec l’indicateur qu’il cause mécaniquement est une question sur le bureau avant d’être un constat sur les écoles. C’est le même raisonnement que le fourre-tout des motifs de clôture du cours de protection, et il mérite d’être un réflexe.
L’abandon par année, et le pic de sixième
by_grade = cohort[cohort["grade"].between(1, 6)].groupby("grade").agg(
n=("student_id", "size"),
dropout=("end_of_year_status", lambda s: s.eq("dropped-out").mean()),
repetition=("end_of_year_status", lambda s: s.eq("repeated").mean()),
)
print((by_grade * [1, 100, 100]).round(1))
cohort |> filter(between(grade, 1, 6)) |>
summarise(n = n(),
dropout = mean(end_of_year_status == "dropped-out"),
repetition = mean(end_of_year_status == "repeated"), .by = grade)
| Année | n | Abandon | Redoublement |
|---|---|---|---|
| 1 | 149 | 8,1 % | 2,0 % |
| 2 | 271 | 8,5 % | 7,0 % |
| 3 | 332 | 13,0 % | 6,6 % |
| 4 | 231 | 8,7 % | 10,4 % |
| 5 | 165 | 8,5 % | 19,4 % |
| 6 | 110 | 18,2 % | 12,7 % |
Deux choses de ce tableau demandent une lecture attentive.
Le redoublement monte avec l’année et l’abandon non. La cinquième retient 19,4 % de ses élèves alors que son abandon vaut 8,5 % ; la sixième fait l’inverse. Les deux sont des sorties alternatives d’un même point de décision, et lire l’une seule donne une forme fausse du problème.
L’abandon en sixième vaut 18,2 % sur 110 élèves. C’est le taux le plus élevé du tableau et le plus petit dénominateur, donc avant d’en faire la priorité, posez-lui un intervalle — la machinerie du cours sur les enquêtes, sur une proportion qui bougerait de plusieurs points si quatre enfants avaient fait autre chose.
Rapportez les sorties, puis les taux
End of school year 2023, 1,292 students
Promoted 70.4% 910
Dropped out 10.2% 132
Repeated 9.8% 127
Completed the final grade 5.7% 74
Transferred out 2.9% 37 excluded from the rates below
Still enrolled at cut-off 0.9% 12
Rates on 1,255 students, transfers excluded:
Promotion (incl. completion) 78.4%
Repetition 10.1%
Dropout 10.5%
Centre reports repetition at 7.4% against 12.0-12.3% elsewhere while
holding the highest over-age share of the four districts. Treat as a
recording difference pending verification, not as performance.
La suite
Ce sont les sorties d’une année. Les transformer en « combien d’enfants achèvent le primaire » exige une cohorte, et la leçon suivante en construit une — avec la raison pour laquelle le nombre qu’elle produit est presque certainement faux.