Année universitaire 2025–2026 • Parcours Économie de la santé & Développement durable
2025-11-27
SUBTYPE).GRADE).Données : cancer.dta (288 femmes avec cancer de l’endomètre).
On suppose que le fichier cancer.dta se trouve dans le dossier ./data/.
Rows: 288
Columns: 7
$ id <dbl> 10009, 10025, 10038, 10042, 10049, 10113, 10131, 10160, 10164…
$ grade <dbl+lbl> 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 2, 1, 2, 2, …
$ race <dbl+lbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ estrogen <dbl+lbl> 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, …
$ subtype <dbl+lbl> 1, 2, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, …
$ age <dbl+lbl> 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, …
$ smoking <dbl+lbl> 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
On dispose notamment des variables :
grade (3 modalités ordonnées),race,estrogen,subtype,age,smoking.On crée des facteurs explicites pour la régression, en choisissant des références cohérentes avec l’énoncé :
cancer <- cancer_raw |>
mutate(
# convertir les labels Stata en facteurs R
grade_f = as_factor(grade),
subtype_f = as_factor(subtype),
race_f = as_factor(race),
estrogen_f = as_factor(estrogen),
age_f = as_factor(age),
smk_f = as_factor(smoking),
# forcer l'ordre pour l'ordinal (adapter les noms à ce que tu vois)
grade_ord = fct_relevel(
grade_f,
"bien différencié",
"moyennement différencié",
"peu différencié"
)
)
cancer |>
dplyr::select(grade, grade_ord, subtype, subtype_f,
race_f, estrogen_f, age_f, smk_f) |>
head()# A tibble: 6 × 8
grade grade_ord subtype subtype_f race_f estrogen_f age_f smk_f
<dbl+lbl> <fct> <dbl+l> <fct> <fct> <fct> <fct> <fct>
1 1 [moyennement diff… moyennem… 1 [ade… adenosqu… blanc… never too… 50-64 yes
2 0 [bien différencié] bien dif… 2 [oth… other blanc… took oest… 50-64 no
3 1 [moyennement diff… moyennem… 1 [ade… adenosqu… blanc… never too… 65-79 no
4 0 [bien différencié] bien dif… 0 [ade… adenocar… blanc… never too… 65-79 no
5 0 [bien différencié] bien dif… 0 [ade… adenocar… blanc… took oest… 50-64 no
6 0 [bien différencié] bien dif… 0 [ade… adenocar… blanc… took oest… 65-79 no
Variables explicatives : RACE, ESTROGEN, SMK, AGE.
# weights: 18 (10 variable)
initial value 314.203115
iter 10 value 247.216796
final value 246.965190
converged
# A tibble: 10 × 8
y.level term estimate std.error statistic p.value conf.low conf.high
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 adenosquamous (Inter… 0.169 0.447 -3.97 7.18e-5 0.0705 0.407
2 adenosquamous race_f… 0.806 0.413 -0.521 6.02e-1 0.359 1.81
3 adenosquamous estrog… 0.483 0.378 -1.93 5.39e-2 0.230 1.01
4 adenosquamous smk_fy… 2.43 0.526 1.69 9.06e-2 0.869 6.82
5 adenosquamous age_f6… 2.66 0.412 2.38 1.75e-2 1.19 5.96
6 other (Inter… 0.282 0.378 -3.35 8.07e-4 0.134 0.591
7 other race_f… 1.13 0.376 0.319 7.49e-1 0.539 2.36
8 other estrog… 0.943 0.343 -0.171 8.64e-1 0.482 1.85
9 other smk_fy… 0.166 1.05 -1.71 8.67e-2 0.0214 1.29
10 other age_f6… 1.33 0.329 0.872 3.83e-1 0.699 2.54
Call:
multinom(formula = subtype_f ~ race_f + estrogen_f + smk_f +
age_f, data = cancer)
Coefficients:
(Intercept) race_fnoire estrogen_ftook oestrogen treatment
adenosquamous -1.775326 -0.2151038 -0.72813726
other -1.266281 0.1201888 -0.05867755
smk_fyes age_f65-79
adenosquamous 0.889793 0.9780758
other -1.793171 0.2865677
Std. Errors:
(Intercept) race_fnoire estrogen_ftook oestrogen treatment
adenosquamous 0.4471631 0.4127306 0.3777940
other 0.3779403 0.3762200 0.3425219
smk_fyes age_f65-79
adenosquamous 0.5257988 0.4117731
other 1.0467384 0.3285697
Residual Deviance: 493.9304
AIC: 513.9304
On obtient, pour chaque modalité \(g\) \(\neq\) référence, une équation :
\(\log \frac{P(\text{SUBTYPE}=g)}{P(\text{SUBTYPE}=\text{adenocarcinomous})} = \beta{g0} + \beta{g,\text{race}} + \dots\)
À caractéristiques identiques (race, oestrogènes, âge), les fumeuses ont environ 2,4 fois plus de chances (odds) d’avoir un cancer adenosquamous plutôt que le sous-type de référence, comparées aux non fumeuses.
# A tibble: 10 × 8
y.level term estimate std.error statistic p.value conf.low conf.high
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 adenosquamous (Inter… 0.169 0.447 -3.97 7.18e-5 0.0705 0.407
2 adenosquamous race_f… 0.806 0.413 -0.521 6.02e-1 0.359 1.81
3 adenosquamous estrog… 0.483 0.378 -1.93 5.39e-2 0.230 1.01
4 adenosquamous smk_fy… 2.43 0.526 1.69 9.06e-2 0.869 6.82
5 adenosquamous age_f6… 2.66 0.412 2.38 1.75e-2 1.19 5.96
6 other (Inter… 0.282 0.378 -3.35 8.07e-4 0.134 0.591
7 other race_f… 1.13 0.376 0.319 7.49e-1 0.539 2.36
8 other estrog… 0.943 0.343 -0.171 8.64e-1 0.482 1.85
9 other smk_fy… 0.166 1.05 -1.71 8.67e-2 0.0214 1.29
10 other age_f6… 1.33 0.329 0.872 3.83e-1 0.699 2.54
Ici, estimate = odds-ratio, conf.low / conf.high = IC 95 %.
On génère les probabilités prédites pour chaque modalité de SUBTYPE :
adenocarcinomous adenosquamous other
1 0.6852096 0.2826449 0.03214548
2 0.7420517 0.0607007 0.19724761
3 0.5476498 0.2467524 0.20559787
4 0.5476498 0.2467524 0.20559787
5 0.7420517 0.0607007 0.19724761
6 0.6363103 0.1384208 0.22526893
adenocarcinomous adenosquamous other
0.6433555 0.1573435 0.1993010
On recolle ces probabilités aux données :
# weights: 18 (10 variable)
initial value 314.203115
iter 10 value 247.216796
final value 246.965190
converged
adenocarcinomous adenosquamous other
0.6433555 0.1573435 0.1993010
# 4) On transforme en tibble et on renomme
phat_sub_tbl <- as_tibble(phat_sub)
names(phat_sub_tbl) <- paste0("p_", levels(cancer_complete$subtype_f))
# ex : "p_adenocarcinomous" "p_adenosquamous" "p_other"
# 5) On colle aux données *complètes* (286 lignes)
cancer_sub <- bind_cols(cancer_complete, phat_sub_tbl)
cancer_sub |>
dplyr::select(subtype_f, starts_with("p_")) |>
slice(1:5)# A tibble: 5 × 4
subtype_f p_adenocarcinomous p_adenosquamous p_other
<fct> <dbl> <dbl> <dbl>
1 adenosquamous 0.685 0.283 0.0321
2 other 0.742 0.0607 0.197
3 adenosquamous 0.548 0.247 0.206
4 adenocarcinomous 0.548 0.247 0.206
5 adenocarcinomous 0.742 0.0607 0.197
On utilise logitgof() du package generalhoslem.
obs : modalités de SUBTYPE sous forme numérique (1, 2, 3).exp : matrice de probabilités prédites.g : nombre de groupes (à ajuster si nécessaire).
Hosmer and Lemeshow test (multinomial model)
data: y_num, exp_mat
X-squared = 4.0727, df = 10, p-value = 0.944
Le test de Hosmer–Lemeshow (généralisé au multinomial et implémenté par
logitgof()dansgeneralhoslem) compare, dans des groupes de probabilités prédites similaires, les effectifs observés de chaque catégorie de la variable dépendante aux effectifs attendus selon le modèle. La statistique de test est de type χ². Une grande p-value indique que l’on ne détecte pas de mauvais ajustement global du modèle aux données ; une petite p-value suggère un manque d’adéquation (modèle mal calibré).
Si le test ne passe pas (classes attendues trop petites), on réduit le nombre de groupes :
# A tibble: 7 × 3
g stat p_value
<int> <dbl> <dbl>
1 4 1.69 0.792
2 5 2.01 0.735
3 6 15.7 0.0474
4 7 2.63 0.955
5 8 2.63 0.955
6 9 16.5 0.170
7 10 4.07 0.944
Lecture :
En diminuant le nombre de groupes, le test Hosmer–Lemeshow devient applicable. Les p-values varient avec g, ce qui montre que le test est assez instable dans ce petit échantillon multinomial. Néanmoins, pour la plupart des partitions (g = 4, 5, 7, 8, 9, 10), on ne rejette pas l’hypothèse d’un bon ajustement (p-value > 5 %). On peut donc conclure qu’il n’y a pas de signe clair de mauvais ajustement du modèle aux données, tout en rappelant que ce test doit être interprété avec prudence.
On cherche un modèle plus parcimonieux en retirant les variables non significatives.
Exemple : on teste si on peut retirer smk_f puis age_f
# weights: 15 (8 variable)
initial value 314.203115
iter 10 value 251.550761
final value 251.468001
converged
Likelihood ratio tests of Multinomial Models
Response: subtype_f
Model Resid. df Resid. Dev Test Df
1 race_f + estrogen_f + age_f 564 502.9360
2 race_f + estrogen_f + smk_f + age_f 562 493.9304 1 vs 2 2
LR stat. Pr(Chi)
1
2 9.005622 0.01107781
Test 1 : peut-on retirer smk_f ?
Modèles comparés :
Modèle réduit : subtype_f ~ race_f + estrogen_f + age_f
Modèle complet : subtype_f ~ race_f + estrogen_f + smk_f + age_f
Résultat du test LR :
smk_f” : le tabagisme (smk_f) apporte une information significative pour expliquer le sous-type de cancer → on garde smk_f.# weights: 15 (8 variable)
initial value 314.203115
iter 10 value 250.492692
final value 250.192192
converged
Likelihood ratio tests of Multinomial Models
Response: subtype_f
Model Resid. df Resid. Dev Test Df
1 race_f + estrogen_f + smk_f 564 500.3844
2 race_f + estrogen_f + smk_f + age_f 562 493.9304 1 vs 2 2
LR stat. Pr(Chi)
1
2 6.454004 0.03967628
Interprétation :
smk_f mais pas ageLes degrés de liberté du test de rapport de vraisemblance sont égaux au nombre de paramètres supprimés entre le modèle complet et le modèle réduit.
Dans un modèle multinomial, retirer une variable facteur à \(L\) modalités enlève \((J-1)(L-1)\) coefficients, donc \((J-1)(L-1)\) degrés de liberté. \(J\) catégories et \(L\) modalités.
On teste sur les deux variables restantes
# weights: 15 (8 variable)
initial value 314.203115
iter 10 value 247.380882
final value 247.202541
converged
Likelihood ratio tests of Multinomial Models
Response: subtype_f
Model Resid. df Resid. Dev Test Df
1 age_f + estrogen_f + smk_f 564 494.4051
2 race_f + estrogen_f + smk_f + age_f 562 493.9304 1 vs 2 2
LR stat. Pr(Chi)
1
2 0.4747033 0.7887139
On retire la variable estrogen pour expliquer SUBTYPE :
# 1) Construire un sous-échantillon complet pour toutes les variables en jeu
cancer_lr <- cancer |>
filter(
!is.na(subtype_f),
!is.na(age_f),
!is.na(race_f),
!is.na(smk_f),
!is.na(estrogen_f) # même si tu ne l'utilises pas dans tous les modèles
)
# 2) Re-estimer les modèles sur CE MÊME jeu de données
mod_sub_full <- multinom(
subtype_f ~ age_f + race_f + smk_f + estrogen_f,
data = cancer_lr
)# weights: 18 (10 variable)
initial value 314.203115
iter 10 value 247.216796
final value 246.965190
converged
# weights: 15 (8 variable)
initial value 314.203115
iter 10 value 249.150731
final value 248.865000
converged
Likelihood ratio tests of Multinomial Models
Response: subtype_f
Model Resid. df Resid. Dev Test Df
1 age_f + race_f + smk_f 564 497.7300
2 age_f + race_f + smk_f + estrogen_f 562 493.9304 1 vs 2 2
LR stat. Pr(Chi)
1
2 3.799621 0.1495969
La fonction se base uniquement sur les résultats passés (modèles déjà estimés)
et sélectionne celui qui minimise l’AIC (ou le BIC).
# weights: 12 (6 variable)
initial value 316.400339
iter 10 value 250.030104
final value 250.024195
converged
# A tibble: 6 × 8
y.level term estimate std.error statistic p.value conf.low conf.high
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 adenosquamous (Interc… 0.111 0.374 -5.88 4.16e-9 0.0535 0.231
2 adenosquamous age_f65… 2.67 0.409 2.40 1.63e-2 1.20 5.95
3 adenosquamous smk_fyes 2.36 0.520 1.65 9.83e-2 0.853 6.54
4 other (Interc… 0.283 0.271 -4.66 3.10e-6 0.166 0.481
5 other age_f65… 1.30 0.328 0.812 4.17e-1 0.686 2.48
6 other smk_fyes 0.167 1.05 -1.71 8.69e-2 0.0214 1.30
Principe d’interprétation :
adenocarcinomous), toutes choses égales par ailleurs.On attribue à chaque individu la classe prédite correspondant à la probabilité la plus élevée :
adenocarcinomous adenosquamous other
0.6458337 0.1562522 0.1979141
phat_sub_tbl <- as_tibble(phat_sub)
names(phat_sub_tbl) <- paste0("p_", levels(cancer_complete$subtype_f))
# ex : "p_adenocarcinomous" "p_adenosquamous" "p_other"
cancer <- bind_cols(cancer, phat_sub_tbl)
# vecteur des probas sous forme de matrice
probs_mat <- as.matrix(
cancer[, c("p_adenocarcinomous", "p_adenosquamous", "p_other")]
)
# indices de la proba max par individu (1, 2 ou 3)
idx_max <- max.col(probs_mat)
# noms des modalités dans le bon ordre
lev <- c("adenocarcinomous", "adenosquamous", "other")
cancer <- cancer |>
mutate(
pred_subtype = factor(lev[idx_max],
levels = levels(subtype_f))
)
cancer |>
dplyr::select(subtype_f, pred_subtype, starts_with("p_")) |>
slice(1:5)# A tibble: 5 × 5
subtype_f pred_subtype p_adenocarcinomous p_adenosquamous p_other
<fct> <fct> <dbl> <dbl> <dbl>
1 adenosquamous adenocarcinomous 0.763 0.201 0.0360
2 other adenocarcinomous 0.717 0.0798 0.203
3 adenosquamous adenocarcinomous 0.600 0.178 0.221
4 adenocarcinomous adenocarcinomous 0.600 0.178 0.221
5 adenocarcinomous adenocarcinomous 0.717 0.0798 0.203
On modélise le stade de la tumeur (bien / moyennement / peu différenciée) en fonction de :
RACE, ESTROGEN, SUBTYPE, AGE, SMK.On utilise polr() (MASS) avec grade_ord comme variable ordinale.
Call:
polr(formula = grade_ord ~ race_f + estrogen_f + subtype_f +
age_f + smk_f, data = cancer, Hess = TRUE)
Coefficients:
Value Std. Error t value
race_fnoire 0.59764 0.2790 2.14222
estrogen_ftook oestrogen treatment -0.61411 0.2549 -2.40904
subtype_fadenosquamous 1.78110 0.3252 5.47653
subtype_fother 0.07823 0.2991 0.26153
age_f65-79 0.13110 0.2486 0.52742
smk_fyes 0.01358 0.3992 0.03401
Intercepts:
Value Std. Error t value
bien différencié|moyennement différencié -0.0205 0.2890 -0.0708
moyennement différencié|peu différencié 1.9682 0.3196 6.1589
Residual Deviance: 540.4501
AIC: 556.4501
(2 observations deleted due to missingness)
Les sorties donnent :
GRADE.On ajoute l’interaction estrogen_f * subtype_f :
Likelihood ratio tests of ordinal regression models
Response: grade_ord
Model Resid. df Resid. Dev Test
1 race_f + estrogen_f + subtype_f + age_f + smk_f 278 540.4501
2 race_f + estrogen_f * subtype_f + age_f + smk_f 276 537.5181 1 vs 2
Df LR stat. Pr(Chi)
1
2 2 2.932012 0.2308457
anova() réalise un test LR entre modèle sans interaction (mod_grade_base) et modèle avec interaction (mod_grade_es).On peut tester d’autres interactions pertinentes, par exemple :
ESTROGEN × AGE :Likelihood ratio tests of ordinal regression models
Response: grade_ord
Model Resid. df Resid. Dev Test
1 race_f + estrogen_f + subtype_f + age_f + smk_f 278 540.4501
2 race_f + estrogen_f * age_f + subtype_f + smk_f 277 539.2085 1 vs 2
Df LR stat. Pr(Chi)
1
2 1 1.241621 0.2651588
SUBTYPE × AGE :Likelihood ratio tests of ordinal regression models
Response: grade_ord
Model Resid. df Resid. Dev Test
1 race_f + estrogen_f + subtype_f + age_f + smk_f 278 540.4501
2 race_f + estrogen_f + subtype_f * age_f + smk_f 276 538.9157 1 vs 2
Df LR stat. Pr(Chi)
1
2 2 1.534397 0.4643119
Pour chaque interaction :
Call:
polr(formula = grade_ord ~ race_f + estrogen_f + subtype_f +
age_f + smk_f, data = cancer, Hess = TRUE)
Coefficients:
Value Std. Error t value
race_fnoire 0.59764 0.2790 2.14222
estrogen_ftook oestrogen treatment -0.61411 0.2549 -2.40904
subtype_fadenosquamous 1.78110 0.3252 5.47653
subtype_fother 0.07823 0.2991 0.26153
age_f65-79 0.13110 0.2486 0.52742
smk_fyes 0.01358 0.3992 0.03401
Intercepts:
Value Std. Error t value
bien différencié|moyennement différencié -0.0205 0.2890 -0.0708
moyennement différencié|peu différencié 1.9682 0.3196 6.1589
Residual Deviance: 540.4501
AIC: 556.4501
(2 observations deleted due to missingness)
Race (noire vs non noire)
Coefficient = 0,60 (t ≈ 2,14) → significatif.
Les femmes noires ont des cotes ≈ 1,8 fois plus élevées d’avoir un grade plus mauvais (passer vers “moyennement/peu différencié”), toutes choses égales par ailleurs.
Traitement aux œstrogènes (oui vs non)
Coefficient = −0,61 (t ≈ −2,41) → significatif.
Les femmes ayant reçu un traitement œstrogénique ont des cotes ≈ divisées par 2 d’avoir un grade plus mauvais. → Le traitement est associé à des grades un peu meilleurs.
Sous-type tumoral (réf. = adenocarcinomous)
adenosquamous : coef = 1,78 (t ≈ 5,48) → très significatif.
→ Cotes ≈ 6 fois plus élevées d’avoir un grade plus défavorable que l’adenocarcinome.
other : effet faible et non significatif.
Âge (65–79 ans) et tabagisme (smk_fyes)
SUBTYPE ;GRADE.À retenir :