Participants in a study often not all enter on the same day, such that their dates of randomization differ. This leads to one of two phenomena in the statistical analysis that we – following the literature – will call ‘left-truncation’ and ‘staggered entry’. Whether our analysis has to deal with either of the two depends on the chosen time scale most relevant to the occurrence of the events.
On the one hand, time-to-event can be calendar time, e.g. time to an infection that occurs in (epidemic) waves. All participants in a risk set share a hazard if they are in follow-up and event-free on the same calendar date, such that late entry occurs as left-truncated event times. Left-truncation means that participants only enter the risk set once they enter the study but have already ‘survived’ some calendar time that might have observed an event for other participants. Nevertheless, they should not be part of the risk set to evaluate events that happened before they entered, since we know that an event before entry is impossible, e.g. because being alive or more general event-free is an inclusion criterion for study enrollment.
On the other hand, time-to-event can be participant time, specific to each participant, e.g. time since surgery. All participants in a risk set of an event share a hazard if they are in follow-up and event-free for the same time since their own specific date of enrollment/randomization/intervention, such that late entry occurs as ‘staggered entry’. Staggered entry means that participants that enter late could still enter the risk set of events that happened earlier, for events of participants that had the same participant time since their own date of intervention, as the late entered participant experienced since its date of intervention.
Hence in a ‘left-truncation’ analysis, participants that enter late can only enter the risk set of events that happen after (in calendar time) they enter the study, while in ‘staggered entry’ analysis, participants that enter late can enter the risk set of events that already happened. We provide a tutorial on each scenario to illustrate the difference and the right statistical analysis for the sequential Safe logrank test. This is the tutorial on staggered entry. You can find the tutorial on left-truncation here.
In case of sequential analysis under staggered entry we do need to take two types of time into account. We consider the time since enrollment/randomization/intervention (participant time) as the time scale, while calibrating for the calendar time of the sequential stages of the analysis. The main thing for an analysis of a data set per calendar time is that it needs to be aware that participants not yet included in the study can also not provide (censored) information. This tutorial illustrates how to process a time-to-event data set into a sequence of logrank e-values per calendar date that takes both types of time into account.
Please also find the complete R markdown file here.
library(devtools)
devtools::install_github("AlexanderLyNL/Safestats", ref = "logrank")
library(safestats)
library(survival)
library(knitr)
Consider the following small data set:
enrollment <- 10 # 5 treatment, 5 placebo, so:
ratio <- 1 # ratio = nT/nP = 1
fup <- 40 # folow up of 40 days
nEventsC <- 8 # 8 events in 40 days among 10
# this defines the baseline hazard
hr1 <- 0.5 # hazard ratio between treatment en placebo group
# (hr1: alternative hypothesis is true)
# 6 events anticipated within 40 days
# if treatment reduces risk with hr1:
anticipNevents <- nEventsC*(1/(1 + ratio)) +
nEventsC*(ratio/(1 + ratio))*hr1
# assuming constant hazard, we simulate the following baseline hazard and data:
lambdaC <- 1 - (1 - (nEventsC/enrollment))^(1/fup)
data <- generateSurvData(nP = enrollment*(1/(1 + ratio)),
nT = enrollment*(ratio/(1 + ratio)),
lambdaP = lambdaC,
lambdaT = hr1*lambdaC,
endTime = fup,
seed = 2006) # set seed to make the result reproducible
time | status | group |
---|---|---|
4 | 2 | P |
4 | 2 | P |
13 | 2 | T |
18 | 2 | P |
19 | 2 | T |
24 | 2 | T |
40 | 2 | P |
40 | 1 | P |
40 | 1 | T |
40 | 1 | T |
We see events occurring in either the treatment (T
) or the placebo control group (P
) at days after randomization (participant time) d = 4, 13, 18, 19, 24, 40,...
(events have status = 2
, censoring has status = 1
). Our event times now do not have a calendar date yet, and the Kaplan-Meier plot above gives the survival by participant time.
Now suppose that it took us two weeks to enroll and randomize everyone. We started on dateRand.start = 2020-05-04
and finished on dateRand.end = 2020-05-15
. So we assign our participants a random start date and order them by date of enrollment/randomization:
set.seed(2005)
data$"dateRand" <- sample(seq.Date(from = dateRand.start, to = dateRand.end, by = "day"),
size = enrollment, replace = TRUE)
data$"dateEvent/LastFup" <- as.Date(data$"dateRand" + data$"time")
data$"dateLastFup" <- as.Date("2020-06-15")
# Order the participants by date of enrollment/randomization:
data$"participantID" <- 1:nrow(data)
data$"participantID"[order(data$"dateRand")] <- 1:nrow(data)
data <- data[order(data$"dateRand"), ]
calDate <- sort(data$"dateEvent/LastFup")[1]
d <- data$"time"[data$"dateEvent/LastFup" == calDate]
We do our first interim analysis at calDate = 2020-05-08
. We assume that everyone who is randomized on exactly 2020-05-08, is only at risk starting from the next day, so on this exact calendar day we have 5 participants at risk of event, 3 in the placebo control group (grey) and 2 in the treatment group (green).
But to judge the event occurring at 2020-05-08, participant time d = 4
days after randomization, we should only consider the participants that are in follow-up for at least 4 days, such that they have a participant time of 4 days and should have been randomized at least d = 4
days ago (on 2020-05-04). That means we have only 2 participants at risk at this first event (\(N_1 = 2\)), 1 in the placebo control group and 1 in the treatment group (\(N_{T, 1} = 1\)).
Our first logrank statistic on 2020-05-08 contains 1 event (\(O_1 = 1\)), which is an event in the placebo control group, so the number of events in the treatment group is 0 (\(O_{T, 1} = 0\)). The expected number of events in the treatment group is \(E_{T, 1} = N_{T, 1} \cdot \frac{O_1}{N_1} = 1 \cdot \frac{1}{2} = \frac{1}{2}\).
So our logrank \(Z\)-score at this first event is:
\[\begin{equation} Z^{(1)} = \frac{O_{T, 1} - E_{T, 1}}{\sqrt{V_{T, 1}}} = \frac{0 - \frac{1}{2}}{\sqrt{\frac{1}{2} \cdot (1 - \frac{1}{2})}} = -1 \end{equation}\]
with \(V_{T, 1}\) the variance of the Bernoulli distribution with probability \(E_{T, 1}\):
\[\begin{equation} V_{T, 1} = E_{T, 1} \cdot (1 - E_{T, 1}) = \frac{1}{2} \cdot (1 - \frac{1}{2}) \end{equation}\]
Our Safe logrank test should consider this exact logrank statistic for the first event at participant time d = 4
and calendar date calDate = 2020-05-08
, so we need to tell the function that we have not yet observed participants or events randomized at a calender date later than 2020-05-08. We define the dataSoFar
as follows:
dataSoFar <- data[data$"dateRand" < calDate, ]
dataSoFar$"dateLastFup" <- calDate
# We do not yet know the event times in the future,
# only that these participants are in follow-up until this date
dataSoFar$"time" <- pmin(dataSoFar$"time", calDate - dataSoFar$"dateRand")
# The status of future event times is censored for now:
dataSoFar$"status"[dataSoFar$"dateEvent/LastFup" > calDate] <- 1
# And we can assign the following survival object
dataSoFar$"survObj" <- Surv(dataSoFar$"time", dataSoFar$"status")
kable(dataSoFar, row.names = F)
time | status | group | dateRand | dateEvent/LastFup | dateLastFup | participantID | survObj |
---|---|---|---|---|---|---|---|
4 | 2 | P | 2020-05-04 | 2020-05-08 | 2020-05-08 | 1 | 4 |
4 | 1 | T | 2020-05-04 | 2020-06-13 | 2020-05-08 | 2 | 4+ |
2 | 1 | T | 2020-05-06 | 2020-05-25 | 2020-05-08 | 3 | 2+ |
1 | 1 | P | 2020-05-07 | 2020-05-11 | 2020-05-08 | 4 | 1+ |
1 | 1 | P | 2020-05-07 | 2020-05-25 | 2020-05-08 | 5 | 1+ |
The third and fourth line in the above code redefine the participant time as we know them on calDate = 2020-05-08
. 3 out of the 5 participants in the data set are in follow-up for a shorter time than participant time d = 4
, so by redefining their time
and status
variable, the logrank test does not take them into account to specify the risk set for our first event. So we can calculate our logrank test based on this calDate = 2020-05-08
version of our temporary data set dataSoFar
:
safeLogrankTest(exact = FALSE, # to get the approximate safe logrank test
designObj = designObjL, # (based on the logrank Z-statistic)
dataSoFar$"survObj" ~ dataSoFar$"group")
##
## Safe Logrank Test
##
## data: dataSoFar$survObj by dataSoFar$group (P, T). nEvents = 1
## estimates: hazard ratio = 0.13534
##
## test: z = -1, log(thetaS) = -0.35667
## e-value = 1.1764 > 1/alpha = 20 : FALSE
## alternative hypothesis: true hazard ratio is less than 1
##
## design: the test was designed with alpha = 0.05
## for minimal relevant hazard ratio = 0.7 (less)
(The object designObjL
is defined below.)
We do exactly the same for the second event:
calDate <- sort(data$"dateEvent/LastFup")[2]
d <- data$"time"[data$"dateEvent/LastFup" == calDate]
At our second date of interim analysis, calDate = 2020-05-11
, we now have observed 2 events, both in the placebo control group with event participant time d = 4
.
So our logrank statistic on 2020-05-11 contains 2 events in the placebo control group, which means that the \(O_{T, 2} = 0\) in the treatment group. We have 5 participants at risk (\(N_2 = 5\)), 2 in the treatment group (\(N_{T, 2} = 2\)) and 3 in the placebo control group:
dataSoFar <- data[data$"dateRand" < calDate, ]
dataSoFar$"dateLastFup" <- calDate
dataSoFar$"time" <- pmin(dataSoFar$"time", calDate - dataSoFar$"dateRand")
dataSoFar$"status"[dataSoFar$"dateEvent/LastFup" > calDate] <- 1
dataSoFar$"survObj" <- Surv(dataSoFar$"time", dataSoFar$"status")
kable(dataSoFar[dataSoFar$"time" >= d, ], row.names = F)
time | status | group | dateRand | dateEvent/LastFup | dateLastFup | participantID | survObj |
---|---|---|---|---|---|---|---|
4 | 2 | P | 2020-05-04 | 2020-05-08 | 2020-05-11 | 1 | 4 |
7 | 1 | T | 2020-05-04 | 2020-06-13 | 2020-05-11 | 2 | 7+ |
5 | 1 | T | 2020-05-06 | 2020-05-25 | 2020-05-11 | 3 | 5+ |
4 | 2 | P | 2020-05-07 | 2020-05-11 | 2020-05-11 | 4 | 4 |
4 | 1 | P | 2020-05-07 | 2020-05-25 | 2020-05-11 | 5 | 4+ |
So the expected number of events in the treatment group at this second event time is \(E_{T, 2} = N_{T, 2} \cdot \frac{O_2}{N_2} = 2 \cdot \frac{2}{5} = \frac{4}{5}\).
Our logrank \(Z\)-score of the first and the second event combined is:
\[\begin{equation} Z^{(2)} = \frac{O_{T, 2} - E_{T, 2}}{\sqrt{V_{T, 2}}} = \frac{0 - \frac{4}{5}}{\sqrt{\frac{4}{5} \cdot \frac{3}{5} \cdot \frac{3}{4}}} = -1.3333333 \end{equation}\]
with \(V_{T, 2}\) the variance of the hypergeometric distribution with probability \(E_{T, 2}\):
\[\begin{equation} V_{T, 2} = E_{T, 2} \cdot \left(\frac{N_2 - O_2}{N_2}\right) \cdot \left(\frac{N_2 - N_{T, 2}}{N_2 - 1}\right) = \frac{4}{5} \cdot \frac{3}{5} \cdot \frac{3}{4} \end{equation}\]
safeLogrankTest(exact = FALSE, # to get the approximate safe logrank test
designObj = designObjL, # (based on the logrank Z-statistic)
dataSoFar$"survObj" ~ dataSoFar$"group")
##
## Safe Logrank Test
##
## data: dataSoFar$survObj by dataSoFar$group (P, T). nEvents = 2
## estimates: hazard ratio = 0.15174
##
## test: z = -1.3333, log(thetaS) = -0.35667
## e-value = 1.3559 > 1/alpha = 20 : FALSE
## alternative hypothesis: true hazard ratio is less than 1
##
## design: the test was designed with alpha = 0.05
## for minimal relevant hazard ratio = 0.7 (less)
calDate <- sort(data$"dateEvent/LastFup")[3]
d <- data$"time"[data$"dateEvent/LastFup" == calDate]
The third event occurs at calDate = 2020-05-21
. This event is of a different kind, because it introduces a new event time (participant time d = 13
) with a possibly different underlying hazard.
dataSoFar <- data[data$"dateRand" < calDate, ]
dataSoFar$"dateLastFup" <- calDate
dataSoFar$"time" <- pmin(dataSoFar$"time", calDate - dataSoFar$"dateRand")
dataSoFar$"status"[dataSoFar$"dateEvent/LastFup" > calDate] <- 1
dataSoFar$"survObj" <- Surv(dataSoFar$"time", dataSoFar$"status")
kable(dataSoFar[dataSoFar$"time" >= d, ], row.names = F)
time | status | group | dateRand | dateEvent/LastFup | dateLastFup | participantID | survObj |
---|---|---|---|---|---|---|---|
17 | 1 | T | 2020-05-04 | 2020-06-13 | 2020-05-21 | 2 | 17+ |
15 | 1 | T | 2020-05-06 | 2020-05-25 | 2020-05-21 | 3 | 15+ |
14 | 1 | P | 2020-05-07 | 2020-05-25 | 2020-05-21 | 5 | 14+ |
13 | 2 | T | 2020-05-08 | 2020-05-21 | 2020-05-21 | 6 | 13 |
13 | 1 | T | 2020-05-08 | 2020-06-17 | 2020-05-21 | 7 | 13+ |
At participant time d = 13
, two participants had an event and are not at risk anymore, but also, two new participants are at risk for 13 days (participant 3 and 9) that were not included in our data set before. So we have 3 - 2 = 1 participants at risk in the placebo control group and 2 + 2 = 4 at risk in the treatment group (\(N_{T, 3} = 4\)), a total of \(N_3 = 5\).
Our third logrank statistic on 2020-05-21 includes 1 event (\(O_3 = 1\)), which is an event in the treatment group (\(O_{T, 3} = 1\)). Our expected number of events in the treatment group at this third event time is therefore \(E_{T, 3} = N_{T, 3} \cdot \frac{O_3}{N_3} = 4 \cdot \frac{1}{5} = \frac{4}{5}\).
Because we have new participants at risk, we also need to reevaluate the expected hazard at the previous event time that had two events at participant time d = 4
:
kable(dataSoFar, row.names = F)
time | status | group | dateRand | dateEvent/LastFup | dateLastFup | participantID | survObj |
---|---|---|---|---|---|---|---|
4 | 2 | P | 2020-05-04 | 2020-05-08 | 2020-05-21 | 1 | 4 |
17 | 1 | T | 2020-05-04 | 2020-06-13 | 2020-05-21 | 2 | 17+ |
15 | 1 | T | 2020-05-06 | 2020-05-25 | 2020-05-21 | 3 | 15+ |
4 | 2 | P | 2020-05-07 | 2020-05-11 | 2020-05-21 | 4 | 4 |
14 | 1 | P | 2020-05-07 | 2020-05-25 | 2020-05-21 | 5 | 14+ |
13 | 2 | T | 2020-05-08 | 2020-05-21 | 2020-05-21 | 6 | 13 |
13 | 1 | T | 2020-05-08 | 2020-06-17 | 2020-05-21 | 7 | 13+ |
11 | 1 | T | 2020-05-10 | 2020-06-03 | 2020-05-21 | 8 | 11+ |
11 | 1 | P | 2020-05-10 | 2020-06-19 | 2020-05-21 | 9 | 11+ |
7 | 1 | P | 2020-05-14 | 2020-06-23 | 2020-05-21 | 10 | 7+ |
For the first two events, we now have had everyone at risk (\(N^{(3)}_2 = 10\)), 5 in the placebo control group and 5 in the treatment group (\(N^{(3)}_{T, 2} = 5\)). So we get: \(E^{(3)}_{T, 2} = N^{(3)}_{T, 2} \cdot \frac{O^{(3)}_2}{N^{(3)}_2} = 5 \cdot \frac{2}{10} = 1\).
Our logrank \(Z\)-score of the three events combined is:
\[\begin{equation} Z^{(3)} = \frac{O^{(3)}_{T, 2} - E^{(3)}_{T, 2} + O_{T, 3} - E_{T, 3}}{\sqrt{V^{(3)}_{T, 2} + V_{T, 3}}} = \frac{0 - 1 +1 - \frac{4}{5}}{\sqrt{1 \cdot \frac{8}{10} \cdot \frac{5}{9} + \frac{4}{5} \cdot (1 - \frac{4}{5})}} = -1.0289915 \end{equation}\]
with \(V_{T, 2}\) the re-evaluated variance of the hypergeometric distribution for everyone in the data set:
\[\begin{equation} V^{(3)}_{T, 2} = E^{(3)}_{T, 2} \cdot \left(\frac{N^{(3)}_2 - O^{(3)}_2}{N^{(3)}_2}\right) \cdot \left(\frac{N^{(3)}_2 - N^{(3)}_{T, 2}}{N^{(3)}_2 - 1}\right) = 1 \cdot \frac{8}{10} \cdot \frac{5}{9} \end{equation}\]
safeLogrankTest(exact = FALSE, # to get the approximate safe logrank test
designObj = designObjL, # (based on the logrank Z-statistic)
dataSoFar$"survObj" ~ dataSoFar$"group")
##
## Safe Logrank Test
##
## data: dataSoFar$survObj by dataSoFar$group (P, T). nEvents = 3
## estimates: hazard ratio = 0.30478
##
## test: z = -1.029, log(thetaS) = -0.35667
## e-value = 1.3101 > 1/alpha = 20 : FALSE
## alternative hypothesis: true hazard ratio is less than 1
##
## design: the test was designed with alpha = 0.05
## for minimal relevant hazard ratio = 0.7 (less)
calDate <- sort(data$"dateEvent/LastFup")[4]
d <- unique(data$"time"[data$"dateEvent/LastFup" == calDate])
Now we have two events, but with differing event times participant time d = 19, 18
.
d4 <- d[1]
d5 <- d[2]
dataSoFar <- data[data$"dateRand" < calDate, ]
dataSoFar$"dateLastFup" <- calDate
dataSoFar$"time" <- pmin(dataSoFar$"time", calDate - dataSoFar$"dateRand")
dataSoFar$"status"[dataSoFar$"dateEvent/LastFup" > calDate] <- 1
dataSoFar$"survObj" <- Surv(dataSoFar$"time", dataSoFar$"status")
kable(dataSoFar[dataSoFar$"time" >= d4, ], row.names = F)
time | status | group | dateRand | dateEvent/LastFup | dateLastFup | participantID | survObj |
---|---|---|---|---|---|---|---|
21 | 1 | T | 2020-05-04 | 2020-06-13 | 2020-05-25 | 2 | 21+ |
19 | 2 | T | 2020-05-06 | 2020-05-25 | 2020-05-25 | 3 | 19 |
So for participant time d = 19
we observe an event with only 2 participants at risk in the treatment group (\(N_{T, 4} = 2\)), out of a total of total of \(N_4 = 2\). Our expected number of events in the treatment group at this fourth event time is \(E_{T, 4} = N_{T, 4} \cdot \frac{O_4}{N_4} = \frac{2}{2}\).
For participant time d = 18
we observe an event with one participant less at risk, so 2 at risk in the treatment group (\(N_{T, 5} = 2\)), out of a total of total of \(N_5 = 3\). Our expected number of events in the treatment group at this fifth event time is \(E_{T, 5} = N_{T, 5} \cdot \frac{O_5}{N_5} = \frac{2}{3}\).
The component of our logrank statistic for the first two events stays the same after five events:
\(O^{(5)}_{T, 2} = O^{(3)}_{T, 2}\), \(E^{(5)}_{T, 2} = E^{(3)}_{T, 2}\) and \(V^{(5)}_{T, 2} = V^{(3)}_{T, 2}\)
But we have to reevaluate our third event with new participants at risk for participant time d = 13
:
kable(dataSoFar[dataSoFar$"time" >= d, ], row.names = F)
time | status | group | dateRand | dateEvent/LastFup | dateLastFup | participantID | survObj |
---|---|---|---|---|---|---|---|
21 | 1 | T | 2020-05-04 | 2020-06-13 | 2020-05-25 | 2 | 21+ |
19 | 2 | T | 2020-05-06 | 2020-05-25 | 2020-05-25 | 3 | 19 |
18 | 2 | P | 2020-05-07 | 2020-05-25 | 2020-05-25 | 5 | 18 |
13 | 2 | T | 2020-05-08 | 2020-05-21 | 2020-05-25 | 6 | 13 |
17 | 1 | T | 2020-05-08 | 2020-06-17 | 2020-05-25 | 7 | 17+ |
15 | 1 | T | 2020-05-10 | 2020-06-03 | 2020-05-25 | 8 | 15+ |
15 | 1 | P | 2020-05-10 | 2020-06-19 | 2020-05-25 | 9 | 15+ |
For an event at 13 days after randomization, we now have 7 participants at risk that are at least 13 days in follow-up (\(N^{(5)}_3 = 7\)), 2 in the placebo control group and 5 in the treatment group (\(N^{(5)}_{T, 3} = 5\)). So we get: \(E^{(5)}_{T, 3} = N^{(5)}_{T, 3} \cdot \frac{O^{(5)}_3}{N^{(5)}_3} = 5 \cdot \frac{1}{7} = \frac{5}{7}\).
Our logrank \(Z\)-score of the five events combined is:
\[\begin{equation} \begin{split} Z^{(5)} =& \frac{O^{(5)}_{T, 2} - E^{(5)}_{T, 2} + O^{(5)}_{T, 3} - E^{(5)}_{T, 3} + O_{T, 5} - E_{T, 5} + O_{T, 5} - E_{T, 5}}{\sqrt{V^{(5)}_{T, 2} + V^{(5)}_{T, 3} + V_{T, 5} + V_{T, 5}}} \\ =& \frac{0 - 1 + 1 - \frac{5}{7} +1 - \frac{2}{2} + 0 - \frac{2}{3}}{\sqrt{1 \cdot \frac{8}{10} \cdot \frac{5}{9} + \frac{5}{7} \cdot (1 - \frac{5}{7}) + \frac{2}{2} \cdot (1 - \frac{2}{2}) + \frac{2}{3} \cdot (1 - \frac{2}{3})}} = -1.4799001 \end{split} \end{equation}\]
safeLogrankTest(exact = FALSE, # to get the approximate safe logrank test
designObj = designObjL, # (based on the logrank Z-statistic)
dataSoFar$"survObj" ~ dataSoFar$"group")
##
## Safe Logrank Test
##
## data: dataSoFar$survObj by dataSoFar$group (P, T). nEvents = 5
## estimates: hazard ratio = 0.26616
##
## test: z = -1.4799, log(thetaS) = -0.35667
## e-value = 1.6664 > 1/alpha = 20 : FALSE
## alternative hypothesis: true hazard ratio is less than 1
##
## design: the test was designed with alpha = 0.05
## for minimal relevant hazard ratio = 0.7 (less)
The above shows the rationale of retrospectively obtaining a sequence of e-values for various calendar dates in the past. To process an entire data set at once, the following code obtains two sequences of e-values for two one-sided tests:
eValuesL <-
eValuesG <- structure(rep(NA, times = max(data$"dateLastFup") -
dateRand.start + 1),
names = as.character(seq.Date(from = dateRand.start,
to = max(data$"dateLastFup"),
by = "day")))
interimCalDates <- as.character(sort(unique(data$"dateEvent/LastFup"[data$"status" == 2])))
# before you observe any event, your e-value is 1
eValuesL[1:(which(names(eValuesL) == interimCalDates[1]) - 1)] <- 1
eValuesG[1:(which(names(eValuesG) == interimCalDates[1]) - 1)] <- 1
for (calDate in as.character(seq.Date(from = as.Date(interimCalDates[1]),
to = max(data$"dateLastFup"),
by = "day"))) {
# at days on which you do not observe an event
if (!(calDate %in% interimCalDates)) {
# evidence stays the same as the day before
eValuesL[calDate] <- eValuesL[as.character(as.Date(calDate) - 1)]
eValuesG[calDate] <- eValuesG[as.character(as.Date(calDate) - 1)]
} else {
dataSoFar <- data[data$"dateRand" < as.Date(calDate), ]
dataSoFar$"time" <- pmin(dataSoFar$"time", as.Date(calDate) - dataSoFar$"dateRand")
dataSoFar$"status"[dataSoFar$"dateEvent" > as.Date(calDate)] <- 1
dataSoFar$"survObj" <- Surv(dataSoFar$"time", dataSoFar$"status")
eValuesL[calDate] <- safeLogrankTest(dataSoFar$"survObj" ~ dataSoFar$"group",
designObj = designObjL, exact = FALSE
)$"eValue"
eValuesG[calDate] <- safeLogrankTest(dataSoFar$"survObj" ~ dataSoFar$"group",
designObj = designObjG, exact = FALSE
)$"eValue"
}
}
designObjL <- designSafeLogrank(hrMin = 0.7,
alpha = 0.025,
alternative = "less", # one-sided test hr < 1
ratio = 1)
designObjL
##
## Safe Logrank Test Design
##
## minimal hazard ratio = 0.7
## alternative = less
## parameter: log(thetaS) = -0.3566749
## alpha = 0.025
## decision rule: e-value > 1/alpha = 40
##
## Timestamp: 2021-06-30 10:08:05 CEST
designObjG <- designSafeLogrank(hrMin = 1/0.7,
alpha = 0.025,
alternative = "greater", # one-sided test hr > 1
ratio = 1)
designObjG
##
## Safe Logrank Test Design
##
## minimal hazard ratio = 1.428571
## alternative = greater
## parameter: log(thetaS) = 0.3566749
## alpha = 0.025
## decision rule: e-value > 1/alpha = 40
##
## Timestamp: 2021-06-30 10:08:05 CEST
We have obtained the following e-values:
eValuesL
## 2020-05-04 2020-05-05 2020-05-06 2020-05-07 2020-05-08 2020-05-09 2020-05-10
## 1.000000 1.000000 1.000000 1.000000 1.176372 1.176372 1.176372
## 2020-05-11 2020-05-12 2020-05-13 2020-05-14 2020-05-15 2020-05-16 2020-05-17
## 1.355909 1.355909 1.355909 1.355909 1.355909 1.355909 1.355909
## 2020-05-18 2020-05-19 2020-05-20 2020-05-21 2020-05-22 2020-05-23 2020-05-24
## 1.355909 1.355909 1.355909 1.310146 1.310146 1.310146 1.310146
## 2020-05-25 2020-05-26 2020-05-27 2020-05-28 2020-05-29 2020-05-30 2020-05-31
## 1.666351 1.666351 1.666351 1.666351 1.666351 1.666351 1.666351
## 2020-06-01 2020-06-02 2020-06-03 2020-06-04 2020-06-05 2020-06-06 2020-06-07
## 1.666351 1.666351 1.146276 1.146276 1.146276 1.146276 1.146276
## 2020-06-08 2020-06-09 2020-06-10 2020-06-11 2020-06-12 2020-06-13 2020-06-14
## 1.146276 1.146276 1.146276 1.146276 1.146276 1.146276 1.146276
## 2020-06-15
## 1.146276
eValuesG
## 2020-05-04 2020-05-05 2020-05-06 2020-05-07 2020-05-08 2020-05-09 2020-05-10
## 1.0000000 1.0000000 1.0000000 1.0000000 0.8234606 0.8234606 0.8234606
## 2020-05-11 2020-05-12 2020-05-13 2020-05-14 2020-05-15 2020-05-16 2020-05-17
## 0.6920614 0.6920614 0.6920614 0.6920614 0.6920614 0.6920614 0.6920614
## 2020-05-18 2020-05-19 2020-05-20 2020-05-21 2020-05-22 2020-05-23 2020-05-24
## 0.6920614 0.6920614 0.6920614 0.6938142 0.6938142 0.6938142 0.6938142
## 2020-05-25 2020-05-26 2020-05-27 2020-05-28 2020-05-29 2020-05-30 2020-05-31
## 0.5118839 0.5118839 0.5118839 0.5118839 0.5118839 0.5118839 0.5118839
## 2020-06-01 2020-06-02 2020-06-03 2020-06-04 2020-06-05 2020-06-06 2020-06-07
## 0.5118839 0.5118839 0.7208356 0.7208356 0.7208356 0.7208356 0.7208356
## 2020-06-08 2020-06-09 2020-06-10 2020-06-11 2020-06-12 2020-06-13 2020-06-14
## 0.7208356 0.7208356 0.7208356 0.7208356 0.7208356 0.7208356 0.7208356
## 2020-06-15
## 0.7208356
Plot these e-values by their calendar date: