recur.bart: Nonparametric survival analysis with BART

Description Usage Arguments Value Author(s) References See Also Examples

Description

Here we have implemented a simple and direct approach to utilize BART in survival analysis that is very flexible, and is akin to discrete-time survival analysis. Following the capabilities of BART, we allow for maximum flexibility in modeling the dependence of survival times on covariates. In particular, we do not impose proportional hazards.

To elaborate, consider data in the usual form: (t, delta, x) where t is the event time, delta is an indicator distinguishing events (delta=1) from right-censoring (delta=0), x is a vector of covariates, and i=1, ..., N (i suppressed for convenience) indexes subjects.

We denote the K distinct event/censoring times by 0<t(1)<...< t(K)<infinity thus taking t(j) to be the j'th order statistic among distinct observation times and, for convenience, t(0)=0. Now consider event indicators y(j) for each subject i at each distinct time t(j) up to and including the subject's observation time t=t(n) with n=sum I[t(j)<=t]. This means y(j)=0 if j<n and y(n)=delta.

We then denote by p(j) the probability of an event at time t(j) conditional on no previous event. We now write the model for y(j) as a nonparametric probit regression of y(j) on the time t(j) and the covariates x, and then utilize BART for binary responses. Specifically, y(j) = delta I[t=t(j)], j=1, ..., n ; we have p(j) = F(mu(j)), mu(j) = mu0+f(t(j), x) where F denotes the standard normal cdf (probit link). As in the binary response case, f is the sum of many tree models.

Usage

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
recur.bart(x.train, y.train=NULL, times=NULL, delta=NULL, x.test=matrix(0.0,0,0),
           x.test.nogrid=FALSE, keepcall=FALSE,
           k=2.0, power=2.0, base=.95, binaryOffset=NULL,
           ntree=50, ndpost=10000, nskip=250, printevery=100, keepevery=10,
           keeptrainfits=TRUE, usequants=FALSE, numcut=100, printcutoffs=0,
           verbose=TRUE,
           seed=99,    ## mc.recur.bart only
           mc.cores=2, ## mc.recur.bart only
           nice=19L    ## mc.recur.bart only
         )

mc.recur.bart(x.train, y.train=NULL, times=NULL, delta=NULL, x.test=matrix(0.0,0,0),
              x.test.nogrid=FALSE, keepcall=FALSE,
              k=2.0, power=2.0, base=.95, binaryOffset=NULL,
              ntree=50, ndpost=10000, nskip=250, printevery=100, keepevery=10,
              keeptrainfits=TRUE, usequants=FALSE, numcut=100, printcutoffs=0,
              verbose=TRUE,
              seed=99,    ## mc.recur.bart only
              mc.cores=2, ## mc.recur.bart only
              nice=19L    ## mc.recur.bart only
            )

Arguments

x.train

Explanatory variables for training (in sample) data.
Must be a matrix with (as usual) rows corresponding to observations and columns to variables.
recur.bart will generate draws of f(t, x) for each x which is a row of x.train (note that the definition of x.train is dependent on whether y.train has been specified; see below).

y.train

Binary response dependent variable for training (in sample) data.
If y.train is NULL, then y.train (x.train and x.test, if specified) are generated by a call to recur.pre.bart (which require that times and delta be provided: see below); otherwise, y.train (x.train and x.test, if specified) are utilized as given assuming that the data construction has already been performed.

times

The time of event or right-censoring.
If y.train is NULL, then times (and delta) must be provided.

delta

The event indicator: 1 is an event while 0 is censored.
If y.train is NULL, then delta (and times) must be provided.

x.test

Explanatory variables for test (out of sample) data.
Must be a matrix and have the same structure as x.train.
recur.bart will generate draws of f(t, x) for each x which is a row of x.test.

x.test.nogrid

Occasionally, you do not need the entire time grid for x.test. If so, then for performance reasons, you can set this argument to TRUE.

keepcall

Logical; if FALSE, returned object will have call set to call("NULL"), otherwise the call used to instantiate BART.

k

k is the number of prior standard deviations f(t, x) is away from +/-3. The bigger k is, the more conservative the fitting will be.

power

Power parameter for tree prior.

base

Base parameter for tree prior.

binaryOffset

The model is P(Y=1 | t, x) = F(f(t, x) + mu0) where mu0 is specified by binaryOffset.
The idea is that f is shrunk towards 0, so the offset allows you to shrink towards a probability other than .5.
If binaryOffset=NULL when times and delta were provided, then an exponential distribution offset is assumed independent of the covariates, i.e. binaryOffset=qnorm(1-exp(-mean.diff*sum(delta)/sum(times))) where mean.diff is the mean of the differences of the distinct ordered adjacent times, i.e. mean(t(1)-t(0), ..., t(K)-t(K-1)).
If binaryOffset=NULL when times and delta were not provided, then binaryOffset=0.

ntree

The number of trees in the sum.

ndpost

The number of posterior draws after burn in, ndpost/keepevery will actually be returned.

nskip

Number of MCMC iterations to be treated as burn in.

printevery

As the MCMC runs, a message is printed every printevery draws.

keepevery

Every keepevery draw is kept to be returned to the user.
A “draw” will consist of values f*(t, x) at x = rows from the train(optionally) and test data, where f* denotes the current draw of f.

keeptrainfits

If true the draws of f(t, x) for x = rows of x.train are returned.

usequants

