Modèle de Cox stratifié en R : corriger une violation des risques proportionnels avec strata()

Quand une covariable catégorielle viole les risques proportionnels, donnez-lui son propre risque de base avec strata() au lieu d’un seul hazard ratio — ajustez, interprétez, vérifiez la correction avec cox.zph, et visualisez avec ggadjustedcurves

Apprenez le modèle de Cox stratifié en R avec les packages survival et survminer. Quand l’hypothèse des risques proportionnels échoue pour une covariable catégorielle, stratifiez dessus avec strata() de sorte que chaque strate obtienne son propre risque de base tandis que les autres covariables partagent des coefficients communs. Ajustez le modèle sur les données du cancer du côlon, interprétez pourquoi la variable de stratification n’a pas de hazard ratio, vérifiez que la violation est résolue avec cox.zph, comparez la stratification à l’interaction-avec-strates, et tracez des courbes de survie ajustées avec ggadjustedcurves.

Date de publication

26 juin 2026

Modifié

7 juillet 2026

AstucePoints clés
  • Quand une covariable catégorielle viole les risques proportionnels (PH), stratifiez dessus. Un modèle de Cox stratifié donne à cette variable son propre risque de base dans chaque strate, de sorte que PH n’a plus à tenir pour elle — et le hazard ratio unique et trompeur est remplacé par un risque de base par strate.
  • Syntaxe : enveloppez la variable fautive dans strata()coxph(Surv(time, status) ~ rx + age + strata(differ), data). Tout ce qui est hors de strata() garde un coefficient commun estimé à travers les strates ; la variable de stratification n’obtient aucun hazard ratio.
  • Le compromis : vous ne pouvez plus estimer l’effet de la variable de stratification (pas de HR pour elle). Stratifiez une covariable que vous voulez contrôler, pas rapporter — un facteur de confusion gênant ou un violateur des PH.
  • Vérifiez la correction : relancez cox.zph() sur le modèle stratifié — la violation devrait disparaître.
  • Stratification vs interaction : la stratification suppose que les autres covariables ont le même effet dans chaque strate. Si un effet lui-même diffère d’une strate à l’autre, ajoutez une interaction covariate * strata().
  • Visualisez-la avec ggadjustedcurves() de survminer — des courbes de survie ajustées, une par strate.

Introduction

Vous avez testé l’hypothèse des risques proportionnels de votre modèle de Cox, et cox.zph() a signalé une covariable : ses résidus de Schoenfeld mis à l’échelle dérivent avec le temps, donc son hazard ratio n’est pas constant au cours du suivi. Le HR unique que le modèle a affiché est une moyenne temporelle trompeuse. Et maintenant ?

Vous avez deux corrections honnêtes. Si le violateur est le traitement ou l’effet clé que vous voulez rapporter, modélisez son effet dépendant du temps — voir covariables dépendantes du temps. Mais si le violateur est une covariable catégorielle gênante — grade tumoral, centre d’étude, région — que vous avez seulement besoin d’ajuster, il existe un geste plus simple : stratifier dessus. La stratification dit « laissons cette variable avoir sa propre survie de base dans chaque groupe, et ne m’obligez pas à supposer PH pour elle. » Vous la contrôlez sans l’estimer.

Un cas classique : un essai multicentrique où le centre a une survie de base très différente (différents case-mix, standard de soins) et viole PH, mais où vous ne vous souciez que de l’effet du traitement ajusté sur le centre. Stratifiez par centre, estimez proprement le HR du traitement, et la non-proportionnalité du centre cesse de contaminer le résultat.

Cette leçon ajuste un modèle de Cox stratifié en R avec survival (le modèle) et survminer (le graphique des courbes ajustées), sur les données de l’essai sur le cancer du côlon. Vous apprendrez à :

  • reconnaître quand stratifier (un violateur catégoriel des PH ou un facteur de confusion gênant) ;
  • écrire la syntaxe strata() et comprendre pourquoi la variable de stratification perd son hazard ratio ;
  • vérifier que la violation est résolue avec cox.zph() ;
  • distinguer la stratification d’une interaction covariable × strates ;
  • et visualiser un modèle stratifié avec ggadjustedcurves().
