Test du log-rank par paires en R : quelles courbes diffèrent ?

Après un test du log-rank global significatif sur 3 groupes ou plus, lancez pairwise_survdiff() pour repérer quels bras diffèrent — avec les p-values ajustées pour les comparaisons multiples

Comparez trois courbes de survie ou plus en R. Une fois qu’un test du log-rank global indique que la survie diffère, lancez les tests du log-rank par paires avec pairwise_survdiff() de survminer et ajustez les p-values (Benjamini-Hochberg, Holm, Bonferroni) pour voir exactement quels bras de traitement diffèrent — sur les données de l’essai sur le cancer du côlon, avec les courbes de Kaplan-Meier et la p-value globale sur le graphique.

Date de publication

25 juin 2026

Modifié

7 juillet 2026

AstucePoints clés
  • Avec trois groupes ou plus, le test du log-rank global (survdiff()) vous indique que certains groupes diffèrent — mais pas lesquels. Pour trouver les paires précises, lancez les tests du log-rank par paires.
  • Faire chaque paire séparément à p < 0.05 gonfle le taux de faux positifs (le problème de multiplicité). Vous devez ajuster les p-values par paires selon le nombre de comparaisons.
  • Utilisez pairwise_survdiff(Surv(time, status) ~ group, data, p.adjust.method = "BH") de survminer — il teste toutes les paires et renvoie une matrice de p-value ajustée en un seul appel.
  • Sur l’essai sur le cancer du côlon, le test global est significatif (χ² = 11.7, df = 2, p = 0.003), et les tests par paires montrent que Lev+5FU bat à la fois Observation (p = 0.005) et Levamisole seul (p = 0.006), tandis que Observation vs Levamisole ne diffèrent pas (p = 0.81).
  • Choisissez l’ajustement selon l’objectif : Bonferroni/Holm contrôlent le taux d’erreur par famille (strict, confirmatoire) ; Benjamini-Hochberg (BH) contrôle le taux de fausses découvertes (plus de puissance, exploratoire).

Introduction

Vous avez mené un essai clinique avec trois bras de traitement et un test du log-rank global est ressorti significatif : la survie diffère quelque part. Mais « quelque part » n’est pas une conclusion à mettre dans un article. La nouvelle combinaison bat-elle le médicament standard ? L’un des deux bat-il l’abstention ? Le test global ne le dira pas — par construction, il regroupe tous les groupes dans un unique verdict « au moins une courbe diffère » sans détail.

Pour obtenir le détail, vous comparez les groupes par paires : bras A vs B, A vs C, B vs C. Chaque paire est un test du log-rank distinct. Le piège, c’est la multiplicité — lancez assez de tests à p < 0.05 et vous finirez par signaler une différence qui n’est pas réelle. Avec trois groupes vous faites trois tests, et la probabilité d’au moins un faux positif grimpe de 5 % vers 14 %. Les comparaisons de survie par paires nécessitent donc un ajustement de p-value.

Cette leçon procède de façon pratique : confirmez d’abord le test du log-rank global, puis lancez tous les tests du log-rank par paires en un seul appel avec pairwise_survdiff() de survminer, lisez la matrice de p-value ajustée, et choisissez la bonne correction. Nous utilisons l’essai sur le cancer du côlon (colon, dans le paquet survival) — trois vrais bras de traitement, si bien que la question des comparaisons multiples est authentique, pas artificielle.

NoteLe problème de multiplicité en une ligne

Chaque test du log-rank par paires a 5 % de chance de donner un faux positif à lui seul. Faites 3 tests indépendants et la probabilité d’au moins un faux positif est \(1 - (1 - 0.05)^3 \approx 0.14\) — presque le triple des 5 % que vous croyez contrôler. Ajuster les p-values par paires ramène le taux d’erreur global à 5 %.

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

Nous utilisons colon, un essai clinique de chimiothérapie adjuvante pour le cancer du côlon de stade B/C, livré avec le paquet survival. Avec survival attaché, colon est disponible directement (aucun appel à data() nécessaire). L’essai compte trois bras de traitement dans la colonne rx — c’est ce qui le rend parfait ici :

library(survival)

# One row per recurrence AND per death; keep the death records (etype == 2)
colon_death <- colon[colon$etype == 2, ]

table(colon_death$rx)

    Obs     Lev Lev+5FU 
    315     310     304 

Les colonnes que nous allons utiliser :

  • time — temps de suivi en jours.
  • status — indicateur d’événement : 1 = décès (l’événement), 0 = censuré (vivant au dernier suivi).
  • rx — le bras de traitement, notre variable de groupement, avec trois niveaux : Obs (observation, sans traitement), Lev (levamisole seul) et Lev+5FU (levamisole plus fluorouracil).