Decision rules in the tree are of the form x <= c vs. x > c for each variable corresponding to a column of x.train. usequants determines how the set of possible c is determined. If usequants is true, then the c are a subset of the values (xs[i]+xs[i+1])/2 where xs is unique sorted values obtained from the corresponding column of x.train. If usequants is false, the cutoffs are equally spaced across the range of values taken on by the corresponding column of x.train.

numcut

The number of possible values of c (see usequants). If a single number if given, this is used for all variables. Otherwise a vector with length equal to ncol(x.train) is required, where the i^th element gives the number of c used for the i^th variable in x.train. If usequants is false, numcut equally spaced cutoffs are used covering the range of values in the corresponding column of x.train. If usequants is true, then min(numcut, the number of unique values in the corresponding columns of x.train - 1) c values are used.

printcutoffs

The number of cutoff rules c to printed to screen before the MCMC is run. Give a single integer, the same value will be used for all variables. If 0, nothing is printed.

verbose

Logical, if FALSE supress printing.

seed

mc.recur.bart only: seed required for reproducible MCMC.

mc.cores

mc.recur.bart only: number of cores to employ in parallel.

nice

mc.recur.bart only: set the job priority. The default priority is 19: priorities go from 0 (highest) to 19 (lowest).

Value

recur.bart returns a list. Besides the items listed below, the list has a binaryOffset component giving the value used, a times component giving the unique times and K which is the number of unique times.

yhat.train

A matrix with (ndpost/keepevery) rows and nrow(x.train) columns. Each row corresponds to a draw f* from the posterior of f and each column corresponds to a row of x.train. The (i,j) value is f*(t, x) for the i\^th kept draw of f and the j\^th row of x.train.
Burn-in is dropped.

haz.train

The hazard function, h(t|x), where x's are the rows of the training data.

cum.train

The cumulative hazard function, h(t|x), where x's are the rows of the training data.

yhat.test

Same as yhat.train but now the x's are the rows of the test data.

haz.test

The hazard function, h(t|x), where x's are the rows of the test data.

cum.test

The cumulative hazard function, h(t|x), where x's are the rows of the test data.

varcount

a matrix with (ndpost/keepevery) rows and nrow(x.train) columns. Each row is for a draw. For each variable (corresponding to the columns), the total count of the number of times that variable is used in a tree decision rule (over all trees) is given.

Note that yhat.train and yhat.test are f(t, x) + binaryOffset. If you want draws of the probability P(Y=1 | t, x) you need to apply the normal cdf (pnorm) to these values.

Author(s)

Rodney Sparapani: rsparapa@mcw.edu

References

Sparapani R., Rein L., Tarima S., Jackson T., Meurer J. (2017) Nonparametric recurrent events analysis with BART and an application to the hospital admissions of patients with diabetes. Biostatistics manuscript submitted.

See Also

recur.pre.bart

Examples

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
## Not run: 

data(x.train)
data(x.test)
data(y.train)

set.seed(99)    
post <- recur.bart(x.train=x.train, y.train=y.train, x.test=x.test)
## larger data sets such as these can take some time
## if parallel processing is available, uncomment this block    
## post <- mc.recur.bart(x.train=x.train, y.train=y.train,
##                       x.test=x.test, mc.cores=5, seed=99)
    
dss <- rpart(post$yhat.train.mean~x.train)

require(rpart.plot)

rpart.plot(dss)
## notice that all splits in the tree
## (except 1 at the bottom involving a small group)
## involve ca, cci_pud, cci_pvd, ins270 and n

## compare patients treated with insulin vs not   
N.train <- 235
N.test <- 253
K <- post$K ## 798 unique time points

## only testing set, i.e., remove training set    
x.test. <- x.test[N.train*K+(1:(N.test*K)), ]
x.test. <- rbind(x.test., x.test.)
x.test.[ , 'ins270'] <- rep(0:1, each=N.test*K)

## with timebart we can't predict x.test without also training
## we are working on enhancing this in the near future    
set.seed(99)    
post. <- recur.bart(x.train=x.train, y.train=y.train, x.test=x.test.)
## larger data sets such as these can take some time
## if parallel processing is available, uncomment this block    
## post. <- mc.recur.bart(x.train=x.train, y.train=y.train,
##                        x.test=x.test., mc.cores=5, seed=99)
    
## create Friedman's partial dependence function for the
## intensity/hazard by time and ins270
NK.test <- N.test*K
M <- nrow(post$haz.test) ## number of MCMC samples, typically 1000
    
RI <- matrix(0, M, K)
    
for(i in 1:N.test) 
    RI <- RI+(post.$haz.test[ , (N.test+i-1)*K+1:K]/
              post.$haz.test[ , (i-1)*K+1:K])/N.test
    
RI.lo <- apply(RI, 2, quantile, probs=0.025)
RI.mu <- apply(RI, 2, mean)
RI.hi <- apply(RI, 2, quantile, probs=0.975)
    
plot(post.$times, RI.hi, type='l', lty=2, log='y',
     ylim=c(min(RI.lo, 1/RI.hi), max(1/RI.lo, RI.hi)),
     xlab='t', ylab='RI(t, x)',
     sub='insulin(ins270=1) vs. no insulin(ins270=0)',
     main='Relative intensity of hospital admissions for diabetics')
lines(post.$times, RI.mu)
lines(post.$times, RI.lo, lty=2)
lines(post.$times, rep(1, K), col='darkgray')

## RI for insulin therapy seems fairly constant with time
mean(RI.mu)
    

## End(Not run)

timebart documentation built on May 2, 2019, 4:43 p.m.