Skip to content

Commit 36b56a5

Browse files
Terry M Therneaucran-robot
authored andcommitted
version 3.8-3
1 parent 214edd1 commit 36b56a5

File tree

117 files changed

+3937
-2459
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

117 files changed

+3937
-2459
lines changed

DESCRIPTION

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
Title: Survival Analysis
22
Priority: recommended
33
Package: survival
4-
Version: 3.7-0
5-
Date: 2024-06-01
4+
Version: 3.8-3
5+
Date: 2024-12-17
66
Depends: R (>= 3.5.0)
77
Imports: graphics, Matrix, methods, splines, stats, utils
88
LazyData: Yes
@@ -22,12 +22,12 @@ Description: Contains the core survival analysis routines, including
2222
License: LGPL (>= 2)
2323
URL: https://github.com/therneau/survival
2424
NeedsCompilation: yes
25-
Packaged: 2024-06-03 15:17:04 UTC; therneau
25+
Packaged: 2024-12-17 16:37:18 UTC; therneau
2626
Author: Terry M Therneau [aut, cre],
2727
Thomas Lumley [ctb, trl] (original S->R port and R maintainer until
2828
2009),
2929
Atkinson Elizabeth [ctb],
3030
Crowson Cynthia [ctb]
3131
Maintainer: Terry M Therneau <therneau.terry@mayo.edu>
3232
Repository: CRAN
33-
Date/Publication: 2024-06-05 16:30:02 UTC
33+
Date/Publication: 2024-12-17 20:20:02 UTC

MD5

Lines changed: 115 additions & 108 deletions
Large diffs are not rendered by default.

NAMESPACE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export(Surv, Surv2, Surv2data, aeqSurv, aareg, agreg.fit,
3535
survobrien,
3636
survpenal.fit,
3737
survreg, survreg.control, survreg.fit,
38-
survreg.distributions, survregDtest, tcut,
38+
survreg.distributions, survregDtest, tcut, totimeline, fromtimeline,
3939
tmerge, untangle.specials, yates, yates_setup,
4040
survConcordance, survConcordance.fit, survfitcoxph.fit)
4141
S3method('[', aareg)