colon enregistre deux types d’événement par patient (etype : 1 = récidive, 2 = décès). Nous conservons les enregistrements de décès pour que chaque patient apparaisse une seule fois et que le critère soit la survie globale.

Étape 1 — d’abord le test du log-rank global

Ne passez jamais directement aux tests par paires. Lancez d’abord le test du log-rank global : s’il n’est pas significatif, vous n’avez aucune licence pour aller pêcher des différences par paires. Le test global est survdiff() avec les trois groupes à droite de la formule :

library(survival)

colon_death <- colon[colon$etype == 2, ]

survdiff(Surv(time, status) ~ rx, data = colon_death)
Call:
survdiff(formula = Surv(time, status) ~ rx, data = colon_death)

             N Observed Expected (O-E)^2/E (O-E)^2/V
rx=Obs     315      168      148      2.58      3.85
rx=Lev     310      161      146      1.52      2.25
rx=Lev+5FU 304      123      157      7.55     11.62

 Chisq= 11.7  on 2 degrees of freedom, p= 0.003 

Lisez-le en langage clair : le tableau montre, par bras, les décès observés par rapport aux décès attendus si la survie était identique. Lev+5FU a eu 123 observés vs 157 attendus — bien moins de décès que prévu, le signal d’un bénéfice. Le résultat clé est Chisq = 11.7 on 2 degrees of freedom, p = 0.003 (df = degrés de liberté = nombre de groupes − 1 = 2). Comme p < 0.05, nous rejetons l’hypothèse nulle que les trois courbes sont identiques : la survie diffère selon les bras. Maintenant — et seulement maintenant — nous demandons quels bras.

Étape 2 — tests du log-rank par paires avec pairwise_survdiff()

pairwise_survdiff() de survminer lance un test du log-rank pour chaque paire de groupes et renvoie les p-values ajustées sous forme de matrice — un seul appel au lieu de trois comparaisons survdiff() construites à la main. Réglez la correction avec p.adjust.method ; "BH" (Benjamini-Hochberg) est un défaut judicieux :

library(survival)
library(survminer)

colon_death <- colon[colon$etype == 2, ]

pairwise_survdiff(
  Surv(time, status) ~ rx,
  data = colon_death,
  p.adjust.method = "BH"
)

    Pairwise comparisons using Log-Rank test 

data:  colon_death and rx 

        Obs    Lev   
Lev     0.8114 -     
Lev+5FU 0.0048 0.0063

P value adjustment method: BH 

Lisez la matrice. Chaque cellule est la p-value ajustée pour cette paire (le groupe en ligne vs le groupe en colonne) :

  • Lev+5FU vs Obsp = 0.005 — significatif. La combinaison améliore la survie par rapport à l’absence de traitement.
  • Lev+5FU vs Levp = 0.006 — significatif. La combinaison bat aussi le levamisole seul.
  • Obs vs Levp = 0.81 — non significatif. Le levamisole seul ne fait pas mieux que l’observation.

Le test global significatif est donc entièrement porté par Lev+5FU : c’est le seul bras qui diffère des autres, et le levamisole seul n’ajoute rien. Voilà une conclusion que vous pouvez écrire — et c’est exactement ce qu’a réellement trouvé l’essai.

ImportantPourquoi vous ne pouvez pas sauter l’ajustement

Regardez ce qu’auraient été les p-values par paires non ajustées : Lev+5FU vs Obs = 0.0016 et Lev+5FU vs Lev = 0.0042. L’ajustement BH les a poussées à 0.005 et 0.006 — toujours significatives, donc la conclusion tient. Mais c’est l’ajustement qui la rend fiable : rapportez des p-values par paires brutes et un relecteur vous demandera (à juste titre) comment vous avez contrôlé le fait de lancer trois tests. Rapportez toujours les valeurs ajustées et nommez la méthode.

Étape 3 — choisir la méthode d’ajustement

pairwise_survdiff() passe p.adjust.method directement à p.adjust() de R de base, si bien que toute correction standard est disponible. Elles échangent la rigueur contre la puissance :

library(survival)
library(survminer)

colon_death <- colon[colon$etype == 2, ]

# The same pairwise tests under three corrections — compare the p-value matrices
bonferroni <- pairwise_survdiff(Surv(time, status) ~ rx, data = colon_death,
                                p.adjust.method = "bonferroni")$p.value
holm       <- pairwise_survdiff(Surv(time, status) ~ rx, data = colon_death,
                                p.adjust.method = "holm")$p.value
