Simulation des données de survie
Transcription
Simulation des données de survie
Simulation des données de survie Michel Cucherat 18 août 2010 La simulation des processus de survie tel qu’on les analysent à l’aide des techniques d’analyse de survie peuvent se simuler de différentes façons. Ces simulations reposent toutes sur le modèle du risque instantané. 1 Simulation de la survenue d’un événement Pour simuler ces processus de survie nous avons besoin d’un outil pouvant simuler la survenu d’un événement ayant une probabilité p de survenir (loi de Bernouilli). Ce dispositif de simulation s’obtient facilement avec un générateur de nombre aléatoire produisant des nombres entre 0 et 1, distribués de façon strictement uniforme entre ces 2 bornes. Pour simuler un évènement de probabilité p il suffit de générer un nombre aléatoire et de regarder si celui-ci est inférieur ou supérieur à p. S’il est inférieur, l’événement survient. Dans le cas contraire il n’y a pas d’événement. Avec cet algorithme la probabilité de survenu de l’événement simulé est bien de p puise que un nombre aléatoire va se situer entre et 0 et p dans p% des cas (par définition). Vérifions cela sur une série de 1000 simulation d’un évènement ayant une probabilité de survenu de 0.2. > > > > p <- 0.2 x <- runif(1000) ev <- x <= p print(sum(ev)/length(ev)) [1] 0.189 > print(table(ev)/length(ev)) ev FALSE TRUE 0.811 0.189 2 Simulation du moment de survenu d’un événement Avec le modèle du risque instantané, un événement à une probabilité h de survenir durant un espace de temps court ∆t, par exemple une journée. Pour faire la simulation, il suffit de simuler chaque jour en commençant par le 1er jour 1 du suivi et de voir si l’évènement survient. Si pour un jour donné, l’événement ne survient pas on passe à la simulation du jour suivant. En fait il est plus simple de simuler tous les jours simultanément et de prendre comme date de survenu de l’évènement le 1er jour où survient un événement. Considérons un événement dont la probabilité de survenu est 0.01 par jour (le risque instantané est de 1% par jour). Le temps de suivi est de 365 jours. > > > + + + + + + > h <- 0.01 e <- runif(365) <= h if (any(e)) { d <- min(which(e)) ev <- T } else { d <- length(e) ev <- F } print(d) [1] 300 > print(ev) [1] TRUE which est une fonction qui ramène les rangs d’un vecteur de booléen où se trouve les valeurs TRUE. L’application de la fonction min sur ce vecteur de rangs ramène donc le numéro (le rang) du premier jour où survient un événement. Si aucun événement ne survient, any(e) est égal à FALSE et donc ce patient est censuré vivant à la date de fin de suivi (length(e) qui est égal à 365). L’encapsulation de tout cela dans une fonction permet de simuler le suivi d’un patient. > suivi.patient <- function(h, t.max = 365) { + e <- runif(t.max) <= h + if (any(e)) { + d <- min(which(e)) + ev <- 1 + } + else { + d <- t.max + ev <- 0 + } + c(d, ev) + } > replicate(5, suivi.patient(0.01)) [1,] [2,] [,1] [,2] [,3] [,4] [,5] 35 168 95 41 45 1 1 1 1 1 L’analyse des courbes de survie se fait à l’aide du package survival dans R. 2 > > > > > > library(survival) d <- replicate(80, suivi.patient(0.01)) t <- d[1, ] ev <- d[2, ] r <- survfit(Surv(t, ev) ~ 1) print(r) Call: survfit(formula = Surv(t, ev) ~ 1) records 80 n.max n.start 80 80 events 79 median 0.95LCL 0.95UCL 69 52 89 0.0 0.2 0.4 survie 0.6 0.8 1.0 > plot(r, xlab = "temps (jours)", ylab = "survie") 0 50 100 150 200 250 300 350 temps (jours) Et voici la simulation de la survie de 80 patients dont le risque instantané est de 0.01. 3 Faisons appel à la théorie Dans la section précédente nous effectuons une simulation de bas niveau, se situant au plus près du modèle. Le temps de calcul est important puisque chaque jours du suivi de chaque patient est simulé. La connaissance de la théorie permet de réduire dramatiquement ce temps de calcul. En effet, quand le risque instantanée est constant h(t) = cte, les temps de survenu sont distribué suivant une loi exponentielle. la simulation devient alors > t <- rexp(80, 0.01) > ev <- t < 365 3 > t[!ev] <- 365 > r <- survfit(Surv(t, ev) ~ 1) > print(r) Call: survfit(formula = Surv(t, ev) ~ 1) records 80.0 n.max n.start 80.0 80.0 events 75.0 median 0.95LCL 0.95UCL 87.1 74.3 101.4 0.0 0.2 0.4 survie 0.6 0.8 1.0 > plot(r, xlab = "temps (jours)", ylab = "survie") 0 50 100 150 200 250 300 350 temps (jours) Même résultat mais un besoin en calcul bien inférieur. Cela illustre parfaitement un principe de la simulation : inutile de simuler le plus bas niveau si cela n’apporte rien par rapport à la simulation de propriété parfaitement connue par la théorie. ici inutile de simuler chaque jours étant donné que l’on connaı̂t parfaitement la distribution des temps de survie dans cette situation où le hazard est constant. 4 Simulation d’un effet thérapeutique sur la survie A partir de ces éléments il est facile de simuler un essai clinique où le traitement se caractérise par un hr de 0.6 avec un hazard sans traitement de 1%. > n.group <- 80 > h0 <- 0.01 4 > > > + > > > > > > hr <- 0.6 h1 <- h0 * hr trt <- c(rep("studied treatment", n.group), rep("control treatment", n.group)) trt <- factor(trt) t <- c(rexp(n.group, h1), rexp(n.group, h0)) ev <- t < 365 t[!ev] <- 365 r <- survfit(Surv(t, ev) ~ trt) print(r) Call: survfit(formula = Surv(t, ev) ~ trt) trt=control treatment trt=studied treatment records n.max n.start events median 0.95LCL 0.95UCL 80 80 80 74 64.1 43.0 105 80 80 80 76 101.4 71.6 123 1.0 > plot(r, xlab = "temps (jours)", ylab = "survie", col = c(1, 2), + lty = c(1, 2)) > legend("topright", levels(trt), col = 1:2, lty = 1:2, bty = "n") 0.0 0.2 0.4 survie 0.6 0.8 control treatment studied treatment 0 50 100 150 200 250 300 350 temps (jours) Il est aussi possible de calculer le logrank et d’estimer le hazard ratio observé à l’aide d’un modèle de cox. > r2 <- survdiff(Surv(t, ev) ~ trt) > print(r2) Call: survdiff(formula = Surv(t, ev) ~ trt) 5 N Observed Expected (O-E)^2/E (O-E)^2/V trt=control treatment 80 78 71.1 0.666 1.27 trt=studied treatment 80 73 79.9 0.593 1.27 Chisq= 1.3 on 1 degrees of freedom, p= 0.261 > r3 <- coxph(Surv(t, ev) ~ trt) > print(r3) Call: coxph(formula = Surv(t, ev) ~ trt) coef exp(coef) se(coef) z p trtstudied treatment -0.183 0.832 0.163 -1.12 0.26 Likelihood ratio test=1.26 5 on 1 df, p=0.261 n= 160 Etude par simulation du processus d’estimation du hazard ratio par un cox Pour cela il faut construire une fonction qui simule un essai à partir d’un vrai hazard ratio et qui retourne le hazard ratio estimé par le Cox à partir des données simulées > simuler.essai <- function(hr, h0 = 0.01, n.group = 80, t.max = 365) { + n.group <- 80 + h0 <- 0.01 + hr <- 0.6 + h1 <- h0 * hr + trt <- c(rep(1, n.group), rep(0, n.group)) + trt <- factor(trt) + t <- c(rexp(n.group, h1), rexp(n.group, h0)) + ev <- t < t.max + t[!ev] <- t.max + r <- coxph(Surv(t, ev) ~ trt) + hr <- exp(r$coefficients) + hr + } > simuler.essai(0.5, 0.01, 80) trt1 0.7662047 En répliquant la simulation d’essais on peut ainsi vérifier que le modèle de Cox estime sans biais le hazard ratio dans le cas d’un hazard ratio constant. > hr.true <- 0.6 > hr <- replicate(1000, simuler.essai(hr.true, 0.01, 80)) > print(mean(hr)) 6 [1] 0.6010576 > biais <- hr.true - mean(hr) > print(biais) [1] -0.001057600 > print(summary(hr)) Min. 1st Qu. 0.2526 0.5285 Median 0.5907 Mean 3rd Qu. 0.6011 0.6640 1.0 > boxplot(hr) ● ● 0.4 0.6 0.8 ● ● ● ● ● ● ● ● 7 Max. 1.0210