---
title: "Interactions et prédictions : quand une association dépend d'une autre variable"
---
```{r}
#| label: setup-analyse
#| include: false
source("R/04-initialiser-analyse.R")
```
::: {.callout-important title="Question de départ"}
Le gradient scolaire de pratique sportive est-il identique à tous les âges, ou l'association entre diplôme et sport varie-t-elle selon le cycle de vie ?
:::
## Objectifs d'apprentissage
À la fin de ce chapitre, vous saurez :
- expliquer ce qu'est une interaction et ce qu'elle n'est pas ;
- distinguer modèle additif et modèle avec interaction ;
- comparer les deux modèles sur un échantillon commun ;
- comprendre pourquoi les coefficients principaux deviennent conditionnels ;
- tester un bloc d'interactions plutôt que sélectionner quelques coefficients ;
- interpréter une interaction logistique avec des probabilités et **contrastes de probabilités** ;
- vérifier les tailles de cellules qui soutiennent l'interaction ;
- distinguer modification d'association, cycle de vie et trajectoire longitudinale.
## Une interaction répond à une hypothèse substantive
Un modèle additif suppose que l'association de $X$ avec le résultat ne dépend pas de $Z$ sur l'échelle du prédicteur linéaire :
$$
g(E[Y]) = \beta_0 + \beta_1X + \beta_2Z.
$$
Avec une interaction :
$$
g(E[Y]) = \beta_0 + \beta_1X + \beta_2Z + \beta_3XZ.
$$
Le terme d'interaction demande si **le contraste associé à $X$ varie selon $Z$**.
::: {.callout-warning title="Deux résultats 'significatif / non significatif' ne démontrent pas une interaction"}
Observer `p < 0,05` dans un sous-groupe et `p > 0,05` dans un autre n'est pas un test de la différence entre les deux associations. L'interaction doit être estimée directement.
:::
## Avant le modèle : vérifier les cellules
Une interaction diplôme × âge répartit l'information entre 16 combinaisons. Il faut d'abord vérifier si chacune contient suffisamment d'observations pour rendre l'exercice raisonnable.
```{r}
#| label: tab-ch19-cellules
cellules <- enqueter |>
filter(
!is.na(age_classe_f),
!is.na(diplome_4),
!is.na(sport_regulier),
!is.na(sexe_f),
!is.na(sante_f)
) |>
count(age_classe_f, diplome_4, name = "n")
cellules |>
tidyr::pivot_wider(
names_from = diplome_4,
values_from = n
) |>
knitr::kable()
```
Une interaction ne devient pas crédible parce que la formule R accepte `*`. Si certaines cellules sont très petites, les prédictions correspondantes seront nécessairement fragiles.
## Décrire les deux gradients avant de les combiner
```{r}
#| label: ch19-descriptif
sport_age_diplome <- survey::svyby(
~sport_regulier,
~age_classe_f + diplome_4,
design,
survey::svymean,
na.rm = TRUE,
vartype = c("se", "ci")
)
sport_age_df <- svyby_ci_df(sport_age_diplome, "sport_regulier")
```
```{r}
#| label: plot-ch19-descriptif
#| fig-alt: "La pratique sportive régulière diminue avec l'âge dans les quatre groupes de diplôme, tandis que les niveaux scolaires supérieurs restent généralement plus élevés."
ggplot(
sport_age_df,
aes(
x = age_classe_f,
y = estimation,
colour = diplome_4,
group = diplome_4
)
) +
geom_line(linewidth = .9) +
geom_point(size = 2.6) +
geom_errorbar(
aes(ymin = bas, ymax = haut),
width = .10,
alpha = .55
) +
scale_colour_manual(values = palette_enqueter[1:4]) +
scale_y_continuous(
labels = scales::percent_format(accuracy = 1),
limits = c(0, .80)
) +
labs(
x = "Classe d'âge",
y = "Pratique sportive au moins hebdomadaire",
colour = "Diplôme",
title = "Âge et diplôme structurent simultanément la pratique sportive",
subtitle = "Estimations pondérées et IC à 95 %",
caption = "Les segments relient des catégories ; ils ne représentent pas une trajectoire individuelle."
) +
theme_enqueter()
```
Deux régularités apparaissent : une baisse de la pratique avec l'âge et un gradient scolaire. L'interaction pose une question plus précise : **l'écart entre diplômes se transforme-t-il avec l'âge ?**
## Construire un échantillon commun pour les deux modèles
```{r}
#| label: ch19-echantillon
vars_int <- c("sport_regulier", "diplome_4", "age_classe_f", "sexe_f", "sante_f")
ids_int <- enqueter |>
filter(if_all(all_of(vars_int), ~ !is.na(.x))) |>
pull(id)
design_int <- subset(design, id %in% ids_int)
c(
N_design = nrow(design$variables),
N_commun = nrow(design_int$variables)
)
```
Comparer un modèle additif et un modèle interactif sur des échantillons différents confondrait modification de la formule et modification de la population analysée.
## Modèle additif puis modèle interactif
```{r}
#| label: ch19-modeles
mod_add <- survey::svyglm(
sport_regulier ~ diplome_4 + age_classe_f + sexe_f + sante_f,
design = design_int,
family = quasibinomial()
)
mod_int <- survey::svyglm(
sport_regulier ~ diplome_4 * age_classe_f + sexe_f + sante_f,
design = design_int,
family = quasibinomial()
)
test_int <- survey::regTermTest(
mod_int,
~diplome_4:age_classe_f
)
```
```{r}
#| label: tab-ch19-test
p_interaction <- test_int$p
tibble::tibble(
Question = "Le gradient scolaire varie-t-il selon la classe d'âge ?",
`Test global du bloc d'interactions` = fmt_p(p_interaction),
`N commun aux deux modèles` = nobs(mod_int)
) |>
knitr::kable()
```
Le test global porte sur **l'ensemble du bloc diplôme × âge**. Il est préférable à une sélection de quelques coefficients d'interaction jugés intéressants après coup.
## Pourquoi les coefficients deviennent difficiles à lire
Dans un modèle avec interaction :
- le coefficient d'un niveau de diplôme correspond à ce contraste **dans la classe d'âge de référence** ;
- le coefficient d'une classe d'âge correspond à ce contraste **dans le diplôme de référence** ;
- les termes `diplôme × âge` modifient ces contrastes ;
- le tout vit sur l'échelle des log-odds.
Une longue table de coefficients est donc peu adaptée à la communication. Nous utilisons les coefficients pour calculer ; nous utilisons les **prédictions** pour interpréter.
## Probabilités prédites pour des profils comparables
Fixons sexe = femme et santé = bonne, puis faisons varier âge et diplôme.
```{r}
#| label: ch19-predictions
profils_int <- tidyr::expand_grid(
diplome_4 = factor(
levels(enqueter$diplome_4),
levels = levels(enqueter$diplome_4)
),
age_classe_f = factor(
levels(enqueter$age_classe_f),
levels = levels(enqueter$age_classe_f)
)
) |>
mutate(
sexe_f = factor("Femme", levels = levels(enqueter$sexe_f)),
sante_f = factor("Bonne", levels = levels(enqueter$sante_f))
)
pred_int <- predict(
mod_int,
newdata = profils_int,
type = "response",
se.fit = TRUE
)
ci_pred <- ci_estimation(
coef(pred_int),
SE(pred_int),
design_int,
lower = 0,
upper = 1
)
pred_int_df <- bind_cols(
profils_int,
ci_pred |>
transmute(
probabilite = estimation,
se,
bas,
haut
)
)
```
```{r}
#| label: plot-ch19-predictions
#| fig-alt: "Probabilités ajustées de pratique sportive régulière selon la classe d'âge et le diplôme, avec incertitude plus forte dans certaines combinaisons."
ggplot(
pred_int_df,
aes(
x = age_classe_f,
y = probabilite,
colour = diplome_4,
group = diplome_4
)
) +
geom_line(linewidth = 1) +
geom_point(size = 2.5) +
geom_errorbar(
aes(ymin = bas, ymax = haut),
width = .08,
alpha = .45
) +
scale_colour_manual(values = palette_enqueter[1:4]) +
scale_y_continuous(
labels = scales::percent_format(accuracy = 1),
limits = c(0, .80)
) +
labs(
x = "Classe d'âge",
y = "Probabilité ajustée de sport régulier",
colour = "Diplôme",
title = "L'interaction se juge sur l'évolution des écarts, pas sur une seule courbe",
subtitle = "Profils fixés à sexe = femme et santé = bonne",
caption = "Prédictions conditionnelles issues du modèle survey ; jeu synthétique."
) +
theme_enqueter()
```
## Le contraste qui nous intéresse vraiment : supérieur long – inférieur au bac
Une interaction est souvent plus lisible si l'on calcule directement le contraste substantif pour chaque valeur de la variable modératrice.
```{r}
#| label: tab-ch19-contrastes
contrastes_age <- pred_int_df |>
select(age_classe_f, diplome_4, probabilite) |>
tidyr::pivot_wider(
names_from = diplome_4,
values_from = probabilite
) |>
mutate(
`Écart supérieur long – inférieur au bac` =
`Supérieur long` - `Inférieur au bac`
) |>
transmute(
`Classe d'âge` = age_classe_f,
`Inférieur au bac` = fmt_pct(`Inférieur au bac`, .1),
`Supérieur long` = fmt_pct(`Supérieur long`, .1),
`Écart` = fmt_pp(`Écart supérieur long – inférieur au bac`, .1)
)
knitr::kable(contrastes_age)
```
Ce tableau répond plus directement à la question que douze coefficients : **le gradient scolaire s'élargit-il, se resserre-t-il ou reste-t-il relativement stable selon l'âge ?** Le test global indique ensuite si les variations observées sont suffisamment marquées, relativement à leur incertitude, pour soutenir une interaction sur l'échelle du modèle.
## Une interaction dépend de l'échelle
L'absence d'interaction sur l'échelle du logit n'implique pas nécessairement des écarts constants en **points de probabilité**. Inversement, des écarts de probabilité qui changent visuellement ne signifient pas toujours qu'un terme multiplicatif est nécessaire sur l'échelle du logit.
Pour communiquer une interaction logistique, il faut donc préciser :
- l'échelle du modèle ;
- l'échelle de la figure ;
- les valeurs auxquelles les autres variables sont fixées ;
- le contraste substantif examiné.
## Le prix statistique des interactions
Ajouter une interaction augmente le nombre de paramètres et segmente l'information. Les cellules rares ont des IC plus larges ; certaines combinaisons peuvent devenir quasiment non informatives.
Un modèle sans interaction peut alors être préférable non parce que « l'interaction n'est pas significative », mais parce qu'il représente plus parcimonieusement l'information disponible **et** répond suffisamment à la question.
::: {.callout-note title="Lecture sociologique"}
Le diplôme renvoie ici à une stratification sociale des pratiques ; l'âge renvoie à des positions différentes dans le cycle de vie. L'interaction demande si **la stratification scolaire de la pratique sportive se recompose selon ces positions d'âge**. Cette formulation est plus informative que « le terme d'interaction est-il significatif ? ».
:::
::: {.callout-warning title="Ce que nous ne pouvons pas conclure"}
Les classes d'âge comparent des personnes différentes dans une enquête transversale. Un contraste 18–29 / 60–79 peut refléter âge, génération, sélection et composition. Il ne décrit pas l'évolution d'une même personne au cours de sa vie.
:::
## Mini-exercice
Dans un modèle `Y ~ X * Z`, que signifie le coefficient principal de `X` ?
::: {.callout-caution collapse="true" title="Correction"}
Il correspond à l'association de `X` lorsque `Z` est à sa valeur ou catégorie de référence. Avec une interaction, les « effets principaux » ne sont donc plus des effets moyens généraux.
:::
## Exercice de synthèse
### Niveau A — Appliquer
Estimez les modèles additif et interactif diplôme × âge sur le même échantillon analytique.
### Niveau B — Choisir
Expliquez pourquoi un tableau de contrastes de probabilités peut être plus informatif qu'une table brute de coefficients d'interaction.
### Niveau C — Analyser
Proposez une autre interaction sociologiquement justifiée. Avant de l'estimer, écrivez : hypothèse substantive, cellules nécessaires, direction attendue, échelle sur laquelle le contraste sera communiqué et raison pour laquelle l'interaction serait informative.
## À retenir
- Une interaction signifie que l'association estimée d'une variable dépend d'une autre.
- Les modèles comparés doivent utiliser le même échantillon lorsqu'on veut isoler le rôle de la formule.
- Le test d'interaction doit porter directement sur le terme ou le bloc concerné.
- Les coefficients principaux deviennent conditionnels aux catégories de référence.
- Dans un modèle non linéaire, les interactions sont souvent plus lisibles en probabilités et en contrastes de probabilités.
- Les interactions consomment de l'information et peuvent produire des cellules très imprécises.
- Âge, période et génération ne sont pas interchangeables dans une enquête transversale.
## Pour aller plus loin
Le chapitre suivant élargit le choix du modèle à d'autres types de réponses : ordinales, multinomiales et de comptage.