bh         <- pairwise_survdiff(Surv(time, status) ~ rx, data = colon_death,
                                p.adjust.method = "BH")$p.value

round(bonferroni, 4)   # most conservative
           Obs    Lev
Lev     1.0000     NA
Lev+5FU 0.0048 0.0125
round(holm, 4)         # FWER, uniformly more powerful than Bonferroni
           Obs    Lev
Lev     0.8114     NA
Lev+5FU 0.0048 0.0083
round(bh, 4)           # controls the false-discovery rate
           Obs    Lev
Lev     0.8114     NA
Lev+5FU 0.0048 0.0063

Ce que les trois donnent pour la comparaison Lev+5FU vs Lev : Bonferroni = 0.013, Holm = 0.008, BH = 0.006 — toutes significatives ici, mais de moins en moins conservatrices. Comment choisir :

  • Bonferroni — multiplie chaque p-value brute par le nombre de comparaisons. La plus simple et la plus conservatrice ; contrôle le taux d’erreur par famille (la chance de tout faux positif). Convient pour quelques comparaisons ; perd vite en puissance quand le nombre augmente.
  • Holm — une version descendante (step-down) de Bonferroni. Contrôle aussi le taux d’erreur par famille mais est uniformément plus puissante — elle ne rejette jamais moins que Bonferroni. Il n’y a aucune raison de préférer Bonferroni simple à Holm.
  • Benjamini-Hochberg (BH) — contrôle le taux de fausses découvertes (la proportion attendue de faux positifs parmi les comparaisons que vous signalez), pas la chance d’en avoir un. Plus puissant, et le bon choix quand vous criblez de nombreux groupes ou faites un travail exploratoire et pouvez tolérer une petite fraction de faux positifs.
AstuceQuel ajustement quand ?
  • Comparaison confirmatoire, peu de groupes, réglementaire ou à fort enjeuHolm (contrôle du FWER, plus de puissance que Bonferroni).
  • Criblage exploratoire sur de nombreux groupesBenjamini-Hochberg (contrôle du FDR, plus de puissance).
  • Vous avez besoin de la borne unique la plus simple et la plus défendableBonferroni (mais Holm la domine).

Le défaut dans pairwise_survdiff() est Benjamini-Hochberg (BH), qui est aussi ce que nous avons utilisé plus haut. Quel que soit votre choix, indiquez la méthode à côté des p-values.

Visualiser : les trois courbes avec la p-value globale

Une comparaison multi-groupes a quand même besoin du graphique. Tracez les trois courbes de Kaplan-Meier avec ggsurvplot() de survminer ; pval = TRUE affiche la p-value du log-rank globale sur le panneau (le détail par paires vit dans la matrice ci-dessus), et la table de risque montre combien de patients restent dans chaque bras :

library(survival)
library(survminer)

colon_death <- colon[colon$etype == 2, ]

fit <- survfit(Surv(time, status) ~ rx, data = colon_death)

ggsurvplot(
  fit,
  data = colon_death,
  pval = TRUE,                                  # the OVERALL log-rank p-value, on the plot
  conf.int = TRUE,                              # 95% confidence bands
  risk.table = TRUE,                            # number-at-risk table beneath
  palette = "jco",                              # colourblind-safe journal palette
  legend.labs = c("Observation", "Levamisole", "Lev+5FU"),
  legend.title = "Treatment",
  xlab = "Time (days)",
  ylab = "Survival probability",
  ggtheme = theme_minimal()
)

A survminer Kaplan-Meier plot comparing survival of three colon-cancer treatment arms. The Levamisole-plus-fluorouracil curve sits clearly above the Observation and Levamisole-alone curves, which overlap each other; each curve has a shaded 95% confidence band, the overall log-rank p-value of 0.003 is printed on the plot, and a risk table beneath shows the number of patients still at risk in each arm over time.

Le graphique raconte la même histoire que la matrice : la courbe Lev+5FU se situe nettement au-dessus des deux autres, qui se chevauchent presque exactement — la confirmation visuelle que la combinaison est le seul bras qui diffère. La p = 0.003 globale est sur le panneau ; c’est la matrice par paires qui fixe la différence sur le bon bras.

Rapport

La survie a été comparée entre les trois bras de traitement (Observation, Levamisole, Levamisole + fluorouracil) avec le test du log-rank sur les données de l’essai sur le cancer du côlon (n = 929, 452 décès). Le test global était significatif (χ²(2) = 11.7, p = 0.003). Les tests du log-rank par paires avec ajustement de Benjamini-Hochberg ont montré que Levamisole + fluorouracil améliorait significativement la survie par rapport à la fois à Observation (p = 0.005) et à Levamisole seul (p = 0.006), tandis qu’Observation et Levamisole seul ne différaient pas (p = 0.81). Les courbes de Kaplan-Meier avec des bandes de confiance à 95 % sont présentées en Figure 1.

