---
title: "Aboutissement des référencements et points de rupture du circuit"
subtitle: "Référencements de protection, 2024 · R"
author: "Cassion · data-analysis.cassion.dev"
format:
  html:
    toc: true
    code-fold: false
---

## Avant tout code

Données synthétiques modélisant des cas de protection et de VBG. Aucune personne
réelle n'y est décrite, et ce fichier ne doit jamais servir de modèle pour
stocker de vraies données de cas — la version sûre est un système de gestion de
cas encadré par le consentement.

La même décomposition que l'exemple Python, en dplyr, avec les enregistrements
contradictoires et manquants traités explicitement plutôt que supprimés en
silence.

## Mise en place

```{r}
#| message: false
library(readr)
library(dplyr)
library(tidyr)

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

cas <- read_csv(URL, col_types = cols(
  case_id  = col_character(),
  .default = col_guess()
))

glimpse(cas)
```

## Le consentement conditionne le dénominateur

```{r}
cas |>
  summarise(
    cas             = n(),
    consentants     = sum(consent_to_refer),
    taux_consentement = round(mean(consent_to_refer), 3)
  )
```

```{r}
consentants <- cas |> filter(consent_to_refer)

consentants |>
  summarise(
    denominateur = n(),
    aboutis      = sum(referral_accepted),
    taux         = round(mean(referral_accepted), 3)
  )
```

Le dénominateur de l'aboutissement, ce sont les cas ayant consenti, non
l'ensemble des cas. Compter un cas non consentant comme un échec du circuit
déforme à la fois la performance et la décision d'une personne : le circuit a
fait ce qu'il devait lorsque quelqu'un a refusé.

## Traiter les contradictions explicitement

```{r}
cas |>
  summarise(
    delai_sans_acceptation = sum(!referral_accepted & !is.na(days_to_first_service)),
    delai_sans_referencement = sum(!referral_made & !is.na(days_to_first_service)),
    delai_sans_consentement  = sum(!consent_to_refer & !is.na(days_to_first_service)),
    accepte_sans_delai       = sum(referral_accepted & is.na(days_to_first_service))
  ) |>
  pivot_longer(everything(), names_to = "contradiction", values_to = "cas")
```

Onze enregistrements portent un délai de service sans référencement accepté
derrière, dont six sans aucun référencement effectué. Quarante référencements
acceptés n'ont pas de délai consigné, ce qui signifie que **le dénominateur de la
promptitude est plus petit que celui de l'aboutissement** — employer l'un pour
l'autre fausse les deux.

```{r}
cas <- cas |>
  mutate(contradictoire = !referral_accepted & !is.na(days_to_first_service))

cat("signales, non supprimes :", sum(cas$contradictoire), "\n")
```

Signaler plutôt que supprimer compte ici. En gestion de cas, un enregistrement
contradictoire est un problème de saisie à renvoyer au travailleur social, et le
supprimer détruit la seule trace de l'existence du cas.

## Normaliser le handicap avant de désagréger

```{r}
count(cas, disability_reported)
```

```{r}
cas <- cas |>
  mutate(
    handicap = case_when(
      tolower(trimws(disability_reported)) %in% c("true", "yes") ~ TRUE,
      tolower(trimws(disability_reported)) %in% c("false", "no")  ~ FALSE,
      TRUE ~ NA
    )
  )

consentants <- cas |> filter(consent_to_refer)
count(consentants, handicap)
```

Une zone a employé `Yes` et `No`. Laissée telle quelle, la désagrégation se
fragmente en quatre catégories, dont deux issues de cette seule zone et trop
petites pour être interprétées.

## Où le circuit se rompt

```{r}
aboutissement <- function(df, par) {
  df |>
    group_by(across(all_of(par))) |>
    summarise(
      cas     = n(),
      aboutis = sum(referral_accepted),
      taux    = round(mean(referral_accepted), 3),
      .groups = "drop"
    ) |>
    arrange(taux)
}

aboutissement(consentants, "service_requested")
```

```{r}
aboutissement(consentants, "admin2")
```

Le soutien aux moyens d'existence aboutit à environ 23 % contre environ 62 % pour
la santé. Ce n'est pas de la performance de travailleur social : c'est la
question des services qui existent et disposent de capacités. Une analyse qui
s'arrête au 46 % global escamote tout le constat.

```{r}
grille <- consentants |>
  group_by(admin2, service_requested) |>
  summarise(cas = n(), taux = round(mean(referral_accepted), 2), .groups = "drop") |>
  mutate(taux = if_else(cas >= 20, taux, NA_real_)) |>
  select(-cas) |>
  pivot_wider(names_from = service_requested, values_from = taux)

grille
```

Les cellules sous vingt cas sont laissées vides. Un taux sur huit cas n'est pas
un constat, et en protection une petite cellule constitue un risque de
divulgation autant qu'un problème statistique.

## Le constat d'équité

```{r}
par_handicap <- consentants |>
  filter(!is.na(handicap)) |>
  aboutissement("handicap")

par_handicap
```

```{r}
tbl <- table(
  consentants$handicap[!is.na(consentants$handicap)],
  consentants$referral_accepted[!is.na(consentants$handicap)]
)
chisq.test(tbl)
```

Les cas où un handicap est signalé aboutissent environ dix-sept points plus bas,
et l'écart est peu susceptible d'être dû au hasard. Ce qu'il ne dit pas, c'est
*pourquoi* — si les services sont physiquement inaccessibles, si les circuits
supposent une mobilité que certains clients n'ont pas, ou tout autre chose. À
cette question répondent les travailleurs sociaux, non ce tableau.

## Les petites cellules sont un risque de protection

```{r}
consentants |>
  count(admin2, case_category) |>
  filter(n < 20)
```

Ce contrôle ne renvoie rien ici — toutes les cellules zone par catégorie
dépassent vingt — et ce résultat vide est précisément l'intérêt de l'exécuter.
Une cellule de trois cas de VBG dans un tableau par district peut identifier une
survivante pour quiconque connaît la zone : le contrôle reste donc en permanence
dans la chaîne plutôt que d'être lancé une fois. Fixez le seuil avec l'agence de
gestion de cas, non comme un choix de mise en forme.

## Ce qu'il faut rapporter

L'aboutissement sur le dénominateur conditionné par le consentement, décomposé
assez finement pour localiser le point de rupture, les contradictions signalées et
comptées, l'écart lié au handicap énoncé clairement — et rien à une granularité
susceptible d'identifier une personne.
