ggsurvplot dans R : courbes de survie prêtes à publier
Construisez une figure Kaplan-Meier prête pour une revue, étape par étape — table des effectifs à risque, bandes de confiance, p-value sur le graphique, lignes de survie médiane, couleurs et légende personnalisées, facettes, combinaison de courbes et export
Une référence survminer pratique pour ggsurvplot() : partez d’une courbe de survie de base et ajoutez, un élément à la fois, la table des effectifs à risque, les bandes de confiance à 95 %, la p-value du log-rank sur le graphique, les lignes de survie médiane, les marques de censure, une palette de revue adaptée au daltonisme, une légende personnalisée, des axes et un titre, le facettage par une covariable, la combinaison de plusieurs courbes et l’export de la figure à la bonne taille — sur les données de cancer du poumon NCCTG.
Date de publication
25 juin 2026
Modifié
7 juillet 2026
AstucePoints clés
ggsurvplot() (du package survminer, conçu pour cela) transforme un objet survfit en une figure Kaplan-Meier prête à publier — ne vous contentez jamais d’un simple plot().
Construisez la figure un élément à la fois : risk.table = TRUE (effectifs à risque), conf.int = TRUE (bandes de confiance), pval = TRUE (p-value du log-rank sur le graphique), surv.median.line = "hv" (lignes médianes).
Rendez-la prête pour une revue : palette = "jco"/"npg" (adaptée au daltonisme), legend.labs/legend.title, xlab/ylab/title, break.time.by et ggtheme = theme_minimal().
Comparez entre sous-groupes avec ggsurvplot_facet() (un panneau par covariable) et superposez des critères (par ex. OS vs PFS) avec ggsurvplot_combine().
Exportez à la bonne taille avec ggsave() — un ggsurvplot est une liste, donc enregistrez print(p) (ou arrange_ggsurvplots() pour plusieurs panneaux) à 300 dpi pour l’impression.
Introduction
Vous avez une courbe Kaplan-Meier et vous voulez qu’elle ressemble aux figures de The Lancet ou de JCO — des paliers de survie, une bande de confiance à 95 %, les effectifs à risque sous le graphique, la p-value du log-rank là où un relecteur s’y attend, et des couleurs qui survivent à une imprimante en niveaux de gris. Le plot(survfit(...)) de R de base ne vous donne rien de tout cela sans une lutte.
Le ggsurvplot() de survminer a été écrit exactement pour ce travail (c’est le package que le domaine utilise, et celui que nous maintenons). Ceci est une référence de recettes : partez de la courbe de survie la plus simple et ajoutez chaque élément de publication tour à tour — table des effectifs à risque, intervalles de confiance, p-value, lignes médianes, marques de censure, palette, légende, axes, facettage, combinaison de courbes et export. Chaque section est un paragraphe, le code, et la figure qu’il produit. Copiez ce dont vous avez besoin.
Si vous estimez des courbes pour la première fois, lisez d’abord L’estimation de Kaplan-Meier ; cette leçon suppose que vous avez déjà un objet survfit et se concentre entièrement sur le graphique.
Les données et l’ajustement
Chaque recette ci-dessous trace le même objet survfit, alors construisez-le une seule fois. Nous utilisons lung (NCCTG, livré avec le package survival) — la survie par sexe chez 228 patients atteints d’un cancer du poumon avancé. Le jeu de données s’attache automatiquement avec survival, donc référencez lung directement (pas d’appel data()) :
library(survival)fit <-survfit(Surv(time, status) ~ sex, data = lung)fit
Call: survfit(formula = Surv(time, status) ~ sex, data = lung)
n events median 0.95LCL 0.95UCL
sex=1 138 112 270 212 310
sex=2 90 53 426 348 550
Deux strates (sex=1 homme, sex=2 femme), avec leur survie médiane et leurs IC à 95 %. Chaque bloc {r} ci-dessous recrée fit pour que vous puissiez en copier n’importe lequel et l’exécuter seul.
Étape 1 — une courbe de survie de base
ggsurvplot(fit, data) est le minimum. Passez l’objet survfitet les données sur lesquelles vous l’avez ajusté (survminer a besoin des données pour construire la table des effectifs à risque plus tard) :
library(survival)library(survminer)fit <-survfit(Surv(time, status) ~ sex, data = lung)ggsurvplot(fit, data = lung)
Déjà une figure ggplot2 propre : deux courbes colorées, des marques de censure, une légende. Tout ce qui suit s’ajoute à cet appel.
Étape 2 — la table des effectifs à risque
L’élément le plus attendu dans une figure de survie clinique. risk.table = TRUE dessine une table sous les courbes montrant combien de patients sont encore à risque à chaque instant — elle indique au lecteur la quantité de données qui soutient la queue (là où les courbes deviennent peu fiables). Contrôlez sa hauteur avec risk.table.height :
library(survival)library(survminer)fit <-survfit(Surv(time, status) ~ sex, data = lung)ggsurvplot( fit, data = lung,risk.table =TRUE, # number at risk under the plotrisk.table.height =0.25# 25% of the figure height)
Pour une table plus propre, ajoutez tables.theme = theme_cleantable() et risk.table.y.text = FALSE (affiche une barre colorée au lieu de répéter les noms des strates). Pour combiner les comptes, risk.table = "nrisk_cumcensor" affiche les effectifs à risque et le cumul des censurés dans une seule table.
Étape 3 — les bandes de confiance
conf.int = TRUE ombre l’intervalle de confiance à 95 % autour de chaque courbe — exigé par la plupart des revues. Par défaut, c’est un ruban plein ; conf.int.style = "step" dessine plutôt des bornes en escalier (parfois plus claires lorsque les bandes se chevauchent) :
library(survival)library(survminer)fit <-survfit(Surv(time, status) ~ sex, data = lung)ggsurvplot( fit, data = lung,risk.table =TRUE,conf.int =TRUE, # 95% confidence bandsconf.int.style ="ribbon"# or "step")
Remarquez que les bandes s’évasent à l’extrémité droite — peu de patients restent à risque tardivement, donc la queue est incertaine. C’est le rôle de la table des effectifs à risque de l’exposer.
Étape 4 — la p-value du log-rank sur le graphique
pval = TRUE exécute le test du log-rank comparant les strates et affiche la p-value sur le graphique — pas d’appel de test séparé, pas de copier-coller. Placez-la avec pval.coord et nommez le test avec pval.method = TRUE :
library(survival)library(survminer)fit <-survfit(Surv(time, status) ~ sex, data = lung)ggsurvplot( fit, data = lung,risk.table =TRUE,conf.int =TRUE,pval =TRUE, # log-rank p-value on the plotpval.method =TRUE, # also print the test namepval.coord =c(750, 0.85) # x, y position of the p-value)
Vous pouvez aussi passer à pval une valeur ou une chaîne pour un contrôle total — pval = "Log-rank, p < 0.001" — ou changer la pondération (log.rank.weights = "sqrtN" pour Tarone-Ware, qui est plus sensible aux différences précoces).
Étape 5 — les lignes de survie médiane
surv.median.line = "hv" dessine les guides en pointillés horizontaux-puis-verticaux qui descendent de la ligne de survie à 0.5 jusqu’à la survie médiane de chaque groupe sur l’axe des x — le chiffre phare, lu directement sur la figure ("h" horizontal seulement, "v" vertical seulement) :
library(survival)library(survminer)fit <-survfit(Surv(time, status) ~ sex, data = lung)ggsurvplot( fit, data = lung,risk.table =TRUE,conf.int =TRUE,pval =TRUE,surv.median.line ="hv"# dashed median-survival guides)
Les lignes verticales rencontrent l’axe des x à 270 jours (hommes) et 426 jours (femmes) — les médianes de fit, désormais visibles sur le graphique.
Étape 6 — les marques de censure
Les patients censurés (vivants au dernier suivi) apparaissent comme de petites marques sur les courbes par défaut — ne les confondez pas avec des événements. Basculez-les avec censor, et changez leur apparence avec censor.shape et censor.size :
library(survival)library(survminer)fit <-survfit(Surv(time, status) ~ sex, data = lung)# Custom censoring marksggsurvplot( fit, data = lung,censor.shape ="|", censor.size =4# vertical-bar ticks)
# Hide them entirely (useful when overlaying many curves)ggsurvplot(fit, data = lung, censor =FALSE)
Gardez les marques de censure pour une figure unique (elles sont informatives) ; ne les masquez que lorsque de nombreuses courbes qui se chevauchent rendent le graphique chargé.
Étape 7 — des palettes de revue adaptées au daltonisme
palette = "jco" applique le schéma de couleurs du Journal of Clinical Oncology issu de ggsci — adapté au daltonisme et instantanément crédible pour une revue. Autres palettes nommées : "npg" (Nature), "lancet", "nejm", "aaas". Vous pouvez aussi passer "grey", un nom RColorBrewer ("Dark2"), ou votre propre vecteur hexadécimal :
library(survival)library(survminer)fit <-survfit(Surv(time, status) ~ sex, data = lung)# JCO journal palette (colourblind-safe)ggsurvplot(fit, data = lung, conf.int =TRUE, palette ="jco")
# Nature Publishing Group paletteggsurvplot(fit, data = lung, conf.int =TRUE, palette ="npg")
# Custom colours work too: palette = c("#3a86d4", "#E7B800")
Utilisez une palette de revue nommée pour les groupes ; choisissez-en une et restez cohérent entre les figures d’un article.
Étape 8 — légende, titre et étiquettes d’axes
Remplacez les noms bruts des strates (sex=1, sex=2) par des étiquettes lisibles, déplacez la légende, et étiquetez les axes et l’échelle de temps. Les arguments : legend.labs (dans l’ordre des strates), legend.title, legend (position), xlab/ylab/title, et break.time.by (intervalle des graduations de l’axe) :
library(survival)library(survminer)fit <-survfit(Surv(time, status) ~ sex, data = lung)ggsurvplot( fit, data = lung,risk.table =TRUE,conf.int =TRUE,pval =TRUE,legend.labs =c("Male", "Female"), # in strata orderlegend.title ="Sex",legend ="top", # "top","bottom","left","right", or c(x,y)title ="Overall survival by sex",xlab ="Time (days)",ylab ="Survival probability",break.time.by =200# x-axis tick every 200 days)
Pour raccourcir une queue peu informative, ajoutez xlim = c(0, 800) — cela recadre l’axe sans changer les estimations de survie. Utilisez surv.scale = "percent" pour un axe des y de 0 à 100 %.
Vues transformées : incidence cumulée et risque cumulé
Par défaut, ggsurvplot() trace la probabilité de survie S(t). Inversez l’axe des y avec fun = "event" pour afficher l’incidence cumulée (1 − S(t)) — la vue naturelle lorsque la question est « combien ont subi l’événement avant l’instant t » plutôt que « combien survivent encore » — et fun = "cumhaz" pour le risque cumulé, une vue diagnostique (une ligne à peu près droite suggère un risque constant) :
library(survival)library(survminer)fit <-survfit(Surv(time, status) ~ sex, data = lung)# Cumulative incidence: 1 - S(t), the curves now RISE from 0ggsurvplot(fit, data = lung, fun ="event", conf.int =TRUE, palette ="jco")
# Cumulative hazard (diagnostic view): fun = "cumhaz"
Même ajustement, autre angle : fun = "event" répond à la question de l’incidence pour un lecteur clinique, tandis que fun = "cumhaz" sert à vérifier la forme du risque. Tout le reste (table des effectifs à risque, pval, palette) s’applique encore par-dessus l’axe transformé.
Étape 9 — la figure prête à publier
Assemblez la recette. C’est la figure que vous collez dans un article — table des effectifs à risque, bandes de confiance, p-value, lignes médianes, palette de revue, étiquettes propres, et theme_minimal() :
Chaque élément est justifié : la table des effectifs à risque montre les données derrière la queue, la bande montre l’incertitude, la p-value répond à « diffèrent-ils ? », les lignes médianes donnent le chiffre phare, et la palette est adaptée au daltonisme. Rien de décoratif.
Facettage : un panneau par sous-groupe
Pour comparer la survie au sein des niveaux d’une autre variable, ggsurvplot_facet() (ou facet.by = dans ggsurvplot) dessine une figure multi-panneaux — un Kaplan-Meier par sous-groupe, chacun avec sa propre p-value du log-rank. Ici, la survie par sexe, facettée par traitement (rx) sur le jeu de données colon :
library(survival)library(survminer)fit_colon <-survfit(Surv(time, status) ~ sex, data = colon)ggsurvplot_facet( fit_colon, data = colon,facet.by ="rx", # one panel per treatmentpalette ="jco",pval =TRUE,legend.title ="Sex",legend.labs =c("Male", "Female"),ggtheme =theme_light())
Facettez par deux variables avec facet.by = c("rx", "adhere") pour une grille. Note : ggsurvplot_facet() retourne un ggplot simple, donc il ne porte pas de table des effectifs à risque (c’est le compromis de la disposition en grille).
Combiner des courbes : deux critères sur un graphique
Pour superposer des objets survfit séparés — classiquement la survie globale (OS) vs la survie sans progression (PFS) — utilisez ggsurvplot_combine() avec une liste nommée d’ajustements. Ici, deux critères construits à partir de colon (PFS simulée pour l’illustration) :
library(survival)library(survminer)set.seed(123)demo <-data.frame(os.time = colon$time,os.status = colon$status,pfs.time =sample(colon$time),pfs.status = colon$status)os <-survfit(Surv(os.time, os.status) ~1, data = demo)pfs <-survfit(Surv(pfs.time, pfs.status) ~1, data = demo)ggsurvplot_combine(list(OS = os, PFS = pfs), # named list → legend labelsdata = demo,risk.table =TRUE,tables.theme =theme_cleantable(),censor =FALSE,palette ="jco")
Les clés de la liste nommée (OS, PFS) deviennent les étiquettes de la légende. Utilisez ceci chaque fois que deux courbes proviennent de réponses Surv() différentes plutôt que de groupes d’un seul ajustement.
Exporter la figure
Un objet ggsurvplot est une liste ($plot, $table, …), pas un ggplot unique — donc ggsave() sur l’objet brut peut perdre la table des effectifs à risque. Enregistrez print(p) à la taille et à la résolution dont vous avez besoin (300 dpi pour l’impression) ; pour exporter la courbe avec sa table des effectifs à risque mise en page, enveloppez un seul graphique dans arrange_ggsurvplots() :
library(survival)library(survminer)fit <-survfit(Surv(time, status) ~ sex, data = lung)p <-ggsurvplot(fit, data = lung, risk.table =TRUE, conf.int =TRUE,pval =TRUE, palette ="jco")# Curve + risk table, sized for a journal column, 300 dpiggsave("survival_curve.png",plot =arrange_ggsurvplots(list(p), print =FALSE),width =7, height =6.5, dpi =300)# PDF (vector) for submissionggsave("survival_curve.pdf",plot =arrange_ggsurvplots(list(p), print =FALSE),width =7, height =6.5)
Réglez width/height en pouces pour contrôler le rapport d’aspect ; exportez en PDF (vectoriel) pour les revues qui le préfèrent, en PNG/TIFF à ≥300 dpi pour les soumissions matricielles.
Essayez en direct
Construisez votre propre figure de publication — changez la palette, les étiquettes de la légende, ou ce que vous facettez. Le bac à sable démarre au premier Run.
🟢 Avec un agent IA
Demandez à Prova« construis un ggsurvplot prêt à publier sur mes données de survie — table des effectifs à risque, bandes de confiance, la p-value du log-rank, des lignes médianes, une palette de revue adaptée au daltonisme, et exporte-le à 300 dpi » — elle répond avec du code survminer que vous pouvez exécuter sur vos propres données. The runtime is the judge.Demander à Prova →
Problèmes courants
La table des effectifs à risque est mal alignée ou tronquée. La table partage l’axe des x avec la courbe, donc son alignement se casse si vous recadrez avec xlim après coup ou si vous fixez une hauteur de figure maladroite. Utilisez xlim =à l’intérieur de ggsurvplot() (pour que les deux panneaux se recadrent ensemble), donnez de la place à la table avec risk.table.height = 0.25–0.3, et rangez-la avec tables.theme = theme_cleantable() et risk.table.y.text = FALSE.
Vos étiquettes de légende sont dans le mauvais ordre, ou les mauvais groupes reçoivent la mauvaise couleur.legend.labs suit l’ordre des strates dans l’ajustement, pas l’ordre dans lequel vous les écririez. Affichez fit (ou names(fit$strata)) pour voir l’ordre — pour ~ sex, c’est sex=1 (homme) puis sex=2 (femme). Si les étiquettes semblent inversées, définissez la variable de regroupement comme un factor avec des niveaux explicites avantsurvfit(), puis étiquetez dans cet ordre.
La figure exportée perd la table des effectifs à risque ou ressort floue.ggsave() sur l’objet ggsurvplot brut n’enregistre que $plot. Enregistrez print(p), ou enveloppez-le dans arrange_ggsurvplots(list(p), print = FALSE) pour garder la table, et réglez dpi = 300 (matriciel) ou exportez en PDF (vectoriel) — ne capturez jamais d’écran du volet graphique.
Questions fréquentes
NoteComment ajouter une table des effectifs à risque à une courbe de survie dans R ?
Ajoutez risk.table = TRUE à ggsurvplot(). Cela dessine une table des effectifs à risque sous les courbes. Contrôlez sa taille avec risk.table.height = 0.25, nettoyez-la avec tables.theme = theme_cleantable(), et masquez les noms de strates répétés avec risk.table.y.text = FALSE.
NoteComment changer les couleurs d’un ggsurvplot ?
Utilisez l’argument palette : un nom de revue (palette = "jco", "npg", "lancet", "nejm"), un nom RColorBrewer ("Dark2"), "grey", ou votre propre vecteur hexadécimal (palette = c("#3a86d4", "#E7B800")). Les palettes de revue nommées sont adaptées au daltonisme — privilégiez-les pour les publications.
NoteComment mettre la p-value sur le graphique de survie ?
Ajoutez pval = TRUE — ggsurvplot() exécute le test du log-rank et affiche la p-value sur le graphique. Positionnez-la avec pval.coord = c(x, y), affichez le nom du test avec pval.method = TRUE, ou passez votre propre texte (pval = "Log-rank, p < 0.001").
NoteComment ajouter les effectifs à risque sous un graphique Kaplan-Meier ?
C’est précisément la table des effectifs à risque : réglez risk.table = TRUE. Pour la combiner avec le cumul des censurés ou le cumul des événements dans une seule table, utilisez risk.table = "nrisk_cumcensor" ou "nrisk_cumevents".
NoteComment tracer des courbes de survie côte à côte pour des sous-groupes ?
Utilisez ggsurvplot_facet(fit, data, facet.by = "group") (ou facet.by = dans ggsurvplot) pour un panneau par sous-groupe, chacun avec sa propre p-value du log-rank. Pour superposer des courbes issues de réponses Surv() différentes (par ex. OS vs PFS), utilisez plutôt ggsurvplot_combine() avec une liste nommée d’ajustements.
NoteComment enregistrer un ggsurvplot dans une image haute résolution ?
Un ggsurvplot est une liste, donc ggsave() sur lui seul perd la table des effectifs à risque. Enregistrez print(p), ou enveloppez-le avec arrange_ggsurvplots(list(p), print = FALSE) pour garder la table, puis ggsave("fig.png", plot = ..., width = 7, height = 6.5, dpi = 300). Exportez en PDF pour les soumissions de revue au format vectoriel.
Testez vos connaissances
ImportantPratique
Construisez-la. Dans la cellule interactive ci-dessous, tracez une courbe de survie par traitement sur le jeu de données colon (Surv(time, status) ~ rx) avec une table des effectifs à risque, des bandes de confiance, la p-value du log-rank, et la palette npg. Remplissez les blancs.
Interprétez. Un relecteur vous demande de « montrer combien de patients sont encore à risque au fil du temps » et de « rendre les couleurs adaptées au daltonisme ». Quels deux arguments de ggsurvplot() changez-vous, et en quoi ?
NoteIndice
risk.table et pval prennent TRUE. Le nom de la palette Nature est "npg". Les strates sont les trois niveaux de rx, donc la légende affichera trois courbes.
NoteSolution
fit <-survfit(Surv(time, status) ~ rx, data = colon)ggsurvplot( fit, data = colon,risk.table =TRUE,conf.int =TRUE,pval =TRUE,palette ="npg")
Pour la question 2 : ajoutez risk.table = TRUE (la table des effectifs à risque répond à « combien sont encore à risque »), et réglez palette = "jco" (ou "npg"/"lancet") — les palettes de revue ggsci sont adaptées au daltonisme, contrairement à un vecteur de couleurs arbitraire.
AstuceVérification rapide
Pourquoi enregistrer un ggsurvplot avec ggsave() directement perd-il souvent la table des effectifs à risque ?
NoteAfficher la réponse
Parce que ggsurvplot() retourne une liste d’objets ggplot ($plot, $table, $cumevents, …), pas un ggplot unique. ggsave() n’enregistre que le $plot principal. Enregistrez print(p) pour rendre la figure mise en page, ou enveloppez-le dans arrange_ggsurvplots(list(p), print = FALSE) pour exporter la courbe avec sa table des effectifs à risque.
Conclusion
Vous pouvez désormais construire une courbe de survie prête à publier dans R avec ggsurvplot(), un élément à la fois : la table des effectifs à risque (risk.table), les bandes de confiance (conf.int), la p-value du log-rank (pval), les lignes médianes (surv.median.line), les marques de censure, une palette de revue adaptée au daltonisme (palette), et une légende, un titre et des axes propres. Pour les comparaisons entre sous-groupes, ggsurvplot_facet() donne un panneau par niveau ; pour superposer des critères comme OS et PFS, ggsurvplot_combine() les met sur un même graphique. Enfin, exportez avec ggsave(print(p), ..., dpi = 300) — ou arrange_ggsurvplots() pour garder la table des effectifs à risque — pour une figure qui s’insère directement dans un article.
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.
Chaque figure de cette page a été produite par le code montré, exécuté au moment du build dans un environnement R figé — modifiez n’importe quel bloc et faites Run pour la reproduire vous-même.
@online{2026,
author = {},
title = {ggsurvplot dans R : courbes de survie prêtes à publier},
date = {2026-06-25},
url = {https://www.datanovia.com/learn/biostatistics/survival-analysis/publication-ready-survival-curves},
langid = {fr}
}