Lancez \(m\) tests indépendants, chacun au niveau \(\alpha\). La chance qu’au moins un donne un faux positif sous l’hypothèse nulle est

\[ P_{FWER} = 1 - (1 - \alpha)^{m} \]

qui, pour \(\alpha = 0.05\), croît de 0.05 (\(m=1\)) à 0.14 (\(m=3\)) à 0.40 (\(m=10\)). Deux familles de correctifs :

Taux d’erreur par famille (FWER). Bonferroni compare chaque p-value brute à \(\alpha / m\) (de façon équivalente, multiplie chaque p-value par \(m\)). Holm trie les p-values \(p_{(1)} \le \dots \le p_{(m)}\) et teste \(p_{(i)}\) contre \(\alpha / (m - i + 1)\), s’arrêtant au premier échec — même garantie, plus de rejets.

Taux de fausses découvertes (FDR). Benjamini-Hochberg trie les p-values et trouve le plus grand \(k\) avec \(p_{(k)} \le \frac{k}{m}\,\alpha\), rejetant tous les \(p_{(1)}, \dots, p_{(k)}\). Il contrôle la proportion attendue de faux positifs parmi les rejets, pas la chance d’en avoir un — il rejette donc plus, au prix d’autoriser une fraction contrôlée de fausses découvertes.

Essayez en direct

Lancez les comparaisons par paires vous-même — changez la méthode d’ajustement, ou comparez les courbes d’une autre manière. Le bac à sable démarre au premier Run.

🟢 Avec un agent IA

Demandez à Prova « J’ai trois groupes de traitement et un test du log-rank significatif — lance des comparaisons du log-rank par paires sur mes données avec des p-values ajustées, dis-moi en clair quels bras diffèrent, et trace les courbes » — 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 avez lancé des tests par paires mais sauté le test global. À éviter. Le test du log-rank global est le verrou — s’il n’est pas significatif (p ≥ 0.05), rien ne justifie de chasser des différences par paires, et toute paire « significative » que vous trouvez est probablement fortuite. Lancez survdiff() sur tous les groupes d’abord ; ne passez à pairwise_survdiff() que lorsque le test global est significatif.

Vous avez rapporté les p-values par paires non ajustées. pairwise_survdiff() ajuste toujours (Benjamini-Hochberg par défaut), si bien que la matrice qu’il affiche est déjà corrigée — mais si vous construisez des tests par paires à la main avec survdiff() sur des sous-ensembles, ces p-values brutes ne sont pas ajustées. Passez-les par p.adjust(), et nommez toujours la méthode dans votre rapport.

Votre facteur a un niveau vide ou minuscule. Si rx (ou tout facteur de groupement) porte un niveau inutilisé après le sous-ensemblage, pairwise_survdiff() peut produire des cellules NA ou avertir. Supprimez les niveaux vides avec droplevels(colon_death$rx) avant de tester, et rappelez-vous qu’une paire avec très peu d’événements donne une p-value du log-rank instable quel que soit l’ajustement — vérifiez le nombre d’événements, pas seulement les tailles d’échantillon.

Questions fréquentes

Lancez les tests du log-rank par paires avec pairwise_survdiff(Surv(time, status) ~ group, data, p.adjust.method = "BH") de survminer. Le test du log-rank global vous indique seulement que certains groupes diffèrent ; la fonction par paires teste chaque paire et renvoie une matrice de p-value ajustée pour que vous puissiez lire exactement quels bras diffèrent. Lancez toujours le test global d’abord et ne passez par paires que lorsqu’il est significatif.

Chaque test par paires a un taux de faux positifs de 5 % à lui seul, si bien que lancer plusieurs tests à p < 0.05 gonfle la chance d’un résultat « significatif » fortuit — avec trois comparaisons elle monte à environ 14 %. Ajuster les p-values (Bonferroni, Holm ou Benjamini-Hochberg) ramène le taux d’erreur global aux 5 % que vous visez, ce qui explique pourquoi les revues et les régulateurs attendent des comparaisons de survie par paires corrigées.