NoteCe que change la stratification

Un modèle de Cox standard donne à chaque patient le même risque de base \(h_0(t)\) et un hazard ratio pour chaque covariable. Un modèle de Cox stratifié divise les données en strates \(s\) et laisse chacune avoir son propre risque de base \(h_{0s}(t)\), tandis que les covariables non stratifiées partagent un seul jeu de coefficients :

\[ h_s(t \mid x) = h_{0s}(t)\,\exp(b_1 x_1 + \dots + b_p x_p) \]

Ainsi la variable de stratification est absorbée dans le risque de base — elle n’obtient aucun HR (c’est tout l’intérêt). Les HR des autres covariables sont désormais ajustés au sein des strates : PH doit toujours tenir pour elles, mais pas pour la variable de stratification.

Les données : l’essai sur le cancer du côlon

Nous utilisons colon, l’essai de chimiothérapie adjuvante pour le cancer du côlon de stade B/C livré avec le package survival. Il enregistre deux résultats par patient (récidive et décès) ; nous gardons les enregistrements de décès (etype == 2), supprimons les quelques lignes auxquelles manquent des covariables clés, et étiquetons deux prédicteurs catégoriels pour que la sortie se lise clairement. Construisez la table d’analyse en bloc pour que chaque exemple s’exécute de façon autonome :

library(survival)

# Death outcome, complete cases on the covariates we model
dcolon <- colon[colon$etype == 2, ]
dcolon <- dcolon[!is.na(dcolon$differ) & !is.na(dcolon$nodes), ]

# Label the categorical predictors for readable output
dcolon$rx     <- factor(dcolon$rx)                                   # treatment arm
dcolon$differ <- factor(dcolon$differ, labels = c("well", "moderate", "poor"))  # tumor grade

c(patients = nrow(dcolon), deaths = sum(dcolon$status))
patients   deaths 
     888      430 

Les colonnes que nous modélisons :

  • time / status — le temps de survie en jours, et l’indicateur d’événement (1 = décès).
  • rx — le bras de traitement : Obs (observation), Lev (lévamisole), Lev+5FU (lévamisole + fluorouracile).
  • age, nodes (nombre de ganglions lymphatiques positifs), extent (extension tumorale locale, 1–4).
  • differ — le grade de différenciation tumorale : well / moderate / poor — notre covariable catégorielle.

Trouvez d’abord la violation

La stratification est un remède, alors commencez par confirmer qu’il y a quelque chose à remédier. Ajustez le modèle de Cox complet et testez les risques proportionnels avec cox.zph() :

library(survival)

dcolon <- colon[colon$etype == 2, ]
dcolon <- dcolon[!is.na(dcolon$differ) & !is.na(dcolon$nodes), ]
dcolon$rx     <- factor(dcolon$rx)
dcolon$differ <- factor(dcolon$differ, labels = c("well", "moderate", "poor"))

fit.full <- coxph(Surv(time, status) ~ rx + age + nodes + extent + differ, data = dcolon)
cox.zph(fit.full)
         chisq df       p
rx      2.9292  2 0.23117
age     1.2281  1 0.26777
nodes   0.0137  1 0.90666
extent  3.6014  1 0.05773
differ 16.9031  2 0.00021
GLOBAL 23.7091  7 0.00128

Lisez la sortie : le test GLOBAL est significatif (p ≈ 0.001), et le coupable est differ (p ≈ 0.0002) — l’effet de la différenciation tumorale sur le risque n’est pas constant dans le temps. Toutes les autres covariables sont correctes (toutes p > 0.05). Donc le hazard ratio unique que le modèle complet rapporte pour differ est trompeur, mais differ est ici une nuisance pronostique — nous voulons ajuster sur le grade tumoral, pas publier son HR. C’est le signal classique pour stratifier dessus.

