---
title: "Promptitude au regard du standard clinique"
subtitle: "Référencements de protection, 2024"
author: "Cassion · data-analysis.cassion.dev"
format:
  html:
    toc: true
    code-fold: false
jupyter: python3
---

## La question, et pourquoi le chronomètre compte

Pour une survivante de VBG cherchant des soins de santé, 72 heures n'est pas une
cible administrative. La prophylaxie post-exposition au VIH doit débuter dans les
72 heures pour être efficace, et la contraception d'urgence obéit à une fenêtre
comparable. Un référencement qui aboutit au quatrième jour est un événement
différent d'un référencement abouti au deuxième, et un délai moyen le dissimule
entièrement.

Ce jeu de données est synthétique. Aucune personne réelle n'y est décrite, et il
ne doit jamais servir de modèle pour stocker de vraies données de cas.

## Mise en place

```{python}
import pandas as pd
import numpy as np

URL = (
    "https://data-analysis.cassion.dev/datasets/files/"
    "protection-referrals-2024.v1.csv"
)

cas = pd.read_csv(URL, dtype={"case_id": "string"})
print(cas.shape)
```

## Construire le dénominateur délibérément

La promptitude se mesure sur les référencements **acceptés** : un référencement
qui n'a jamais atteint un service n'a pas de date de service et relève de
l'indicateur d'aboutissement, non de celui-ci. On restreint ensuite à la
population à laquelle le standard s'applique.

```{python}
acceptes = cas[cas["referral_accepted"]].copy()

vbg_sante = acceptes[
    (acceptes["case_category"] == "gbv")
    & (acceptes["service_requested"] == "health")
].copy()

print(f"tous les cas                        : {len(cas)}")
print(f"referencements acceptes             : {len(acceptes)}")
print(f"referencements VBG-sante acceptes   : {len(vbg_sante)}")
print(f"  avec un delai de service consigne : {int(vbg_sante['days_to_first_service'].notna().sum())}")
```

Chaque restriction est une décision, et chacune doit être défendable. Le standard
des 72 heures est clinique et s'applique aux soins de santé après violence
sexuelle — l'appliquer à un référencement d'aide juridique reviendrait à inventer
une cible.

## Les dates manquantes constituent l'analyse

Quarante référencements acceptés, sur l'ensemble du jeu de données, n'ont aucun
délai de service consigné. **Le dénominateur de la promptitude est donc plus
petit que celui de l'aboutissement**, et le traitement retenu pour ces quarante
change la réponse.

```{python}
manquants = vbg_sante["days_to_first_service"].isna()
print(f"referencements VBG-sante acceptes sans delai : {int(manquants.sum())}")
```

Trois traitements défendables, trois chiffres différents :

```{python}
FENETRE_JOURS = 3  # 72 heures

dans_fenetre = vbg_sante["days_to_first_service"] <= FENETRE_JOURS
consigne = vbg_sante["days_to_first_service"].notna()

traitements = pd.DataFrame([
    {
        "traitement": "exclure les manquants (consignes seuls)",
        "denominateur": int(consigne.sum()),
        "sous 72h": int((dans_fenetre & consigne).sum()),
    },
    {
        "traitement": "compter les manquants hors fenetre",
        "denominateur": len(vbg_sante),
        "sous 72h": int((dans_fenetre & consigne).sum()),
    },
    {
        "traitement": "compter les manquants dans la fenetre",
        "denominateur": len(vbg_sante),
        "sous 72h": int((dans_fenetre & consigne).sum() + manquants.sum()),
    },
])
traitements["taux"] = (traitements["sous 72h"] / traitements["denominateur"]).round(3)
traitements
```

**Lequel retenir ?** Le premier est l'option honnête par défaut : il rapporte ce
qui est connu et énonce son dénominateur. Le deuxième est l'option conservatrice,
défendable si vous avez des raisons de penser qu'une date manquante signifie un
service jamais délivré. Le troisième ne l'est pas : il suppose le meilleur cas
pour les dossiers que vous connaissez le moins bien.

Rapportez le premier, et indiquez dans la même phrase le nombre
d'enregistrements qu'il a exclus. Un chiffre de promptitude sans son dénominateur
n'est pas une mesure.

## La distribution, non la moyenne

```{python}
delais_consignes = vbg_sante.loc[consigne, "days_to_first_service"]

print(f"moyenne : {delais_consignes.mean():.1f} jours")
print(f"mediane : {delais_consignes.median():.0f} jours")

delais_consignes.value_counts().sort_index().head(12)
```

La moyenne ne sert quasiment à rien ici. Le standard est un seuil : ce qui compte
est la part du bon côté de ce seuil et la distance à laquelle le reste s'en
écarte. Un programme dont la moyenne est de quatre jours peut avoir tous ses cas
à quatre jours, ou la moitié à un jour et la moitié à sept — et un seul de ces
deux profils constitue une défaillance de service.

```{python}
bandes = pd.cut(
    delais_consignes,
    bins=[-1, 3, 7, 14, np.inf],
    labels=["sous 72h", "4-7 jours", "8-14 jours", "plus de 14 jours"],
)
(bandes.value_counts(normalize=True).sort_index() * 100).round(1)
```

## Où se situe le délai

```{python}
def promptitude(df, par, fenetre=FENETRE_JOURS):
    connus = df[df["days_to_first_service"].notna()]
    out = connus.groupby(par).agg(
        referencements=("days_to_first_service", "size"),
        dans_fenetre=("days_to_first_service", lambda s: (s <= fenetre).sum()),
        mediane_jours=("days_to_first_service", "median"),
    )
    out["sous 72h"] = (out["dans_fenetre"] / out["referencements"]).round(3)
    return out.sort_values("sous 72h")

promptitude(vbg_sante, "admin2")
```

Petits dénominateurs à nouveau — les zones apportent entre six et vingt-sept
référencements chacune, si bien que l'ordre entre elles est fragile et que la
plus petite n'est pas interprétable du tout. Rapportez les zones, rapportez les
effectifs, et renoncez à les classer.

```{python}
promptitude(acceptes[acceptes["service_requested"] == "health"], "case_category")
```

## Ce que cela ne peut pas vous dire

Le chronomètre démarre ici au référencement et s'arrête au premier service. Il ne
démarre pas à l'incident, car ce jeu de données ne porte délibérément aucune date
d'incident — et selon les principes de gestion de l'information VBG, il ne le
devrait pas.

Cela signifie qu'**une survivante ayant atteint un travailleur social au
cinquième jour et un service au sixième apparaît ici comme un référencement d'un
jour**, largement dans la fenêtre, alors que la fenêtre clinique était déjà
close. L'indicateur mesure le circuit de référencement, non le délai total
d'accès aux soins, et un rapport qui confond les deux surestime ce que le
programme a accompli.

Énoncez-le dans les limites. C'est la différence entre un indicateur de circuit
honnête et une affirmation sur des résultats cliniques que les données ne
peuvent pas soutenir.

## Ce qu'il faut rapporter

La part sous 72 heures, son dénominateur, le nombre de référencements acceptés
exclus faute de date de service, la distribution plutôt que la moyenne, et
l'énoncé explicite que le chronomètre démarre au référencement et non à
l'incident.