Bonferroni et Holm contrôlent le taux d’erreur par famille — la chance de tout faux positif ; Holm est un raffinement descendant toujours au moins aussi puissant que Bonferroni, alors préférez Holm. Benjamini-Hochberg (BH) contrôle le taux de fausses découvertes — la proportion attendue de faux positifs parmi les comparaisons que vous signalez — ce qui donne plus de puissance et convient au criblage exploratoire sur de nombreux groupes. Utilisez Holm pour les comparaisons confirmatoires strictes, BH pour les exploratoires.

Oui. pairwise_survdiff() applique une correction par défaut (Holm) et vous laisse la changer avec p.adjust.method ("BH", "bonferroni", "BY", "none", etc.). La matrice qu’il renvoie correspond aux p-values ajustées — vous ne relancez pas p.adjust() sur sa sortie. Ne réglez p.adjust.method = "none" que si vous voulez délibérément des p-values par paires brutes.

Le test du log-rank par paires donne des p-values, pas des tailles d’effet. Pour quantifier l’écart de chaque bras sous forme de hazard ratio, ajustez un modèle de Cox à risques proportionnels avec le traitement comme facteur (le niveau de référence devient le comparateur), ou ajustez-le sur chaque paire. Les tests du log-rank par paires répondent à quels bras diffèrent ; Cox répond à de combien.

Testez vos connaissances

  1. Lancez-le. Dans la cellule interactive ci-dessous, changez l’ajustement en Bonferroni et relancez les tests par paires. Les mêmes paires restent-elles significatives ? Remplissez le blanc.
  2. Interprétez. Une matrice par paires (ajustée BH) montre A vs B = 0.04, A vs C = 0.002, B vs C = 0.51. En clair, quels groupes diffèrent et lesquels non ?

Remplissez le blanc avec "bonferroni" (une chaîne entre guillemets). Comparez la matrice obtenue à celle issue de la leçon en BH : Bonferroni est plus conservateur, donc ses p-values sont plus grandes — vérifiez si Lev+5FU vs Lev est toujours sous 0.05.

pairwise_survdiff(Surv(time, status) ~ rx, data = colon_death,
                  p.adjust.method = "bonferroni")
#>         Obs    Lev
#> Lev     1.000  -
#> Lev+5FU 0.0048 0.0125
#> Lev+5FU still differs significantly from BOTH Obs (p = 0.005) and Lev (p = 0.013);
#> Obs vs Lev is non-significant (p = 1.0). Same conclusion as BH, just larger p-values.

Pour la question 2 : A vs C diffèrent (p = 0.002) et A vs B diffèrent (p = 0.04, juste sous 0.05), mais B vs C non (p = 0.51). Donc C et B diffèrent tous deux de A mais pas l’un de l’autre — A se démarque.

AstuceVérification rapide

Votre test du log-rank global sur quatre groupes n’est pas significatif (p = 0.21). Devriez-vous lancer des tests du log-rank par paires pour chercher une différence entre deux des bras ?

Non. Un test global non significatif signifie qu’il n’y a aucune preuve que des courbes diffèrent ; lancer des tests par paires ensuite, c’est de la pêche, et toute paire « significative » que vous trouvez est probablement un faux positif — précisément la chose que l’ajustement existe pour éviter. Ne passez aux comparaisons par paires (avec p-values ajustées) qu’après un test global significatif. L’exception est une hypothèse par paires préspécifiée planifiée avant de voir les données, ce qui relève d’une décision de plan différente.

Conclusion

Quand vous comparez trois courbes de survie ou plus, le test du log-rank global n’est que la première étape : il signale qu’une différence existe, pas . Lancez pairwise_survdiff(Surv(time, status) ~ group, data, p.adjust.method = ...) pour tester chaque paire et lire la matrice de p-value ajustée — et lancez toujours le test global d’abord comme verrou. Sur l’essai sur le cancer du côlon, la combinaison Lev+5FU était le seul bras qui améliorait la survie, battant à la fois Observation (p = 0.005) et Levamisole seul (p = 0.006). Choisissez l’ajustement selon l’objectif — Holm pour les comparaisons confirmatoires strictes, Benjamini-Hochberg pour le criblage exploratoire — et nommez-le dans votre rapport. Les tests par paires vous disent quels bras diffèrent ; pour de combien ils diffèrent en hazard ratio, passez au modèle de Cox.

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 = {Test du log-rank par paires en R : quelles courbes
    diffèrent\,?},
  date = {2026-06-25},
  url = {https://www.datanovia.com/learn/biostatistics/survival-analysis/multiple-comparisons},
  langid = {fr}
}
Veuillez citer ce travail comme suit :
“Test du log-rank par paires en R : quelles courbes diffèrent ?” 2026. June 25. https://www.datanovia.com/learn/biostatistics/survival-analysis/multiple-comparisons.