AstuceStratifier, ou modéliser l’effet dépendant du temps ?
  • Le violateur des PH est une covariable catégorielle que vous avez seulement besoin de contrôlerstratifiez-la (cette leçon). Vous perdez son HR — dont vous n’aviez pas besoin.
  • Le violateur des PH est le traitement / effet clé que vous devez rapporter → modélisez un coefficient dépendant du temps ou une interaction avec le temps (covariables dépendantes du temps), pour conserver un effet précoce-vs-tardif interprétable.

Ajuster le modèle de Cox stratifié avec strata()

Déplacez la variable fautive à l’intérieur de strata() et laissez le reste de la formule inchangé. Chaque niveau de differ obtient maintenant son propre risque de base ; rx, age, nodes, et extent gardent des coefficients communs estimés à travers les strates :

library(survival)

dcolon <- colon[colon$etype == 2, ]
dcolon <- dcolon[!is.na(dcolon$differ) & !is.na(dcolon$nodes), ]
dcolon$rx     <- factor(dcolon$rx)
dcolon$differ <- factor(dcolon$differ, labels = c("well", "moderate", "poor"))

fit.strat <- coxph(Surv(time, status) ~ rx + age + nodes + extent + strata(differ),
                   data = dcolon)
summary(fit.strat)
Call:
coxph(formula = Surv(time, status) ~ rx + age + nodes + extent + 
    strata(differ), data = dcolon)

  n= 888, number of events= 430 

               coef exp(coef)  se(coef)      z Pr(>|z|)    
rxLev     -0.097313  0.907272  0.114104 -0.853   0.3937    
rxLev+5FU -0.388178  0.678292  0.121492 -3.195   0.0014 ** 
age        0.007117  1.007143  0.004123  1.726   0.0843 .  
nodes      0.084462  1.088131  0.009434  8.953  < 2e-16 ***
extent     0.501467  1.651142  0.115193  4.353 1.34e-05 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

          exp(coef) exp(-coef) lower .95 upper .95
rxLev        0.9073     1.1022    0.7255    1.1347
rxLev+5FU    0.6783     1.4743    0.5346    0.8607
age          1.0071     0.9929    0.9990    1.0153
nodes        1.0881     0.9190    1.0682    1.1084
extent       1.6511     0.6056    1.3174    2.0694

Concordance= 0.653  (se = 0.015 )
Likelihood ratio test= 95.4  on 5 df,   p=<2e-16
Wald test            = 113.3  on 5 df,   p=<2e-16
Score (logrank) test = 117.5  on 5 df,   p=<2e-16

Interprétez la sortie en langage clair :

  • Il n’y a aucune ligne pour differ. Ce n’est pas un bug — la variable de stratification est absorbée dans trois risques de base distincts (well / moderate / poor), donc elle n’a aucun hazard ratio. Vous avez échangé son estimation d’effet contre le fait de ne pas avoir à supposer PH pour elle.
  • rxLev+5FU : HR = 0.68, IC à 95 % 0.53–0.86, p = 0.0014. Ajusté sur les autres covariables et stratifié par grade tumoral, lévamisole + fluorouracile réduit le risque de décès d’environ 32 % par rapport à l’observation — le résultat clé de l’essai, estimé proprement au sein des strates de grade. rxLev seul (HR ≈ 0.91, p = 0.39) ne montre aucun bénéfice convaincant.
  • nodes : HR = 1.09 par ganglion positif, p < 10⁻¹⁸, et extent : HR = 1.65, p < 10⁻⁴ — deux forts facteurs de mauvais pronostic, comme attendu. age est limite (HR ≈ 1.01, p = 0.084).
  • Le modèle est globalement significatif (test du rapport de vraisemblance p < 0.001), et le nombre de strates égale le nombre de niveaux de differ (trois).

Vérifier la correction : cox.zph() sur le modèle stratifié

Stratifier devrait faire disparaître la violation — differ n’a plus besoin de satisfaire PH puisqu’il est désormais un risque de base, pas un coefficient. Confirmez-le :

