Skip to content

Commit 0633132

Browse files
Terry M Therneaucran-robot
authored andcommitted
version 3.5-7
1 parent 49c36d6 commit 0633132

Some content is hidden

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

105 files changed

+9155
-3593
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.5-5
5-
Date: 2023-03-11
4+
Version: 3.5-7
5+
Date: 2023-08-12
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: 2023-03-11 22:45:07 UTC; therneau
25+
Packaged: 2023-08-13 13:14:48 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: 2023-03-12 10:20:03 UTC
33+
Date/Publication: 2023-08-14 07:10:03 UTC

MD5

Lines changed: 100 additions & 80 deletions
Large diffs are not rendered by default.

NAMESPACE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ importFrom(methods, as)
1818
importFrom(utils, getS3method, type.convert, methods, head, tail)
1919
export(Surv, Surv2, Surv2data, aeqSurv, aareg, agreg.fit,
2020
agexact.fit, attrassign,
21-
blogit, bprobit, bcloglog, blog,
21+
blogit, bprobit, bcloglog, blog, brier,
2222
basehaz, cch, clogit, cipoisson, cluster, concordance, concordancefit,
2323
coxph, cox.zph, coxph.control, coxph.detail, coxph.fit,
2424
coxph.wtest, finegray, format.Surv,

R/aareg.R

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ aareg <- function(formula, data, weights, subset, na.action,
130130
ff <- .C(Ccoxdetail, as.integer(nused),
131131
as.integer(nvar),
132132
ndeath= as.integer(ndeath),
133+
center = colMeans(X),
133134
y = Y[ord,],
134135
as.double(X),
135136
index = as.integer(rep(0,nused)),

R/anova.coxph.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ anova.coxph <- function (object, ..., test = 'Chisq') {
9292
tfit <- coxph(Y ~ X[,assign <= alevels[i]] + strata(strats) +
9393
offset(object$offset), ties=mtie)
9494
else tfit <- coxph(Y ~ X[, assign<= alevels[i]] +
95-
offet(object$offset), ties=mtie)
95+
offset(object$offset), ties=mtie)
9696
}
9797
else {
9898
if (has.strata)

R/brier.R

Lines changed: 76 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,73 @@
1-
brier <- function(fit, times, newdata, ties=TRUE) {
2-
# Baseline predicted probabilities, use the same approximations as the
3-
# Cox model
4-
if (fit$method == "efron")
5-
s0 <- survfit(fit$y ~1, se.fit=FALSE, ctype=2, stype=2)
6-
else s0 <- survfit(fit$y ~1, se.fit=FALSE, stype=1)
1+
brier <- function(fit, times, newdata, ties=TRUE, detail =FALSE, timefix=TRUE,
2+
efron = FALSE) {
3+
Call <- match.call()
4+
if (!inherits(fit, "coxph")) stop("fit must be a coxph object")
5+
6+
if (missing(newdata)) mf <- stats::model.frame(fit)
7+
else mf <-stats::model.frame(fit, data=newdata)
8+
Y <- mf[[1]] # the survival object
9+
if (!is.Surv(Y)) stop("response must be a Surv object")
10+
type <- attr(Y, "type")
11+
if (!(type %in% c("right", "mright", "counting", "mcounting")))
12+
stop("response must be right censored")
13+
n <- nrow(Y)
14+
ny <- ncol(Y) # 3 = time1, time2 data
15+
16+
if (!is.logical(timefix) || length(timefix) > 1)
17+
stop("invalid value for timefix option")
18+
if (timefix) Y <- aeqSurv(Y)
19+
20+
casewt <- model.weights(mf)
21+
if (is.null(casewt)) casewt <- rep(1, n)
22+
else {
23+
if (!is.numeric(casewt)) stop("weights must be numeric")
24+
if (any(!is.finite(casewt))) stop("weights must be finite")
25+
if (any(casewt <0)) stop("weights must be non-negative")
26+
casewt <- as.numeric(casewt) # transform integer to numeric
27+
}
28+
29+
id <- model.extract(mf, "(id)")
30+
if (ny==3 && (is.null(id))) stop("id is required for start-stop data")
31+
if (!is.null(id)) {
32+
if (is.null(attr(Y, 'states'))) {
33+
ytemp <- Y
34+
attr(ytemp, 'states') <- 'event' # survcheck2 wants a states attr
35+
check <- survcheck2(ytemp, id)
36+
}
37+
else check <- survcheck2(Y, id)
38+
39+
if (any(check$flag > 0))
40+
stop("one or more flags are >0 in survcheck")
41+
n.startstate <- sum(check$transitions[,1] >1)
42+
if (ny ==2) samestart=TRUE # everyone starts at the same time
43+
else {
44+
etemp <- tapply(Y[,1], id, min)
45+
samestart <- all(etemp== etemp[1])
46+
}
47+
} else check <- NULL
48+
if (length(id)==0) id <- seq.int(n)
49+
50+
# Finally, it's time to do the work.
51+
if (is.null(check) || (n.startstate==1 & samestart)) simple <- TRUE
52+
else simple <- FALSE
53+
if (!simple) stop("delayed entry is not yet implemented")
54+
55+
# For baseline predicted probabilities, use the same approximations as the
56+
# Cox model, if allowed by the 'efron' option.
57+
if (efron && fit$method == "efron")
58+
s0 <- survfit(Y ~1, weights= casewt, se.fit=FALSE, ctype=2, stype=2)
59+
else s0 <- survfit(Y ~1, weights= casewt,se.fit=FALSE, stype=1)
760
if (missing(times)) times <- s0$time[s0$n.event >0]
861
p0 <- 1- summary(s0, times, extend=TRUE)$surv
962

1063
# model predictions
11-
s1 <- survfit(fit, newdata= fit$call$data, se.fit=FALSE)# FALSE is faster
64+
if (missing(newdata))
65+
s1 <- survfit(fit, newdata= fit$call$data, se.fit=FALSE)# FALSE is faster
66+
else s1 <- survfit(fit, newdata= newdata, se.fit=FALSE)
1267
p1 <- 1- summary(s1, times, extend=TRUE)$surv
1368

14-
ny <- ncol(fit$y)
15-
dtime <- fit$y[,ny-1] # time and status in the data
16-
dstat <- fit$y[,ny]
69+
dtime <- Y[,ny-1] # time and status in the data
70+
dstat <- Y[,ny]
1771
n <- length(dtime)
1872
ntime <- length(times)
1973

@@ -24,19 +78,25 @@ brier <- function(fit, times, newdata, ties=TRUE) {
2478
dtime <- dtime + ifelse(dstat==0, mindiff/2, 0)
2579
}
2680

27-
c0 <- survfit0(survfit(Surv(dtime, 1-dstat) ~ 1))
81+
c0 <- survfit0(survfit(Surv(dtime, 1-dstat) ~ 1, weights=casewt))
2882
b0 <- b1 <- matrix(0, nrow=n, ncol=ntime)
29-
wt <- rep(1/n, n) # everyone starts out with a weight of 1/n
83+
casewt <- casewt/sum(casewt)
3084
brier <- matrix(0, ntime, 2)
85+
eff.n <- double(ntime) # the effective n
3186
for (i in 1:ntime) {
3287
indx <- findInterval(pmin(dtime, times[i]), c0$time)
33-
wt <- ifelse(dtime < times[i] & dstat==0, 0, 1/c0$surv[indx])
88+
wt <- ifelse(dtime < times[i] & dstat==0, 0, casewt/c0$surv[indx])
89+
eff.n[i] <- 1/sum(wt^2) # the sum of the weights is always 1
3490

3591
b0[,i] <- ifelse(dtime > times[i], p0[i]^2, (dstat- p0[i])^2)
3692
b1[,i] <- ifelse(dtime > times[i], p1[i,]^2, (dstat- p1[i,])^2)
3793
brier[i,] <- c(sum(wt*b0[,i]), sum(wt*b1[,i]))/ sum(wt)
3894
}
39-
40-
dimnames(brier) <- list(times, c("NULL", "Model"))
41-
list(brier=brier, b0= b0, b1=b1, time=times)
95+
ret <- list(rsquared= 1- brier[,2]/brier[,1], brier=brier[,2], times=times)
96+
if (detail) {
97+
ret$p0 <- p0
98+
ret$phat <- p1
99+
ret$eff.n <- eff.n
100+
}
101+
ret
42102
}

R/concordance.R

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,11 @@ concordancefit <- function(y, x, strata, weights, ymin=NULL, ymax=NULL,
153153
if (timefix) y <- aeqSurv(y)
154154
if (!is.null(ymin)) {
155155
censored <- (y[,ny] ==0)
156-
if (any(y[censored, ny-1] < ymin))
157-
stop("data has a censored value less than ymin")
158-
else y[,ny-1] <- pmax(y[,ny-1], ymin)
156+
# relaxed this rule, 30 March 2023
157+
#if (any(y[censored, ny-1] < ymin))
158+
# stop("data has a censored value less than ymin")
159+
#else y[,ny-1] <- pmax(y[,ny-1], ymin)
160+
y[,ny-1] <- pmax(y[,ny-1], ymin)
159161
}
160162
} else {
161163
# should only occur if another package calls this routine

R/coxph.R

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -435,10 +435,15 @@ coxph <- function(formula, data, weights, subset, na.action,
435435
attr(X, "assign") <- Xatt$assign[!xdrop]
436436
attr(X, "contrasts") <- Xatt$contrasts
437437
offset <- model.offset(mf)
438-
if (is.null(offset) || all(offset==0)) offset <- rep(0., nrow(mf))
439-
else if (any(!is.finite(offset) | !is.finite(exp(offset))))
438+
if (is.null(offset) || all(offset==0)) {
439+
offset <- rep(0., nrow(mf))
440+
meanoffset <- 0
441+
} else if (any(!is.finite(offset) | !is.finite(exp(offset))))
440442
stop("offsets must lead to a finite risk score")
441-
else offset <- offset - mean(offset) # this can help stability of exp()
443+
else {
444+
meanoffset <- mean(offset)
445+
offset <- offset - meanoffset # this can help stability of exp()
446+
}
442447

443448
weights <- model.weights(mf)
444449
if (!is.null(weights) && any(!is.finite(weights)))
@@ -715,7 +720,8 @@ coxph <- function(formula, data, weights, subset, na.action,
715720
fit$formula <- formula(Terms)
716721
if (length(xlevels) >0) fit$xlevels <- xlevels
717722
fit$contrasts <- contr.save
718-
if (any(offset !=0)) fit$offset <- offset
723+
if (meanoffset !=0) fit$linear.predictors <- fit$linear.predictors + meanoffset
724+
if (x & any(offset !=0)) fit$offset <- offset
719725

720726
fit$call <- Call
721727
fit

R/coxph.detail.R

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ coxph.detail <- function(object, riskmat=FALSE, rorder=c("data", "time")) {
3131
newstrat[n] <- 1
3232

3333
# sort the data
34-
x <- x[ord,]
35-
y <- y[ord,]
34+
xnew <- x[ord,]
35+
ynew <- y[ord,]
3636
storage.mode(y) <- 'double'
3737
score <- exp(object$linear.predictors)[ord]
3838
if (is.null(weights)) weights <- rep(1.0, n)
@@ -48,8 +48,9 @@ coxph.detail <- function(object, riskmat=FALSE, rorder=c("data", "time")) {
4848
ff <- .C(Ccoxdetail, as.integer(n),
4949
as.integer(nvar),
5050
ndeath= as.integer(ndeath),
51-
y = y,
52-
as.double(x),
51+
center = object$means,
52+
y = ynew,
53+
as.double(xnew),
5354
index = as.integer(newstrat),
5455
event2 =as.double(score),
5556
weights = as.double(weights),
@@ -61,7 +62,7 @@ coxph.detail <- function(object, riskmat=FALSE, rorder=c("data", "time")) {
6162
double(nvar*(3 + 2*nvar)))
6263
keep <- 1:ff$ndeath
6364
vname<- dimnames(x)[[2]]
64-
time <- y[ff$index[keep],2]
65+
time <- ynew[ff$index[keep],2]
6566
names(time) <- NULL
6667
means<- (matrix(ff$means,ndeath, nvar))[keep,]
6768
score<- matrix(ff$u, ndeath, nvar)[keep,]
@@ -86,7 +87,10 @@ coxph.detail <- function(object, riskmat=FALSE, rorder=c("data", "time")) {
8687
dimnames(ff$y) <- NULL
8788
temp <- list(time = time, means=means, nevent=ff$y[keep,1],
8889
nrisk = ff$y[keep,2], hazard= ff$y[keep,3], score= score, imat=var,
89-
varhaz=ff$weights[keep], y=y, x=x)
90+
varhaz=ff$weights[keep], wtrisk = ff$nrisk2[keep])
91+
if (rorder == "data") {
92+
temp$y <- y; temp$x <-x}
93+
else {temp$y <- ynew; temp$x <- xnew}
9094
if (length(strat)) temp$strata <- table((strat[ord])[ff$index[keep]])
9195
if (riskmat) {
9296
if (rorder=="data") {

R/residcsum.R

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# This function is internal, used by residuals survfit
2+
# Cumsum of each column, restart the sum anew for each stratum
3+
residcsum <- function(y, strata) {
4+
if (!is.matrix(y)) stop("y must be a matrix")
5+
if (!is.integer(strata) || length(strata) != nrow(y))
6+
stop("invalid strata")
7+
storage.mode(y) <- "double"
8+
.Call(Cresidcsum, y, strata)
9+
}

0 commit comments

Comments
 (0)