R/aareg.R

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ aareg <- function(formula, data, weights, subset, na.action,
1818
# a local copy, doing otherwise messes up future use of update() on
1919
# the model object for a user stuck in "+ cluster()" mode.
2020
if (missing(formula)) stop("a formula argument is required")
21+
# make Surv(), strata() resolve to the survival namespace
22+
newform <- removeDoubleColonSurv(formula)
23+
if (!is.null(newform)) {
24+
formula <- newform$formula
25+
if (newform$newcall) Call$formula <- formula
26+
}
2127

2228
ss <- c("cluster", "offset")
2329
Terms <- if (missing(data)) terms(formula, specials=ss) else

R/agsurv.R

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
1-
# Automatically generated from the noweb directory
21
agsurv <- function(y, x, wt, risk, survtype, vartype) {
2+
# This is called by coxsurv.fit, once per stratum, to compute all the
3+
# peices of a coxph survival curve
4+
# y: Surv object
5+
# x: covariate matrix
6+
# wt: case weights
7+
# risk: exp(X beta) = risk score for each observation
8+
# survtype =1 Kalbfleisch-Prentice, 2= exp(cum hazard): Breslow,
9+
# 3= exp(cum hazard) : Efron
10+
#
311
nvar <- ncol(as.matrix(x))
412
status <- y[,ncol(y)]
513
dtime <- y[,ncol(y) -1]
@@ -74,7 +82,13 @@ agsurv <- function(y, x, wt, risk, survtype, vartype) {
7482
result <- list(n= nrow(y), time=time, n.event=nevent, n.risk=irisk,
7583
n.censor=ncens, hazard=haz,
7684
cumhaz=cumsum(haz), varhaz=varhaz, ndeath=ndeath,
77-
xbar=apply(matrix(xbar, ncol=nvar),2, cumsum))
78-
if (survtype==1) result$surv <- km$inc
85+
xbar= matrix(xbar, ncol=nvar))
86+
# varhaz is the first part of the variance; the increment to var(\Lambda)
87+
# at each time, that one we would have if beta were a fixed constant.
88+
# Each row of xbar is (mean of those at risk) * lambda; we
89+
# need it for the second part of the variance. The other elements are
90+
# what you would expect.
91+
92+
if (survtype==1) result$surv <- km$inc # Kalbfleisch-Prentice
7993
result
8094
}

R/anova.coxphlist.R

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,25 @@ anova.coxphlist <- function (object, test = 'Chisq' ,...) {
1414
stop("all models must have the same ties option")
1515

1616
responses <- as.character(unlist(lapply(object,
17-
function(x) deparse(formula(x)[[2]]))))
17+
function(x) deparse1(formula(x)[[2]]))))
1818
sameresp <- (responses == responses[1])
1919
if (!all(sameresp)) {
2020
object <- object[sameresp]
21-
warning(paste("Models with response", deparse(responses[!sameresp]),
21+
warning(paste("Models with response", deparse1(responses[!sameresp]),
2222
"removed because response differs from", "model 1"))
2323
}
2424

2525
ns <- sapply(object, function(x) length(x$residuals))
2626
if (any(ns != ns[1]))
2727
stop("models were not all fit to the same size of dataset")
2828

29+
# verify that all of them used the same strata, if present
30+
stemp <- lapply(object, function(x)
31+
untangle.specials(x$terms, "strata")[["vars"]])
32+
fail <- (any(sapply(stemp, length) > 0) &&
33+
any(sapply(stemp, function(x) !identical(x, stemp[[1]]))))
34+
if (fail) stop("models do not have the same strata")
35+
2936
nmodels <- length(object)
3037
if (nmodels == 1) # only one model remains
3138
return(anova.coxph(object[[1]], test = test))

R/anova.coxphms.R

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# This is has arguments like anova.coxph: a call with a single model is
2+
# not allowed. So: we avoid having separate anova.coxphms and anova.coxphmslist
3+
# functions.
4+
#
5+
anova.coxphms <- function (object, ..., test = c("score", "Wald", "PL")) {
6+
if (!inherits(object, "coxphms"))
7+
stop ("argument must be the fit of a multistate hazard model")
8+
9+
# All the ... args need to be coxphms fits. If any of them
10+
# have a name attached, e.g., 'charlie=T' we assume a priori
11+
# that they are illegal
12+
#
13+
dotargs <- list(...)
14+
named <- if (is.null(names(dotargs)))
15+
rep(FALSE, length(dotargs))
16+
else (names(dotargs) != "")
17+
if (any(named))
18+
warning(paste("The following arguments to anova.coxphms(..)",
19+
"are invalid and dropped:", paste(deparse(dotargs[named]),
20+
collapse = ", ")))
21+
22+
allmod <- c(object, dotargs[!named]) # all the models
23+
is.coxms <- sapply(dotargs, function(x) inherits(x, "coxphms"))
24+
if (!all(is.coxms))
25+
stop("All arguments must be multistate hazard models")
26+
27+
ties <- sapply(allmod, function(x) x$method)
28+
if (any(ties != ties[1]))
29+
stop("all models must have the same ties option")
30+
stop("anova not yet available for multistate")
31+
responses <- as.character(unlist(lapply(allmod,
32+
function(x) deparse(formula(x)[[2]]))))
33+
sameresp <- (responses == responses[1])
34+
if (!all(sameresp)) {
35+
allmod <- allmod[sameresp]
36+
warning(paste("Models with response", deparse(responses[!sameresp]),
37+
"removed because response differs from", "model 1"))
38+
}
39+
nmodel <- length(allmod)
40+
if (nmodel < 2) stop("must have more than one model")
41+
42+
# Check that they were all fit to the same data set. This isn't perfect:
43+
# we could have distince data sets with the same number of rows in the
44+
# model frame (n), same number of unique id values, and same number of
45+
# events. But it is unlikely. The main reason for this is that one fit
46+
# had an extra variable that was missing on some subjects.
47+
ns <- sapply(object, function(x) c(x$n, x$n.id, x$nevent))
48+
if (!(is.matrix(ns))) # only occurs if a user messed with the object
49+
stop("at least one model is missing the n, n.id, or nevent element")
50+
if (any(apply(ns, 2, function(x) any(x != x[1]))))
51+
stop("models were not all fit to the same dataset")
52+
53+
# I can only handle models with the same stratification structure
54+
stest <- sapply(allmod, function(x) identical(x$map, allmod[[1]]$smap))
55+
if (!all(stest))
56+
stop("not all models have the same structure of baseline hazards")
57+
58+
# Models must be in increasing order of complexity
59+
nvar <- sapply(allmod, function(x) length(coef(x)))
60+
if (any(diff(nvar) < 1))
61+
stop("models must be in increasing order of complexity")
62+
# do the more complex nesting via variable names
63+
for (i in 2:nmodel) {
64+
indx <- match(rownames(allmod[i-1][["cmap"]]),
65+
rownames(allmod[i][["cmap"]]), nomatch=0)
66+
if (any(indx==0))
67+
stop(paste("model", i-1, "contains variables not in model", i))
68+
}
69+
70+
# They all must have the same response
71+
for (i in 2:nmodel)
72+
if (!identical(allmod[[i]]$y, allmod[[i-1]]$y))
73+
stop("all models must have the same response")
74+
75+
# Now for the real work
76+
test <- df <- pval <- double(nmodel -1)
77+
for (i in 2:nmodel)
78+
79+
tfun <- function(x) paste(as.character(delete.response(terms(formula(x)))),
80+
collapse=' ')
81+
variables <- lapply(object, tfun)
82+
dimnames(table) <- list(1:nmodel,
83+
c("loglik", "Chisq", "Df"))
84+
title <- paste("Analysis of Deviance Table\n Cox model: response is ",
85+
responses[1])
86+
topnote <- paste(" Model ", format(1:nmodel), ": ", variables,
87+
sep = "", collapse = "\n")
88+
if (!is.null(test)) {
89+
table[['Pr(>|Chi|)']] <- pchisq(table$Chisq, table$Df, lower.tail=FALSE)
90+
}
91+
structure(table, heading = c(title, topnote),
92+
class = c("anova", "data.frame"))
93+
}
94+

R/concordance.R

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,25 @@ concordance.formula <- function(object, data,
1212
timewt <- match.arg(timewt)
1313
if (missing(ymin)) ymin <- NULL
1414
if (missing(ymax)) ymax <- NULL
15-
15+
if (missing(object)) stop("a formula argument is required")
16+
formula <- object # clearer to read below
17+
18+
# make Surv(), strata() etc in a formula resolve to the survival namespace
19+
newform <- removeDoubleColonSurv(formula)
20+
if (!is.null(newform)) {
21+
formula <- newform$formula
22+
if (newform$newcall) Call$formula <- formula
23+
}
24+
1625
index <- match(c("data", "weights", "subset", "na.action",
1726
"cluster"),
1827
names(Call), nomatch=0)
1928
temp <- Call[c(1, index)]
2029
temp[[1L]] <- quote(stats::model.frame)
2130
special <- c("strata", "cluster")
22-
temp$formula <- if(missing(data)) terms(object, special)
23-
else terms(object, special, data=data)
31+
temp$formula <- if(missing(data)) terms(formula, special)
32+
else terms(formula, special, data=data)
33+
2434
mf <- eval(temp, parent.frame()) # model frame
2535
if (nrow(mf) ==0) stop("No (non-missing) observations")
2636
Terms <- terms(mf)

R/coxph.R

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# Automatically generated from the noweb directory
2-
#tt <- function(x) x
32
coxph <- function(formula, data, weights, subset, na.action,
43
init, control, ties= c("efron", "breslow", "exact"),
54
singular.ok =TRUE, robust,
@@ -9,6 +8,8 @@ coxph <- function(formula, data, weights, subset, na.action,
98
missing.ties <- missing(ties) & missing(method) #see later multistate sect
109
ties <- match.arg(ties)
1110
Call <- match.call()
11+
if (missing(formula)) stop("a formula argument is required")
12+
1213
## We want to pass any ... args to coxph.control, but not pass things
1314
## like "dats=mydata" where someone just made a typo. The use of ...
1415
## is simply to allow things like "eps=1e6" with easier typing
@@ -20,14 +21,32 @@ coxph <- function(formula, data, weights, subset, na.action,
2021
stop(gettextf("Argument %s not matched",
2122
names(extraArgs)[indx==0L]), domain = NA)
2223
}
24+
25+
# Gather any leftover arguments into a coxph.control call
26+
# If there is a control argument, force a call to coxph.control to both
27+
# fill it out with all the elements and do sanity checks
2328
if (missing(control)) control <- coxph.control(...)
29+
else if (is.list(control)) control <- do.call(coxph.control, control)
30+
else stop("control argument must be a list")
2431

32+
# make Surv(), strata() etc in a formula resolve to the survival namespace
33+
if (is.list(formula)) {
34+
newform <- removeDoubleColonSurv(formula[[1]])
35+
if (!is.null(newform)) {
36+
formula[[1]] <- newform$formula
37+
if (newform$newcall) Call$formula <- newform$formula
38+
}
39+
} else {
40+
newform <- removeDoubleColonSurv(formula)
41+
if (!is.null(newform)) {
42+
formula <- newform$formula
43+
if (newform$newcall) Call$formula <- formula #save the nicer version
44+
}
45+
}
2546
# Move any cluster() term out of the formula, and make it an argument
2647
# instead. This makes everything easier. But, I can only do that with
2748
# a local copy, doing otherwise messes up future use of update() on
2849
# the model object for a user stuck in "+ cluster()" mode.
29-
if (missing(formula)) stop("a formula argument is required")
30-
3150
ss <- "cluster"
3251
if (is.list(formula))
3352
Terms <- if (missing(data)) terms(formula[[1]], specials=ss) else
@@ -60,7 +79,6 @@ coxph <- function(formula, data, weights, subset, na.action,
6079
indx <- match(c("formula", "data", "weights", "subset", "na.action",
6180
"cluster", "id", "istate"),
6281
names(Call), nomatch=0)
63-
if (indx[1] ==0) stop("A formula argument is required")
6482
tform <- Call[c(1,indx)] # only keep the arguments we wanted
6583
tform[[1L]] <- quote(stats::model.frame) # change the function called
6684

@@ -99,13 +117,6 @@ coxph <- function(formula, data, weights, subset, na.action,
99117
tform$formula <- if(missing(data)) terms(formula, special) else
100118
terms(formula, special, data=data)
101119

102-
# Make "tt" visible for coxph formulas, without making it visible elsewhere
103-
if (!is.null(attr(tform$formula, "specials")$tt)) {
104-
coxenv <- new.env(parent= environment(formula))
105-
assign("tt", function(x) x, envir=coxenv)
106-
environment(tform$formula) <- coxenv
107-
}
108-
109120
# okay, now evaluate the formula
110121
mf <- eval(tform, parent.frame())
111122
Terms <- terms(mf)
@@ -155,9 +166,14 @@ coxph <- function(formula, data, weights, subset, na.action,
155166
stop("multi-state models do not currently support pspline terms")
156167
if (length(attr(Terms, "specials")$ridge) >0)
157168
stop("multi-state models do not currently support ridge penalties")
158-
if (!missing.ties) method <- ties <- "breslow"
169+
if (missing.ties) method <- ties <- "breslow"
159170
}
160171

172+
# the code was never designed for multiple fraily terms, but of course
173+
# someone tried it
174+
if (length(attr(Terms, "specials")$frailty) >1)
175+
stop("multiple frailty terms are not supported")
176+
161177
if (control$timefix) Y <- aeqSurv(Y)
162178
if (length(attr(Terms, 'variables')) > 2) { # a ~1 formula has length 2
163179
ytemp <- innerterms(formula[1:2])
@@ -191,10 +207,6 @@ coxph <- function(formula, data, weights, subset, na.action,
191207
if (!hasinteractions) dropterms <- stemp$terms
192208
} else istrat <- NULL
193209

194-
if (hasinteractions && multi)
195-
stop("multi-state coxph does not support strata*covariate interactions")
196-
197-
198210
timetrans <- attr(Terms, "specials")$tt
199211
if (missing(tt)) tt <- NULL
200212
if (length(timetrans)) {

R/coxph.control.R

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,18 @@ coxph.control <- function(eps=1e-9,
66
iter.max=20,
77
toler.inf= sqrt(eps), outer.max=10,
88
timefix =TRUE) {
9-
if (iter.max <0) stop("Invalid value for iterations")
10-
if (eps <=0) stop ("Invalid convergence criteria")
9+
if (!is.numeric(iter.max) ||iter.max <0) stop("Invalid value for iterations")
10+
if (!is.numeric(eps) || eps <=0) stop ("Invalid convergence criteria")
11+
if (!is.numeric(toler.chol) || toler.chol <=0)
12+
stop("invalid value for toler.chol")
13+
if (!is.numeric(eps) || eps <=0) stop("eps must be > 0")
1114
if (eps <= toler.chol)
1215
warning("For numerical accuracy, tolerance should be < eps")
13-
if (toler.inf <=0) stop ("The inf.warn setting must be >0")
16+
if (!is.numeric(toler.inf) || toler.inf <=0)
17+
stop ("The toler.inf setting must be >0")
1418
if (!is.logical(timefix)) stop("timefix must be TRUE or FALSE")
19+
if (!is.numeric(outer.max) || outer.max <=0)
20+
stop("invalid value for outer.max")
1521
list(eps=eps, toler.chol=toler.chol, iter.max=as.integer(iter.max),
1622
toler.inf=toler.inf, outer.max=as.integer(outer.max),
1723
timefix=timefix)

0 commit comments

Comments
 (0)