library(survival)

dcolon <- colon[colon$etype == 2, ]
dcolon <- dcolon[!is.na(dcolon$differ) & !is.na(dcolon$nodes), ]
dcolon$rx     <- factor(dcolon$rx)
dcolon$differ <- factor(dcolon$differ, labels = c("well", "moderate", "poor"))

fit.strat <- coxph(Surv(time, status) ~ rx + age + nodes + extent + strata(differ),
                   data = dcolon)
cox.zph(fit.strat)
       chisq df    p
rx     2.398  2 0.30
age    1.221  1 0.27
nodes  0.749  1 0.39
extent 2.499  1 0.11
GLOBAL 7.186  5 0.21

La violation est résolue. differ a disparu du test (c’est un risque de base maintenant), et le test GLOBAL est non significatif (p ≈ 0.21) — toutes les covariables restantes satisfont les risques proportionnels. Le modèle stratifié est une représentation valide des données ; celui non stratifié ne l’était pas. (La stratification abaisse légèrement la concordance — ici ~0.65 contre ~0.66 — parce qu’elle dépense de l’information sur trois risques de base au lieu d’un seul HR ; c’est le coût attendu et modeste d’un modèle correct.)

La figure signature : courbes de survie ajustées (ggadjustedcurves())

Un modèle de Cox stratifié a une courbe de survie de base par strate. La fonction ggadjustedcurves() de survminer les trace comme des courbes de survie ajustées — la survie prédite par le modèle pour chaque groupe differ, en maintenant les autres covariables à leur moyenne. Passez le modèle ajusté, les données, et la variable sur laquelle découper :

library(survival)
library(survminer)

dcolon <- colon[colon$etype == 2, ]
dcolon <- dcolon[!is.na(dcolon$differ) & !is.na(dcolon$nodes), ]
dcolon$rx     <- factor(dcolon$rx)
dcolon$differ <- factor(dcolon$differ, labels = c("well", "moderate", "poor"))

fit.strat <- coxph(Surv(time, status) ~ rx + age + nodes + extent + strata(differ),
                   data = dcolon)

ggadjustedcurves(fit.strat, data = dcolon, variable = "differ",
                 method = "average", palette = "jco",
                 ggtheme = theme_minimal(),
                 xlab = "Time (days)", ylab = "Adjusted survival probability")

A survminer ggadjustedcurves plot of adjusted survival probability against time in days for three tumor-differentiation strata from a stratified Cox model on the colon-cancer data. The well-differentiated curve stays highest, the moderate curve sits in the middle, and the poorly-differentiated curve drops fastest and lowest, the three coloured curves separating clearly over follow-up.

Lisez-le : chaque courbe est la propre survie de base d’une strate, ajustée sur le traitement et les autres covariables. Les tumeurs bien différenciées survivent le mieux (courbe du haut), les peu différenciées le pire (en bas) — et surtout les courbes ne sont pas contraintes d’être proportionnelles les unes aux autres, ce qui est précisément pourquoi la stratification a géré la violation des PH. C’est l’image que vous mettez dans un article pour montrer comment les strates diffèrent.

NotePourquoi pas ggforest() ici ?

ggforest() — le forest plot que vous avez utilisé pour un modèle de Cox ordinaire — ne prend pas en charge un modèle avec strata() dans la formule (il échoue sur la colonne de strates manquante). Pour un modèle stratifié, lisez les hazard ratios depuis summary() et montrez les strates avec ggadjustedcurves(). Si vous avez spécifiquement besoin d’un forest plot, ajustez les covariables non stratifiées avec les strates dans un panneau séparé, ou rapportez directement le tableau de summary().

Stratification versus interaction : les autres effets diffèrent-ils selon les strates ?

