Skip to content

Commit 4ca8376

Browse files
committed
0.5 add competing risk analysis
1 parent 83db994 commit 4ca8376

17 files changed

+141
-44
lines changed

DESCRIPTION

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Package: jskm
22
Title: Kaplan-Meier Plot with 'ggplot2'
3-
Version: 0.4.4
4-
Date: 2022-12-29
3+
Version: 0.5
4+
Date: 2023-03-18
55
Authors@R: c(person("Jinseob", "Kim", email = "[email protected]", role = c("aut", "cre"), comment = c(ORCID = "0000-0002-9403-605X")),
66
person("Zarathu", role = c("cph", "fnd"))
77
)
@@ -10,7 +10,7 @@ Depends: R (>= 3.4.0)
1010
License: Apache License 2.0
1111
Encoding: UTF-8
1212
Imports: ggplot2, ggpubr, survival, survey, scales
13-
RoxygenNote: 7.2.2
13+
RoxygenNote: 7.2.3
1414
URL: https://github.com/jinseob2kim/jskm
1515
BugReports: https://github.com/jinseob2kim/jstable/issues
1616
Suggests:

NEWS.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# jskm 0.5
2+
3+
* Add competing risk analysis to `jskm`
4+
15
# jskm 0.4.4
26

37
* Fix: align plot with table

R/jskm.R

