ltrc.data <- data.frame(
ltrc.sim(n = 500, rtrunc = rnorm, trunc.args = list(4, 1),
rtarget = rnorm, target.args = list(4, 1),
rcens = rexp, cens.args = list(1), seed = 1212),
group = rep(1:2, each = 250)
)
test <- ltrc.test(ltrc.data, tests = "ks", weights = rep(1/2, 2), B = 200, plot.curves = TRUE)This package implements tests for the \(k\)-sample problem under left truncation and/or right censoring. It provides
In this vignette, we focus on the case of left-truncated and right-censored (LTRC) data. The functions for only left truncation or only right censoring follow the same structure and are not shown separately.
The methodology implemented here is described in detail in Lago, de Uña-Alvarez and Pardo-Fernandez (2025) and Lago, Pardo-Fernandez and de Uña-Alvarez (2026); see DESCRIPTION for the full references.
The package uses the following variables:
entry: truncation time \(U\)time: observed time \(Y =
\min(X, C)\)status: event indicator \(\Delta\)group: group labelData must be provided as a data frame (or matrix) with these columns,
in this order. Functions that take a single combined data frame (such as
ltrc.test) identify columns by position rather than by
name; the output of ltrc.sim follows the same convention
but labels the event indicator column delta rather than
status — both refer to the same quantity \(\Delta\).
The user only needs to specify the sample size of the simulated
sample and the distributions of the truncation, target, and censoring
variables through the corresponding standard R generators
(rnorm, rweibull, runif, etc.),
along with their parameters supplied as a list. The only requirement is
that the truncation, target, and censoring variables be strictly
positive.
ltrc.data <- ksamplesLTRC::ltrc.sim(n = 500, rtrunc = rnorm, trunc.args = list(4, 1),
rtarget = rnorm, target.args = list(4, 1),
rcens = rexp, cens.args = list(1), seed = 12)
head(ltrc.data)
#> u y delta
#> 1 3.043256 3.714960 0
#> 2 3.079995 4.490035 1
#> 3 2.002358 2.500780 0
#> 4 3.893536 4.713094 0
#> 5 4.428015 4.504474 0
#> 6 3.222280 3.475242 0
mean(ltrc.data[, 1] <= ltrc.data[, 2])
#> [1] 1
table(ltrc.data$delta)
#>
#> 0 1
#> 291 209Despite testing being the main goal of this package, we also include the estimators used internally by the testing procedures.
ltrc.data <- ksamplesLTRC::ltrc.sim(1000, rnorm, trunc.args = list(4, 1),
rnorm, target.args = list(4, 1),
rexp, cens.args = list(1))
est <- ltrc.estimator(o.sample = ltrc.data, conf.int = TRUE, conf.type = 'loglog')
plot(est, col = 'forestgreen', xlab = 't', ylab = 'Estimated survival probability')
curve(1 - pnorm(x, 4, 1), add = TRUE)str(est)
#> List of 6
#> $ fail.time : num [1:428] 2.04 2.24 2.34 2.44 2.51 ...
#> $ estimation : num [1:428] 0.969 0.948 0.933 0.919 0.908 ...
#> $ prob : num [1:428] 0.0312 0.0211 0.0146 0.0137 0.0111 ...
#> $ conf.limits : num [1:428, 1:2] 0.798 0.803 0.799 0.79 0.783 ...
#> ..- attr(*, "dimnames")=List of 2
#> .. ..$ : NULL
#> .. ..$ : chr [1:2] "var.i.l" "var.i.u"
#> $ conf.limits.F: num [1:428, 1:2] 0.00446 0.01305 0.02109 0.02956 0.0371 ...
#> ..- attr(*, "dimnames")=List of 2
#> .. ..$ : NULL
#> .. ..$ : chr [1:2] "varF.l" "varF.u"
#> $ hole : NULL
#> - attr(*, "class")= chr "estimators"$fail.time: the ordered, distinct observed event
times$estimation: the estimated survival probability at each
observed event time$prob: the jumps of the estimator at each observed
event time$conf.limits: the lower and upper values of the
confidence band for the survival function$conf.limits.F: the same limits for the distribution
functionThe functions of this package also contain an input to deal with holes when data are subject to left truncation (with or without censoring). Holes are event times with a unitary risk set, which would make the Lynden-Bell-type estimator degenerate. There are three possibilities:
"holes" (default): no correction; a warning is
issued."add": increases the risk set by 1 at the hole."conditional": discards observations and conditions the
sample on event times larger than the largest hole.For illustration, we use the well-known Channing House dataset. This
dataset presents a hole at time \(t =
781\) in the male group. Below is the resulting estimated
survival function when holes = 'holes' (red),
holes = 'add' (green), and
holes = 'conditional' (blue).
data("channing", package = "KMsurv")
o.sample <- data.frame(entry = channing$ageentry, time = channing$age,
status = channing$death, group = channing$gender)
o.sample <- subset(o.sample, group == 1)
head(o.sample)
#> entry time status group
#> 366 782 909 1 1
#> 367 1020 1128 1 1
#> 368 856 969 1 1
#> 369 915 957 1 1
#> 370 863 983 1 1
#> 371 906 1012 1 1
plot(ksamplesLTRC::ltrc.estimator(o.sample, holes = 'holes'), col = 2,
xlab = 't', ylab = 'Estimated survival probability')
lines(ksamplesLTRC::ltrc.estimator(o.sample, holes = 'add'), col = 3)
lines(ksamplesLTRC::ltrc.estimator(o.sample, holes = 'conditional'), col = 4)When data are subject to left truncation or right censoring only, the matrix or data frame must contain only two columns: truncation and event times in the former case, and event times and censoring indicators in the latter. The survival function estimator for left-truncated data also offers the hole-handling options described above; the right-censored-only estimator does not, since that phenomenon does not occur in that framework.
We aim to test whether the \(k\) target variables follow the same distribution across the \(k\) populations or not. The tests are based on comparing each group-specific estimator \(F_{n_j}\) against a pooled estimator
\[ F_n(t) = \sum_{j=1}^k \varphi_j F_{n_j}(t), \]
where \(\varphi_1,\dots,\varphi_k\) are user-specified weights satisfying \(\varphi_j > 0\) and \(\sum_{j=1}^k \varphi_j = 1\).
There are two options, as the user may perform the tests with either of the two main functions of this package.
The user may perform the tests via ltrc.test.
Columns must appear in this order: entry,
time, status, group. This
function requires the weights \(w_1,\dots,w_k\) used in \(F_n\) above, supplied as a vector with
strictly positive components summing to 1. The user also chooses one or
more of tests = c("ks", "cvm", "logrank"). If
ks and/or cvm are requested, the bootstrap
p-value is approximated using B replications (default
B = 500); this argument need not be set explicitly unless a
different number of replications is desired. Weighted log-rank-type
(Fleming and Harrington) tests are also available: the choice \(p = 0 = q\) returns the classical log-rank
test, while other choices of \(p\) and
\(q\) (scalars or vectors) assign more
weight to discrepancies in early, mid, or late event times. This
function also supports the holes argument described
above.
When holes = 'conditional' is used with more than one
group, all \(k\) groups are conditioned
jointly on event times larger than the largest hole found across all
\(k\) samples, and not on each group’s
own largest hole. This guarantees that the KS and CvM statistics are
computed on a common, hole-free time window across groups; conditioning
each group separately would compare estimators defined on different
supports.
data("channing", package = "KMsurv")
data.channing <- data.frame(entry = channing$ageentry, time = channing$age,
status = channing$death, group = channing$gender)
aa <- ksamplesLTRC::ltrc.test(data.channing, weights = rep(1/2, 2), holes = 'conditional',
tests = c('ks', 'cvm', 'logrank'), plot.curves = TRUE,
p = c(0, 1, 0.5, 0), q = c(0, 0, 0.5, 1)) # equal weights for two groupsaa$p.value
#> $ks
#> [1] 0.5708583
#>
#> $cvm
#> [1] 0.6047904
aa$logrank
#> statistic p.value
#> p=0, q=0 2.285075 0.13062379
#> p=1, q=0 2.814105 0.09343901
#> p=0.5, q=0.5 1.890058 0.16919541
#> p=0, q=1 1.177968 0.27776998ksample.test. This
function differs from ltrc.test in only one aspect: instead
of passing a single matrix or data frame, the user passes
time, entry (when applicable),
status (when applicable), and group as
separate vectors. The function detects which type of data
(left-truncated, right-censored, or both) the user has, and performs the
tests accordingly. The same global-conditioning behavior under
holes = 'conditional' applies here.data("channing", package = "KMsurv")
bb <- ksamplesLTRC::ksample.test(time = channing$age, entry = channing$ageentry,
status = channing$death, group = channing$gender,
weights = rep(1/2, 2), B = 500, holes = 'conditional',
tests = c('ks', 'cvm', 'logrank'), plot.curves = TRUE,
p = c(0, 1, 0.5, 0), q = c(0, 0, 0.5, 1)) # equal weights for two groupsbb$p.value
#> $ks
#> [1] 0.5988024
#>
#> $cvm
#> [1] 0.6347305
bb$logrank
#> statistic p.value
#> p=0, q=0 2.285075 0.13062379
#> p=1, q=0 2.814105 0.09343901
#> p=0.5, q=0.5 1.890058 0.16919541
#> p=0, q=1 1.177968 0.27776998If only the KS or only the CvM test is of interest,
ltrc.pv.ks and ltrc.pv.cvm compute the
statistic and its bootstrap p-value directly, without running the other
tests:
ksamplesLTRC::ltrc.pv.ks(data.channing, weights = rep(1/2, 2), B = 200, holes = 'conditional')$pvalue
#> [1] 0.5870647These are equivalent to calling ltrc.test with
tests = "ks" (respectively "cvm"), and are
provided for convenience when the other tests are not needed.
Although the discussion above focused on left-truncated and
right-censored data, the package includes functions for only
left-truncated or only right-censored data. The structure of the inputs
is the same in all three cases, except for the trivial fact that holes
do not arise under right censoring. The functions follow the same naming
convention as those for LTRC data, with different prefixes: for
instance, ltrc.estimator returns the MLE of the survival
function under left truncation and right censoring,
lt.estimator returns the analogous estimator under left
truncation only, and rc.estimator does so for
right-censored-only data. The same convention applies to the testing and
simulation functions.
Following the results in the literature, we recommend the following:
This package provides tools for testing equality of survival distributions under left truncation and/or right censoring, including log-rank, weighted log-rank, and omnibus (Kolmogorov–Smirnov and Cramér–von Mises) tests.