La stratification fait une hypothèse forte : les covariables non stratifiées ont le même effet dans chaque strate (un seul HR rx commun à travers les tumeurs well / moderate / poor). C’est généralement ce que vous voulez — un effet de traitement unique et regroupé, ajusté sur le grade. Mais que se passe-t-il si vous soupçonnez que le traitement agit différemment selon les grades ? Alors vous avez besoin d’une interaction avec les strates, pas seulement d’une stratification.

Ajoutez une interaction rx * strata(differ) et comparez les deux modèles avec un test du rapport de vraisemblance :

library(survival)

dcolon <- colon[colon$etype == 2, ]
dcolon <- dcolon[!is.na(dcolon$differ) & !is.na(dcolon$nodes), ]
dcolon$rx     <- factor(dcolon$rx)
dcolon$differ <- factor(dcolon$differ, labels = c("well", "moderate", "poor"))

# Common treatment effect across strata (what we fit above)
fit.strat <- coxph(Surv(time, status) ~ rx + age + nodes + extent + strata(differ),
                   data = dcolon)

# Treatment effect ALLOWED to differ across strata
fit.inter <- coxph(Surv(time, status) ~ rx * strata(differ) + age + nodes + extent,
                   data = dcolon)

anova(fit.strat, fit.inter)
Analysis of Deviance Table
 Cox model: response is  Surv(time, status)
 Model 1: ~ rx + age + nodes + extent + strata(differ)
 Model 2: ~ rx * strata(differ) + age + nodes + extent
   loglik  Chisq Df Pr(>|Chi|)
1 -2381.3                     
2 -2379.2 4.0634  4     0.3975

Lisez-le : le test du rapport de vraisemblance est non significatif (p ≈ 0.40), donc laisser l’effet du traitement varier selon le grade tumoral n’améliore pas le modèle — le modèle stratifié à effet commun est adéquat. Le bénéfice du traitement est cohérent à travers les grades, ce qui est l’histoire la plus simple et la plus puissante à rapporter. Si ce test était significatif, vous garderiez l’interaction et rapporteriez un effet de traitement par strate au lieu d’un seul HR regroupé.

AstuceStratification vs interaction en une ligne
  • strata(g) → chaque strate son propre risque de base, mais les mêmes effets de covariables (un seul HR regroupé).
  • x * strata(g) → l’effet de x est autorisé à différer selon les strates (un HR séparé par strate). À utiliser seulement quand un LRT (ou la théorie) indique que l’effet varie réellement.

Un second motif : stratifier une covariable gênante dont vous n’avez jamais voulu de HR

Les violations des PH ne sont pas la seule raison de stratifier. Parfois vous voulez simplement contrôler une variable catégorielle sans estimer son effet — un facteur de confusion gênant. Prenez le modèle lung de la leçon Cox : supposez que sex est un facteur de confusion que vous devez ajuster mais ne voulez pas rapporter. Stratifiez dessus et vous obtenez les effets de l’âge et de l’ECOG ajustés sur le sexe, sans HR pour sex :

library(survival)

res.cox <- coxph(Surv(time, status) ~ age + ph.ecog + strata(sex), data = lung)
summary(res.cox)$coefficients
              coef exp(coef)    se(coef)        z     Pr(>|z|)
age     0.01056625  1.010622 0.009241374 1.143364 2.528875e-01
ph.ecog 0.46242443  1.587919 0.114761098 4.029453 5.590682e-05

age et ph.ecog sont estimés au sein des risques de base masculin et féminin — une estimation ajustée propre sans dépenser de coefficient sur sex. (Dans lung, sex ne viole pas réellement PH — voir la leçon de test des PH, où il a réussi le test — donc ici stratifier est un choix de modélisation pour contrôler une nuisance, pas un remède imposé. Les deux sont des usages légitimes de strata().)

Rapport