+87-32
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#' @param data select specific data - for reactive input, Default = NULL
3333
#' @param cut.landmark cut-off for landmark analysis, Default = NULL
3434
#' @param showpercent Shows the percentages on the right side.
35+
#' @param status.cmprsk Status value when competing risk analysis, Default = 2nd level of status variable
3536
#' @param ... PARAM_DESCRIPTION
3637
#' @return Plot
3738
#' @details DETAILS
@@ -79,7 +80,7 @@
7980
jskm <- function(sfit,
8081
table = FALSE,
8182
xlabs = "Time-to-event",
82-
ylabs = "Survival probability",
83+
ylabs = NULL,
8384
xlims = c(0,max(sfit$time)),
8485
ylims = c(0,1),
8586
surv.scale = c("default", "percent"),
@@ -107,6 +108,7 @@ jskm <- function(sfit,
107108
data = NULL,
108109
cut.landmark = NULL,
109110
showpercent = F,
111+
status.cmprsk = NULL,
110112
...) {
111113

112114

@@ -148,12 +150,19 @@ jskm <- function(sfit,
148150
subs3 <- which(regexpr(ssvar,summary(sfit,times = times,extend = TRUE)$strata, perl=T)!=-1)
149151
}
150152

151-
if(!is.null(subs)) pval <- FALSE
153+
if(!is.null(subs) | !is.null(sfit$states)) pval <- FALSE
152154

153155
##################################
154156
# data manipulation pre-plotting #
155157
##################################
156158

159+
if (is.null(ylabs)){
160+
if (cumhaz | !is.null(sfit$states)){
161+
ylabs <- "Cumulative incidence"
162+
} else{
163+
ylabs <- "Survival probability"
164+
}
165+
}
157166

158167

159168
if(length(levels(summary(sfit)$strata)) == 0) {
@@ -171,21 +180,39 @@ jskm <- function(sfit,
171180
if(length(levels(summary(sfit)$strata)) == 0) {
172181
Factor <- factor(rep("All",length(subs2)))
173182
} else {
174-
Factor <- factor(summary(sfit, censored = T)$strata[subs2])
183+
Factor <- factor(summary(sfit, censored = T)$strata[subs2], levels = names(sfit$strata))
175184
}
176185

177186
#Data to be used in the survival plot
178187

179-
df <- data.frame(
180-
time = sfit$time[subs2],
181-
n.risk = sfit$n.risk[subs2],
182-
n.event = sfit$n.event[subs2],
183-
n.censor = sfit$n.censor[subs2],
184-
surv = sfit$surv[subs2],
185-
strata = Factor,
186-
upper = sfit$upper[subs2],
187-
lower = sfit$lower[subs2]
188-
)
188+
189+
if (is.null(sfit$state)){ # no cmprsk
190+
df <- data.frame(
191+
time = sfit$time[subs2],
192+
n.risk = sfit$n.risk[subs2],
193+
n.event = sfit$n.event[subs2],
194+
n.censor = sfit$n.censor[subs2],
195+
surv = sfit$surv[subs2],
196+
strata = Factor,
197+
upper = sfit$upper[subs2],
198+
lower = sfit$lower[subs2]
199+
)
200+
} else { #cmprsk
201+
if (is.null(status.cmprsk)){
202+
status.cmprsk <- sfit$states[2]
203+
}
204+
col.cmprsk <- which(sfit$state == status.cmprsk)
205+
df <- data.frame(
206+
time = sfit$time[subs2],
207+
n.risk = sfit$n.risk[, 1][subs2],
208+
n.event = sfit$n.event[, col.cmprsk][subs2],
209+
n.censor = sfit$n.censor[subs2],
210+
surv = sfit$pstate[, col.cmprsk][subs2],
211+
strata = Factor,
212+
upper = sfit$upper[, col.cmprsk][subs2],
213+
lower = sfit$lower[, col.cmprsk][subs2]
214+
)
215+
}
189216

190217
form <- sfit$call$formula
191218

@@ -210,24 +237,45 @@ jskm <- function(sfit,
210237
sfit1 <- survfit(as.formula(form), data1)
211238
sfit2 <- survfit(as.formula(form), data[data[[var.time]] >= cut.landmark, ])
212239

213-
if (length(levels(Factor)) == 1){
214-
df2 <- merge(subset(df, time >= cut.landmark)[, c("time", "n.risk", "n.event", "n.censor", "strata")],
215-
data.frame(time = sfit2$time, surv = sfit2$surv, strata = "All", upper = sfit2$upper, lower = sfit2$lower),
216-
by = c("time", "strata"))
240+
if (is.null(sfit$states)){
241+
if (length(levels(Factor)) == 1){
242+
df2 <- merge(subset(df, time >= cut.landmark)[, c("time", "n.risk", "n.event", "n.censor", "strata")],
243+
data.frame(time = sfit2$time, surv = sfit2$surv, strata = "All", upper = sfit2$upper, lower = sfit2$lower),
244+
by = c("time", "strata"))
245+
246+
} else{
247+
df2 <- merge(subset(df, time >= cut.landmark)[, c("time", "n.risk", "n.event", "n.censor", "strata")],
248+
data.frame(time = sfit2$time, surv = sfit2$surv, strata = rep(names(sfit2$strata), sfit2$strata), upper = sfit2$upper, lower = sfit2$lower),
249+
by = c("time", "strata"))
250+
}
251+
252+
df11 <- rbind(subset(df, time < cut.landmark), df2[, names(df)])
253+
df <- rbind(df11, data.frame(time = cut.landmark, n.risk = summary(sfit, times = cut.landmark)$n.risk[[1]], n.event = 0, n.censor = 0, surv = 1, strata = factor(ystratalabs, levels = levels(df$strata)), upper = 1, lower = 1))
217254
} else{
218-
df2 <- merge(subset(df, time >= cut.landmark)[, c("time", "n.risk", "n.event", "n.censor", "strata")],
219-
data.frame(time = sfit2$time, surv = sfit2$surv, strata = rep(names(sfit2$strata), sfit2$strata), upper = sfit2$upper, lower = sfit2$lower),
220-
by = c("time", "strata"))
221-
}
222-
255+
if (is.null(status.cmprsk)){
256+
status.cmprsk <- sfit$states[2]
257+
}
258+
col.cmprsk <- which(sfit$state == status.cmprsk)
259+
260+
if (length(levels(Factor)) == 1){
261+
df2 <- merge(subset(df, time >= cut.landmark)[, c("time", "n.risk", "n.event", "n.censor", "strata")],
262+
data.frame(time = sfit2$time, surv = sfit2$pstate[, col.cmprsk], strata = "All", upper = sfit2$upper[, col.cmprsk], lower = sfit2$lower[, col.cmprsk]),
263+
by = c("time", "strata"))
264+
265+
} else{
266+
df2 <- merge(subset(df, time >= cut.landmark)[, c("time", "n.risk", "n.event", "n.censor", "strata")],
267+
data.frame(time = sfit2$time, surv = sfit2$pstate[, col.cmprsk], strata = rep(names(sfit2$strata), sfit2$strata), upper = sfit2$upper[, col.cmprsk], lower = sfit2$lower[, col.cmprsk]),
268+
by = c("time", "strata"))
269+
}
270+
df11 <- rbind(subset(df, time < cut.landmark), df2[, names(df)])
271+
df <- rbind(df11, data.frame(time = cut.landmark, n.risk = summary(sfit, times = cut.landmark)$n.risk[[1]], n.event = 0, n.censor = 0, surv = 0, strata = factor(ystratalabs, levels = levels(df$strata)), upper = 0, lower = 0))
272+
}
223273

224274

225-
df11 <- rbind(subset(df, time < cut.landmark), df2)
226-
df <- rbind(df11, data.frame(time = cut.landmark, n.risk = summary(sfit, times = cut.landmark)$n.risk, n.event = 0, n.censor = 0, surv = 1, strata = factor(ystratalabs, levels = levels(df$strata)), upper = 1, lower = 1))
227275
}
228276

229277

230-
if (cumhaz){
278+
if (cumhaz & is.null(sfit$states)){
231279
upper.new <- 1 - df$lower
232280
lower.new <- 1 - df$upper
233281
df$surv = 1 - df$surv
@@ -241,10 +289,10 @@ jskm <- function(sfit,
241289
zeros <- data.frame(time = 0, n.risk = NA, n.event = NA, n.censor = NA, surv = 1,
242290
strata = factor(ystratalabs, levels=levels(df$strata)),
243291
upper = 1, lower = 1)
244-
if (cumhaz){
245-
zeros$surv = 0
246-
zeros$lower = 0
247-
zeros$upper = 0
292+
if (cumhaz | !is.null(sfit$states)){
293+
zeros$surv <- 0
294+
zeros$lower <- 0
295+
zeros$upper <- 0
248296
}
249297

250298
df <- rbind(zeros, df)
@@ -326,15 +374,22 @@ jskm <- function(sfit,
326374
p <- p + geom_vline(xintercept = cut.landmark, lty = 2)
327375
}
328376

329-
if (showpercent == TRUE){
377+
if (showpercent == T){
330378
if (is.null(cut.landmark)){
331379
y.percent <- summary(sfit, times = xlims[2], extend = T)$surv
332-
if (cumhaz == TRUE) y.percent <- 1 - y.percent
380+
if (!is.null(sfit$states)){
381+
y.percent <- summary(sfit, times = xlims[2], extend = T)$pstate[, col.cmprsk]
382+
}
383+
if (cumhaz == T & is.null(sfit$states)) y.percent <- 1 - y.percent
333384
p <- p + annotate(geom = "text", x = xlims[2], y = y.percent, label= paste0(round(100 * y.percent, 1), "%"), color = "black")
334385
} else{
335386
y.percent1 <- summary(sfit, times = cut.landmark, extend = T)$surv
336387
y.percent2 <- summary(sfit2, times = xlims[2], extend = T)$surv
337-
if (cumhaz == TRUE) {y.percent1 <- 1 - y.percent1;y.percent2 <- 1 - y.percent2}
388+
if (!is.null(sfit$states)){
389+
y.percent1 <- summary(sfit, times = cut.landmark, extend = T)$pstate[, col.cmprsk]
390+
y.percent2 <- summary(sfit2, times = xlims[2], extend = T)$pstate[, col.cmprsk]
391+
}
392+
if (cumhaz == T & is.null(sfit$states)) {y.percent1 <- 1 - y.percent1;y.percent2 <- 1 - y.percent2}
338393
p <- p + annotate(geom = "text", x = cut.landmark, y = y.percent1, label= paste0(round(100 * y.percent1, 1), "%"), color = "black") +
339394
annotate(geom = "text", x = xlims[2], y = y.percent2, label= paste0(round(100 * y.percent2, 1), "%"), color = "black")
340395
}

README.Rmd

+14
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,20 @@ jskm(fit, mark = F, surv.scale = "percent", pval =T, table = T, cut.landmark =
6363
jskm(fit, mark = F, surv.scale = "percent", pval =T, table = T, cut.landmark = 500, showpercent = T)
6464
```
6565

66+
### Competing risk analysis
67+
68+
`status2` variable: 0 - censoring, 1 - event, 2 - competing risk
69+
```{r}
70+
## Make competing risk variable, Not real
71+
colon$status2 <- colon$status
72+
colon$status2[1:400] <- 2
73+
colon$status2 <- factor(colon$status2)
74+
fit2 <- survfit(Surv(time,status2)~rx, data=colon)
75+
jskm(fit2, mark = F, surv.scale = "percent", table = T)
76+
jskm(fit2, mark = F, surv.scale = "percent", table = T, showpercent = T, cut.landmark = 500)
77+
```
78+
79+
6680
### Weighted Kaplan-Meier plot - `svykm.object` in **survey** package
6781

6882
```{r}

README.md

+29-8
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ Status](https://travis-ci.org/jinseob2kim/jskm.svg?branch=master)](https://travi
1010
status](https://ci.appveyor.com/api/projects/status/github/jinseob2kim/jskm?branch=master&svg=true)](https://ci.appveyor.com/project/jinseob2kim/jskm)
1111
[![Github
1212
action](https://github.com/jinseob2kim/jskm/workflows/R-CMD-check/badge.svg)](https://github.com/jinseob2kim/jskm/actions)
13-
[![CRAN\_Status\_Badge](https://www.r-pkg.org/badges/version/jskm)](https://cran.r-project.org/package=jskm)
14-
[![CRAN\_Download\_Badge](https://cranlogs.r-pkg.org/badges/jskm)](https://CRAN.R-project.org/package=jskm)
13+
[![CRAN_Status_Badge](https://www.r-pkg.org/badges/version/jskm)](https://cran.r-project.org/package=jskm)
14+
[![CRAN_Download_Badge](https://cranlogs.r-pkg.org/badges/jskm)](https://CRAN.R-project.org/package=jskm)
1515
[![codecov](https://codecov.io/github/jinseob2kim/jskm/branch/master/graphs/badge.svg)](https://codecov.io/github/jinseob2kim/jskm)
1616
[![GitHub
1717
issues](https://img.shields.io/github/issues/jinseob2kim/jskm.svg)](https://github.com/jinseob2kim/jskm/issues)
@@ -78,6 +78,27 @@ jskm(fit, mark = F, surv.scale = "percent", pval =T, table = T, cut.landmark =
7878

7979
![](man/figures/README-unnamed-chunk-3-2.png)<!-- -->
8080

81+
### Competing risk analysis
82+
83+
`status2` variable: 0 - censoring, 1 - event, 2 - competing risk
84+
85+
``` r
86+
## Make competing risk variable, Not real
87+
colon$status2 <- colon$status
88+
colon$status2[1:400] <- 2
89+
colon$status2 <- factor(colon$status2)
90+
fit2 <- survfit(Surv(time,status2)~rx, data=colon)
91+
jskm(fit2, mark = F, surv.scale = "percent", table = T)
92+
```
93+
94+
![](man/figures/README-unnamed-chunk-4-1.png)<!-- -->
95+
96+
``` r
97+
jskm(fit2, mark = F, surv.scale = "percent", table = T, showpercent = T, cut.landmark = 500)
98+
```
99+
100+
![](man/figures/README-unnamed-chunk-4-2.png)<!-- -->
101+
81102
### Weighted Kaplan-Meier plot - `svykm.object` in **survey** package
82103

83104
``` r
@@ -95,19 +116,19 @@ s2 <-svykm(Surv(time,status>0) ~ sex, design = dpbc)
95116
svyjskm(s1)
96117
```
97118

98-
![](man/figures/README-unnamed-chunk-4-1.png)<!-- -->
119+
![](man/figures/README-unnamed-chunk-5-1.png)<!-- -->
99120

100121
``` r
101122
svyjskm(s2, pval = T, table = T, design = dpbc)
102123
```
103124

104-
![](man/figures/README-unnamed-chunk-4-2.png)<!-- -->
125+
![](man/figures/README-unnamed-chunk-5-2.png)<!-- -->
105126

106127
``` r
107128
svyjskm(s2, cumhaz = T, ylab = "Cumulative incidence (%)", surv.scale = "percent", pval = T, design = dpbc, pval.coord = c(300, 0.7), showpercent = T)
108129
```
109130

110-
![](man/figures/README-unnamed-chunk-4-3.png)<!-- -->
131+
![](man/figures/README-unnamed-chunk-5-3.png)<!-- -->
111132

112133
If you want to get **confidence interval**, you should apply `se = T`
113134
option to `svykm` object.
@@ -117,16 +138,16 @@ s3 <- svykm(Surv(time,status>0) ~ sex, design=dpbc, se = T)
117138
svyjskm(s3)
118139
```
119140

120-
![](man/figures/README-unnamed-chunk-5-1.png)<!-- -->
141+
![](man/figures/README-unnamed-chunk-6-1.png)<!-- -->
121142

122143
``` r
123144
svyjskm(s3, ci = F)
124145
```
125146

126-
![](man/figures/README-unnamed-chunk-5-2.png)<!-- -->
147+
![](man/figures/README-unnamed-chunk-6-2.png)<!-- -->
127148

128149
``` r
129150
svyjskm(s3, ci = F, surv.scale = "percent", pval =T, table = T, cut.landmark = 1000, showpercent = T)
130151
```
131152

132-
![](man/figures/README-unnamed-chunk-5-3.png)<!-- -->
153+
![](man/figures/README-unnamed-chunk-6-3.png)<!-- -->
-118 Bytes
Loading
-540 Bytes
Loading
-686 Bytes
Loading
13.4 KB
Loading
12.3 KB
Loading
-4.1 KB
Loading
5.44 KB
Loading
-7.92 KB
Loading
21.6 KB
Loading
18.7 KB
Loading
30 KB
Loading

man/jskm.Rd

+4-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)