Parce que le grade de différenciation tumorale violait l’hypothèse des risques proportionnels (cox.zph p < 0.001), nous avons ajusté un modèle de Cox à risques proportionnels stratifié par grade de différenciation, avec le bras de traitement, l’âge, le nombre de ganglions positifs, et l’extension tumorale comme covariables. Lévamisole plus fluorouracile était associé à un risque de décès significativement plus faible que l’observation (HR = 0.68, IC à 95 % 0.53–0.86, p = 0.001), tout comme un nombre plus faible de ganglions positifs (HR = 1.09 par ganglion, p < 0.001) et une moindre extension tumorale (HR = 1.65, p < 0.001). Après stratification, l’hypothèse des risques proportionnels était satisfaite pour toutes les covariables modélisées (GLOBAL cox.zph p = 0.21), et une interaction traitement-par-strate n’a pas amélioré l’ajustement (LRT p = 0.40), confirmant un effet de traitement commun à travers les grades.

Un modèle de Cox ordinaire estime ses coefficients à partir d’une vraisemblance partielle construite sur tous les temps d’événement, où la contribution de chaque événement compare le patient qui a échoué à tous ceux de l’ensemble à risque à ce moment-là. Un modèle stratifié construit une vraisemblance partielle séparée au sein de chaque strate et les multiplie :

\[ L(b) = \prod_{s} L_s(b) \]

À l’intérieur de la strate \(s\), l’ensemble à risque à chaque temps d’événement ne contient que les patients de cette strate, donc le risque de base non spécifié de la strate \(h_{0s}(t)\) s’annule de chaque comparaison (tout comme \(h_0(t)\) s’annule dans le modèle ordinaire). Les covariables à l’intérieur de strata() n’entrent jamais dans aucune comparaison — les patients sont seulement comparés à d’autres dans la même strate — ce qui est précisément pourquoi la variable de stratification n’obtient aucun coefficient. Les covariables non stratifiées contribuent aux vraisemblances de toutes les strates et partagent donc une estimation commune \(b\). Ce produit unique de vraisemblances est ce que coxph() maximise quand vous écrivez strata().

Quel remède quand ?

AstuceChoisir comment gérer une violation des PH
  • Une covariable catégorielle viole PH, et vous avez seulement besoin de l’ajusterstratifiez-la (strata()) — cette leçon. Vous perdez son HR ; vous n’en aviez pas besoin.
  • Le traitement / effet clé viole PH → un coefficient dépendant du temps ou une interaction avec le temps (covariables dépendantes du temps), pour pouvoir rapporter l’effet précoce-vs-tardif.
  • Les effets des autres covariables diffèrent selon les strates → une interaction x * strata() (un HR séparé par strate), confirmée par un LRT.
  • Les courbes de survie des groupes se croisent (une comparaison à deux groupes) → un test log-rank pondéré aux côtés du modèle.

Essayez en direct

Ajustez le modèle stratifié vous-même — changez la variable sur laquelle vous stratifiez, ou remplacez le jeu de données. Le bac à sable démarre au premier Run.

🟢 Avec un agent IA

Demandez à Prova « mon modèle de Cox échoue au test des risques proportionnels pour une covariable catégorielle — stratifie dessus avec strata(), montre-moi les nouveaux hazard ratios, et confirme que cox.zph est désormais non significatif » — elle répond avec du code survival + survminer que vous pouvez exécuter sur vos propres données. The runtime is the judge. Demander à Prova →

Problèmes courants

Vous attendiez un hazard ratio pour la variable de stratification et il manque. C’est voulu, pas un bug. Une variable strata() est absorbée dans un risque de base séparé par niveau — elle n’a aucun HR. Si vous avez besoin d’estimer et rapporter l’effet de cette variable, ne la stratifiez pas : gardez-la comme covariable et, si elle viole PH, modélisez son effet dépendant du temps (covariables dépendantes du temps) à la place.

Trop de strates → ensembles à risque clairsemés et estimations instables. Chaque strate exécute sa propre vraisemblance partielle, donc stratifier sur une variable à forte cardinalité (ou en croiser plusieurs avec strata(a, b)) peut laisser des strates avec peu d’événements, gonflant les erreurs standard ou cassant la convergence. Visez un nombre d’événements sain par strate (une règle empirique approximative : ≥ 10 événements chacune) ; si les strates sont clairsemées, regroupez les niveaux cliniquement similaires avant de stratifier.

Vous avez stratifié une variable continue sans la regrouper en classes. strata(age) créerait une strate distincte pour chaque âge distinct — des centaines de strates à un patient, et un modèle inutile. La stratification est pour les covariables catégorielles (ou une continue découpée en quelques groupes, par ex. strata(cut(age, 3))). Un violateur des PH continu veut généralement une transformation ou un terme dépendant du temps, pas une stratification.

Questions fréquentes

Un modèle de Cox stratifié laisse une covariable catégorielle avoir son propre risque de base dans chaque groupe au lieu d’un seul hazard ratio. Vous l’écrivez en enveloppant la variable dans strata() : coxph(Surv(time, status) ~ treatment + age + strata(group), data). La variable de stratification n’obtient aucun HR (elle est absorbée dans le risque de base), tandis que les autres covariables partagent des coefficients communs et ajustés estimés au sein des strates. C’est la correction standard quand une covariable catégorielle viole les risques proportionnels.

Incluez-la comme covariable quand les risques proportionnels tiennent pour elle et que vous voulez estimer et rapporter son hazard ratio. Stratifiez-la quand elle viole PH (de sorte qu’un seul HR est trompeur) ou quand c’est un facteur de confusion gênant avec une survie de base très différente selon les niveaux que vous avez seulement besoin d’ajuster, pas de rapporter. Le compromis : stratifier vous achète une robustesse à la non-proportionnalité mais vous coûte l’estimation d’effet pour cette variable.

Cela dépend de quelle covariable échoue. Une covariable catégorielle gênantestratifiez-la avec strata() (cette leçon). Le traitement ou l’effet clé que vous devez rapporter → modélisez un coefficient dépendant du temps ou une interaction avec le temps pour pouvoir décrire l’effet précoce-vs-tardif (covariables dépendantes du temps). Pour une comparaison à deux groupes où les courbes se croisent, un test log-rank pondéré est un bon compagnon. N’abandonnez pas le modèle — une violation des PH est un problème que vous corrigez.

Parce que la stratification donne à chaque niveau son propre risque de base, et que les patients ne sont jamais comparés qu’ à d’autres dans la même strate dans la vraisemblance partielle. Le risque de base de la strate s’annule de chaque comparaison, donc la variable de stratification n’entre jamais dans un coefficient — il n’y a rien d’où estimer un HR. C’est le compromis délibéré : vous contrôlez la variable sans supposer PH pour elle, mais vous abandonnez son estimation d’effet.

Stratifier (strata(g)) donne à chaque groupe son propre risque de base mais suppose que les autres covariables ont le même effet dans chaque groupe — un seul hazard ratio regroupé. Une interaction avec les strates (x * strata(g)) laisse l’ effet de x lui-même différer selon les groupes — un HR séparé par strate. Utilisez la stratification simple par défaut ; ajoutez l’interaction seulement quand un test du rapport de vraisemblance (ou la connaissance du domaine) montre que l’ effet varie réellement selon les strates.

Testez vos connaissances

  1. Exécutez-le. Dans la cellule live ci-dessous, ajustez un modèle de Cox sur les données colon sans stratifier (rx + age + nodes + extent + differ), puis exécutez cox.zph(). Remplissez le blanc. Quelle covariable viole les risques proportionnels, et quelle est la p-value GLOBALE ?
  2. Interprétez. Un relecteur demande pourquoi votre modèle stratifié ne rapporte aucun hazard ratio pour differ. Expliquez en une ou deux phrases, et nommez la seule chose que vous abandonnez en stratifiant.

Remplissez le blanc avec differ (comme covariable simple, pas à l’intérieur de strata()). Lisez le tableau de cox.zph() : la ligne avec p < 0.05 est le violateur, et la ligne GLOBAL teste le modèle entier.

fit <- coxph(Surv(time, status) ~ rx + age + nodes + extent + differ, data = dcolon)
cox.zph(fit)
#> differ has p ~ 0.0002 (PH violated) and the GLOBAL test p ~ 0.001.
#> Every other covariate is non-significant (p > 0.05), so differ is the culprit —
#> the signal to stratify on it: strata(differ).

Pour la question 2 : la variable de stratification est absorbée dans un risque de base séparé par niveau, donc elle n’entre jamais dans un coefficient — il n’y a rien d’où calculer un hazard ratio (les patients sont seulement comparés au sein des strates). Ce que vous abandonnez est précisément l’estimation d’effet (HR) pour cette variable ; en échange vous n’avez plus à supposer les risques proportionnels pour elle.

AstuceVérification rapide

Vous stratifiez votre modèle de Cox sur region (un facteur de confusion catégoriel à 5 niveaux qui violait PH). Après stratification, le GLOBAL de cox.zph() est maintenant non significatif (p = 0.40), mais il n’y a aucun hazard ratio pour region dans la sortie. Le modèle est-il cassé ?

Non — c’est exactement ce que fait la stratification. region est maintenant cinq risques de base séparés, pas un coefficient, donc il n’a correctement aucun HR. Le GLOBAL non significatif de cox.zph() confirme que la violation est résolue et que les covariables restantes satisfont les risques proportionnels. Vous avez échangé l’estimation d’effet de region (dont vous n’aviez pas besoin — c’était un facteur de confusion gênant) contre un modèle valide. Si vous aviez besoin de l’effet de region, vous modéliseriez son effet dépendant du temps au lieu de stratifier.

Conclusion

Vous savez maintenant corriger une violation des risques proportionnels avec un modèle de Cox stratifié en R. Quand cox.zph() signale une covariable catégorielle que vous avez seulement besoin d’ajuster, enveloppez-la dans strata() : chaque niveau obtient son propre risque de base, les autres covariables gardent des coefficients communs ajustés, et la variable de stratification perd correctement son hazard ratio — le prix de ne pas avoir à supposer PH pour elle. Sur les données du cancer du côlon, stratifier par grade tumoral a résolu la violation (GLOBAL cox.zph p est passé de 0.001 à 0.21) et donné un effet de traitement propre (Lev+5FU HR = 0.68). Vous avez vérifié la correction, distingué la stratification d’une interaction covariable × strates avec un test du rapport de vraisemblance, et tracé les courbes de survie ajustées avec ggadjustedcurves(). Quand le violateur est l’effet que vous voulez réellement rapporter, tournez-vous vers un coefficient dépendant du temps à la place — l’outil suivant de la boîte.

Leçons connexes

Cette page vous a-t-elle été utile ?

Prouvez que vous savez le faire. Maîtrisez toute la série Analyse de survie en R — suivez votre parcours, construisez des projets et obtenez un certificat.

Commencer gratuitement →

Passez à Pro — Prova illimité sur vos propres données et un certificat vérifiable qui atteste la compétence.

dès 15 $/mois facturé annuellement

Passer à Pro →

✓ Vous êtes Pro — continuez. The runtime is the judge.

Recevez les nouvelles leçons R & Python par e-mail

Pratique, reproductible, sans spam. Désinscription à tout moment.

Double opt-in. Nous ne partageons jamais votre e-mail.

Partager cette pageXLinkedInRedditHN
Note

Chaque résultat de cette page a été produit par le code montré, exécuté au moment du build contre un environnement R figé — modifiez n’importe quel bloc et faites Run pour le reproduire vous-même.

Réutilisation

Citation

BibTeX
@online{2026,
  author = {},
  title = {Modèle de Cox stratifié en R : corriger une violation des
    risques proportionnels avec strata()},
  date = {2026-06-26},
  url = {https://www.datanovia.com/learn/biostatistics/survival-analysis/stratified-cox},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
“Modèle de Cox stratifié en R : corriger une violation des risques proportionnels avec strata().” 2026. June 26. https://www.datanovia.com/learn/biostatistics/survival-analysis/stratified-cox.