├── inst └── WORDLIST ├── vignettes ├── .gitignore ├── plot_cs.Rmd └── estimate_cs.Rmd ├── .gitignore ├── .Rbuildignore ├── docs ├── articles │ ├── plot_cs_files │ │ └── figure-html │ │ │ └── unnamed-chunk-3-1.png │ ├── index.html │ ├── plot_cs.html │ └── estimate_cs.html ├── pkgdown.yml ├── link.svg ├── sitemap.xml ├── bootstrap-toc.css ├── docsearch.js ├── pkgdown.js ├── bootstrap-toc.js ├── 404.html ├── reference │ ├── index.html │ ├── conditional_surv_est.html │ └── gg_conditional_surv.html ├── authors.html ├── index.html ├── pkgdown.css └── docsearch.css ├── NAMESPACE ├── condsurv.Rproj ├── _pkgdown.yml ├── DESCRIPTION ├── README.md ├── README.Rmd ├── man ├── gg_conditional_surv.Rd └── conditional_surv_est.Rd ├── archive ├── condKMplot.R └── condKMapp.R └── R ├── conditional_surv_est.R └── gg_conditional_surv.R /inst/WORDLIST: -------------------------------------------------------------------------------- 1 | ggplot 2 | Kaplan 3 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | inst/doc 6 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^archive$ 4 | ^README\.Rmd$ 5 | ^data-raw$ 6 | ^_pkgdown\.yml$ 7 | ^docs$ 8 | ^pkgdown$ 9 | -------------------------------------------------------------------------------- /docs/articles/plot_cs_files/figure-html/unnamed-chunk-3-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zabore/condsurv/HEAD/docs/articles/plot_cs_files/figure-html/unnamed-chunk-3-1.png -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(conditional_surv_est) 4 | export(gg_conditional_surv) 5 | importFrom(magrittr,"%>%") 6 | importFrom(rlang,.data) 7 | -------------------------------------------------------------------------------- /docs/pkgdown.yml: -------------------------------------------------------------------------------- 1 | pandoc: '2.18' 2 | pkgdown: 2.0.6 3 | pkgdown_sha: ~ 4 | articles: 5 | estimate_cs: estimate_cs.html 6 | plot_cs: plot_cs.html 7 | last_built: 2022-10-20T15:11Z 8 | urls: 9 | reference: http://www.emilyzabor.com/condsurv/reference 10 | article: http://www.emilyzabor.com/condsurv/articles 11 | 12 | -------------------------------------------------------------------------------- /condsurv.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageRoxygenize: rd,collate,namespace 22 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: http://www.emilyzabor.com/condsurv/ 2 | 3 | template: 4 | params: 5 | bootswatch: lumen 6 | 7 | navbar: 8 | type: default 9 | left: 10 | - icon: fa-home 11 | href: index.html 12 | - text: Function Documentation 13 | href: reference/index.html 14 | - text: Tutorials 15 | menu: 16 | - text: "Estimate conditional survival" 17 | href: articles/estimate_cs.html 18 | - text: "Plot conditional survival curves" 19 | href: articles/plot_cs.html 20 | right: 21 | - icon: fa-github fa-lg 22 | href: https://github.com/zabore/condsurv 23 | 24 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: condsurv 2 | Title: Conditional survival estimates and plots 3 | Version: 1.0.0 4 | Date: 2018-04-07 5 | Authors@R: c(person("Emily C.", "Zabor", email = "zabore2@ccf.org", role = c("aut", "cre")), person("Mithat", "Gonen", role = "aut")) 6 | Description: Functions to produce conditional survival estimates with 95% confidence intervals, and to plot Kaplan-Meier conditional survival estimates over a range of conditioned times. 7 | Depends: 8 | R (>= 3.1.0) 9 | License: GPL-2 10 | LazyData: TRUE 11 | Imports: 12 | survival, 13 | dplyr, 14 | ggplot2, 15 | purrr, 16 | tibble, 17 | magrittr, 18 | rlang 19 | RoxygenNote: 7.2.1 20 | Encoding: UTF-8 21 | Suggests: 22 | knitr, 23 | rmarkdown 24 | VignetteBuilder: knitr 25 | URL: http://www.emilyzabor.com/condsurv/ 26 | Roxygen: list(markdown = TRUE) 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # condsurv 5 | 6 | The `condsurv` package contains a function for generating conditional 7 | survival estimates with associated confidence intervals, and a function 8 | for plotting conditional survival curves. 9 | 10 | ## Installation 11 | 12 | Install the package using 13 | 14 | ``` r 15 | remotes::install_github("zabore/condsurv") 16 | ``` 17 | 18 | ## Documentation 19 | 20 | This package is documented using 21 | [pkgdown](https://pkgdown.r-lib.org/articles/pkgdown.html), and the 22 | resulting website is available at , 23 | where detailed Tutorials can be found covering all of the package 24 | functionality. 25 | 26 | See for detailed 27 | function documentation. 28 | -------------------------------------------------------------------------------- /docs/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | # condsurv 8 | 9 | The `condsurv` package contains a function for generating conditional survival estimates with associated confidence intervals, and a function for plotting conditional survival curves. 10 | 11 | ## Installation 12 | 13 | Install the package using 14 | 15 | ```{r eval = FALSE} 16 | remotes::install_github("zabore/condsurv") 17 | ``` 18 | 19 | ## Documentation 20 | 21 | This package is documented using [pkgdown](https://pkgdown.r-lib.org/articles/pkgdown.html), and the resulting website is available at [http://www.emilyzabor.com/condsurv/](http://www.emilyzabor.com/condsurv/), where detailed Tutorials can be found covering all of the package functionality. 22 | 23 | See [http://www.emilyzabor.com/condsurv/reference/](http://www.emilyzabor.com/condsurv/reference/) for detailed function documentation. 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | http://www.emilyzabor.com/condsurv/404.html 5 | 6 | 7 | http://www.emilyzabor.com/condsurv/articles/estimate_cs.html 8 | 9 | 10 | http://www.emilyzabor.com/condsurv/articles/index.html 11 | 12 | 13 | http://www.emilyzabor.com/condsurv/articles/plot_cs.html 14 | 15 | 16 | http://www.emilyzabor.com/condsurv/authors.html 17 | 18 | 19 | http://www.emilyzabor.com/condsurv/index.html 20 | 21 | 22 | http://www.emilyzabor.com/condsurv/reference/conditional_surv_est.html 23 | 24 | 25 | http://www.emilyzabor.com/condsurv/reference/gg_conditional_surv.html 26 | 27 | 28 | http://www.emilyzabor.com/condsurv/reference/index.html 29 | 30 | 31 | -------------------------------------------------------------------------------- /man/gg_conditional_surv.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/gg_conditional_surv.R 3 | \name{gg_conditional_surv} 4 | \alias{gg_conditional_surv} 5 | \title{Generate conditional survival plots using ggplot2} 6 | \usage{ 7 | gg_conditional_surv( 8 | basekm, 9 | at, 10 | main = NULL, 11 | xlab = "Years", 12 | ylab = "Survival probability", 13 | lwd = 1 14 | ) 15 | } 16 | \arguments{ 17 | \item{basekm}{\code{survfit} object} 18 | 19 | \item{at}{vector of times on which to condition} 20 | 21 | \item{main}{plot title} 22 | 23 | \item{xlab}{x-axis label} 24 | 25 | \item{ylab}{y-axis label, defaults to "Survival probability"} 26 | 27 | \item{lwd}{plot line width, defaults to 1} 28 | } 29 | \value{ 30 | A ggplot with a line for the overall Kaplan-Meier plot and one 31 | additional line for each value in \code{at} 32 | 33 | #' @details See the vignette 34 | at \url{http://www.emilyzabor.com/condsurv/articles/plot_cs.html} for 35 | details and examples. 36 | } 37 | \description{ 38 | \code{gg_conditional_surv} produces a Kaplan-Meier plot for a variety of times on 39 | which to condition using ggplot2 40 | } 41 | -------------------------------------------------------------------------------- /man/conditional_surv_est.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/conditional_surv_est.R 3 | \name{conditional_surv_est} 4 | \alias{conditional_surv_est} 5 | \title{Estimate conditional survival with a 95\% confidence interval} 6 | \usage{ 7 | conditional_surv_est(basekm, t1, t2) 8 | } 9 | \arguments{ 10 | \item{basekm}{\code{survfit} object} 11 | 12 | \item{t1}{the time on which to condition} 13 | 14 | \item{t2}{the survival time to estimate} 15 | } 16 | \value{ 17 | A list where \code{cs_est} is the conditional survival estimate, 18 | \code{cs_lci} is the lower bound of the 95\% confidence interval and 19 | \code{cs_uci} is the upper bound of the 95\% confidence interval 20 | } 21 | \description{ 22 | \code{conditional_surv_est} estimates the Kaplan-Meier conditional survival at 23 | fixed time points and produces a 95\% confidence interval 24 | } 25 | \details{ 26 | For example, if \code{t1} = 2 and \code{t2} = 5, the function 27 | will return the probability of surviving to year 5 conditioned on having 28 | already survived to year 2. See the vignette 29 | at \url{http://www.emilyzabor.com/condsurv/articles/estimate_cs.html} for 30 | details on calculations of conditional survival estimates and confidence 31 | intervals, and examples. 32 | } 33 | -------------------------------------------------------------------------------- /vignettes/plot_cs.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Plot conditional survival curves" 3 | author: "Emily C. Zabor" 4 | date: "Last updated: `r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{Plot conditional survival curves} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | ```{r, include = FALSE} 13 | knitr::opts_chunk$set( 14 | collapse = TRUE, 15 | comment = "#>" 16 | ) 17 | ``` 18 | 19 | ```{r setup, message = FALSE, warning = FALSE} 20 | library(condsurv) 21 | library(survival) 22 | library(ggplot2) 23 | library(dplyr) 24 | ``` 25 | 26 | To plot the conditional survival curves at baseline, and for those who have survived 6 months, 1 year, 1.5 years, and 2 years, we use the `gg_conditional_surv` function. 27 | 28 | The `lung` dataset from the `survival` package will be used to illustrate. 29 | 30 | ```{r message = FALSE} 31 | # Scale the time variable to be in years rather than days 32 | lung2 <- 33 | mutate( 34 | lung, 35 | os_yrs = time / 365.25 36 | ) 37 | ``` 38 | 39 | ```{r fig.width = 6, fig.height = 4} 40 | myfit <- survfit(Surv(os_yrs, status) ~ 1, data = lung2) 41 | 42 | cond_times <- seq(0, 2, 0.5) 43 | 44 | gg_conditional_surv( 45 | basekm = myfit, 46 | at = cond_times, 47 | main = "Conditional survival in lung data" 48 | ) 49 | ``` 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /archive/condKMplot.R: -------------------------------------------------------------------------------- 1 | #' Generate conditional survival plots 2 | #' 3 | #' \code{condKMplot} produces a Kaplan-Meier plot for a variety of times on 4 | #' which to condition 5 | #' 6 | #' @param .basekm \code{survfit} object 7 | #' @param .at vector of times on which to condition 8 | #' @param .main plot title 9 | #' @param .xlab x-axis label 10 | #' @param .ylab y-axis label, defaults to "Survival probability" 11 | #' @param .lwd plot line width, defaults to 1 12 | #' @param .mark controls whether censoring times are marked on curves, 13 | #' defaults to F 14 | #' 15 | #' @return A plot with a line for the overall Kaplan-Meier plot and one 16 | #' additional line for each value in \code{.at} 17 | #' 18 | #' @export 19 | #' 20 | 21 | condKMplot <- function(.basekm, .at, .main = NULL, .xlab = NULL, 22 | .ylab = "Survival probability", .lwd = 1, .mark = F) { 23 | library(survival) 24 | 25 | if (class(.basekm) != "survfit") { 26 | stop("Argument to .basekm must be of class survfit") 27 | } 28 | if (max(.at) > max(.basekm$time)) { 29 | stop(paste( 30 | "Argument to .at specifies value(s) outside the range of observed times;", 31 | "the maximum observed time is", round(max(.basekm$time), 2) 32 | )) 33 | } 34 | plot(.basekm, 35 | conf.int = F, xlab = .xlab, ylab = .ylab, main = .main, 36 | lwd = .lwd, mark.time = .mark 37 | ) 38 | nt <- length(.at) 39 | fitkm <- list() 40 | for (i in 1:nt) { 41 | fitkm[[i]] <- survfit( 42 | formula = as.formula(.basekm$call$formula), 43 | data = eval(.basekm$call$data), 44 | start.time = .at[i] 45 | ) 46 | lines(fitkm[[i]], conf.int = F, col = i + 1, lwd = .lwd, mark.time = .mark) 47 | abline(v = i, lty = 3) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /docs/bootstrap-toc.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Table of Contents v0.4.1 (http://afeld.github.io/bootstrap-toc/) 3 | * Copyright 2015 Aidan Feldman 4 | * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */ 5 | 6 | /* modified from https://github.com/twbs/bootstrap/blob/94b4076dd2efba9af71f0b18d4ee4b163aa9e0dd/docs/assets/css/src/docs.css#L548-L601 */ 7 | 8 | /* All levels of nav */ 9 | nav[data-toggle='toc'] .nav > li > a { 10 | display: block; 11 | padding: 4px 20px; 12 | font-size: 13px; 13 | font-weight: 500; 14 | color: #767676; 15 | } 16 | nav[data-toggle='toc'] .nav > li > a:hover, 17 | nav[data-toggle='toc'] .nav > li > a:focus { 18 | padding-left: 19px; 19 | color: #563d7c; 20 | text-decoration: none; 21 | background-color: transparent; 22 | border-left: 1px solid #563d7c; 23 | } 24 | nav[data-toggle='toc'] .nav > .active > a, 25 | nav[data-toggle='toc'] .nav > .active:hover > a, 26 | nav[data-toggle='toc'] .nav > .active:focus > a { 27 | padding-left: 18px; 28 | font-weight: bold; 29 | color: #563d7c; 30 | background-color: transparent; 31 | border-left: 2px solid #563d7c; 32 | } 33 | 34 | /* Nav: second level (shown on .active) */ 35 | nav[data-toggle='toc'] .nav .nav { 36 | display: none; /* Hide by default, but at >768px, show it */ 37 | padding-bottom: 10px; 38 | } 39 | nav[data-toggle='toc'] .nav .nav > li > a { 40 | padding-top: 1px; 41 | padding-bottom: 1px; 42 | padding-left: 30px; 43 | font-size: 12px; 44 | font-weight: normal; 45 | } 46 | nav[data-toggle='toc'] .nav .nav > li > a:hover, 47 | nav[data-toggle='toc'] .nav .nav > li > a:focus { 48 | padding-left: 29px; 49 | } 50 | nav[data-toggle='toc'] .nav .nav > .active > a, 51 | nav[data-toggle='toc'] .nav .nav > .active:hover > a, 52 | nav[data-toggle='toc'] .nav .nav > .active:focus > a { 53 | padding-left: 28px; 54 | font-weight: 500; 55 | } 56 | 57 | /* from https://github.com/twbs/bootstrap/blob/e38f066d8c203c3e032da0ff23cd2d6098ee2dd6/docs/assets/css/src/docs.css#L631-L634 */ 58 | nav[data-toggle='toc'] .nav > .active > ul { 59 | display: block; 60 | } 61 | -------------------------------------------------------------------------------- /docs/docsearch.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | // register a handler to move the focus to the search bar 4 | // upon pressing shift + "/" (i.e. "?") 5 | $(document).on('keydown', function(e) { 6 | if (e.shiftKey && e.keyCode == 191) { 7 | e.preventDefault(); 8 | $("#search-input").focus(); 9 | } 10 | }); 11 | 12 | $(document).ready(function() { 13 | // do keyword highlighting 14 | /* modified from https://jsfiddle.net/julmot/bL6bb5oo/ */ 15 | var mark = function() { 16 | 17 | var referrer = document.URL ; 18 | var paramKey = "q" ; 19 | 20 | if (referrer.indexOf("?") !== -1) { 21 | var qs = referrer.substr(referrer.indexOf('?') + 1); 22 | var qs_noanchor = qs.split('#')[0]; 23 | var qsa = qs_noanchor.split('&'); 24 | var keyword = ""; 25 | 26 | for (var i = 0; i < qsa.length; i++) { 27 | var currentParam = qsa[i].split('='); 28 | 29 | if (currentParam.length !== 2) { 30 | continue; 31 | } 32 | 33 | if (currentParam[0] == paramKey) { 34 | keyword = decodeURIComponent(currentParam[1].replace(/\+/g, "%20")); 35 | } 36 | } 37 | 38 | if (keyword !== "") { 39 | $(".contents").unmark({ 40 | done: function() { 41 | $(".contents").mark(keyword); 42 | } 43 | }); 44 | } 45 | } 46 | }; 47 | 48 | mark(); 49 | }); 50 | }); 51 | 52 | /* Search term highlighting ------------------------------*/ 53 | 54 | function matchedWords(hit) { 55 | var words = []; 56 | 57 | var hierarchy = hit._highlightResult.hierarchy; 58 | // loop to fetch from lvl0, lvl1, etc. 59 | for (var idx in hierarchy) { 60 | words = words.concat(hierarchy[idx].matchedWords); 61 | } 62 | 63 | var content = hit._highlightResult.content; 64 | if (content) { 65 | words = words.concat(content.matchedWords); 66 | } 67 | 68 | // return unique words 69 | var words_uniq = [...new Set(words)]; 70 | return words_uniq; 71 | } 72 | 73 | function updateHitURL(hit) { 74 | 75 | var words = matchedWords(hit); 76 | var url = ""; 77 | 78 | if (hit.anchor) { 79 | url = hit.url_without_anchor + '?q=' + escape(words.join(" ")) + '#' + hit.anchor; 80 | } else { 81 | url = hit.url + '?q=' + escape(words.join(" ")); 82 | } 83 | 84 | return url; 85 | } 86 | -------------------------------------------------------------------------------- /R/conditional_surv_est.R: -------------------------------------------------------------------------------- 1 | #' Estimate conditional survival with a 95% confidence interval 2 | #' 3 | #' `conditional_surv_est` estimates the Kaplan-Meier conditional survival at 4 | #' fixed time points and produces a 95% confidence interval 5 | #' 6 | #' @param basekm `survfit` object 7 | #' @param t1 the time on which to condition 8 | #' @param t2 the survival time to estimate 9 | #' 10 | #' @details For example, if `t1` = 2 and `t2` = 5, the function 11 | #' will return the probability of surviving to year 5 conditioned on having 12 | #' already survived to year 2. See the vignette 13 | #' at [http://www.emilyzabor.com/condsurv/articles/estimate_cs.html](http://www.emilyzabor.com/condsurv/articles/estimate_cs.html) for 14 | #' details on calculations of conditional survival estimates and confidence 15 | #' intervals, and examples. 16 | #' 17 | #' @return A list where `cs_est` is the conditional survival estimate, 18 | #' `cs_lci` is the lower bound of the 95% confidence interval and 19 | #' `cs_uci` is the upper bound of the 95% confidence interval 20 | #' 21 | #' @export 22 | #' 23 | 24 | conditional_surv_est <- function(basekm, t1, t2) { 25 | if (class(basekm) != "survfit") { 26 | stop( 27 | "Argument to basekm must be of class survfit" 28 | ) 29 | } 30 | 31 | if (max(t1) > max(basekm$time)) { 32 | stop( 33 | paste( 34 | "Argument to t1 specifies a value outside the range of observed times;", "the maximum observed time is", round(max(basekm$time), 2) 35 | ) 36 | ) 37 | } 38 | 39 | if (max(t2) > max(basekm$time)) { 40 | stop(paste( 41 | "Argument to t2 specifies a value outside the range of observed times;", 42 | "the maximum observed time is", round(max(basekm$time), 2) 43 | )) 44 | } 45 | 46 | cs <- summary(basekm, times = c(t1, t2))$surv[2] / 47 | summary(basekm, times = c(t1, t2))$surv[1] 48 | 49 | cs.sq <- cs^2 50 | 51 | d <- basekm$n.event[basekm$time >= t1 & 52 | basekm$time <= t2 & 53 | basekm$n.event > 0] 54 | 55 | r <- basekm$n.risk[basekm$time >= t1 & 56 | basekm$time <= t2 & 57 | basekm$n.event > 0] 58 | 59 | dr <- d / (r * (r - d)) 60 | 61 | var.cs <- 1 / (log(cs)^2) * sum(dr) 62 | 63 | ci <- cs^(exp(c(1, -1) * stats::qnorm(0.975) * sqrt(var.cs))) 64 | 65 | ci.cs <- round(ci, 2) 66 | 67 | return( 68 | list( 69 | cs_est = round(cs, 2), 70 | cs_lci = ci.cs[1], 71 | cs_uci = ci.cs[2] 72 | ) 73 | ) 74 | } 75 | -------------------------------------------------------------------------------- /R/gg_conditional_surv.R: -------------------------------------------------------------------------------- 1 | #' Generate conditional survival plots using ggplot2 2 | #' 3 | #' `gg_conditional_surv` produces a Kaplan-Meier plot for a variety of times on 4 | #' which to condition using ggplot2 5 | #' 6 | #' @param basekm `survfit` object 7 | #' @param at vector of times on which to condition 8 | #' @param main plot title 9 | #' @param xlab x-axis label 10 | #' @param ylab y-axis label, defaults to "Survival probability" 11 | #' @param lwd plot line width, defaults to 1 12 | #' 13 | #' @return A ggplot with a line for the overall Kaplan-Meier plot and one 14 | #' additional line for each value in `at` 15 | #' 16 | #' #' @details See the vignette 17 | #' at [http://www.emilyzabor.com/condsurv/articles/plot_cs.html](http://www.emilyzabor.com/condsurv/articles/plot_cs.html) for 18 | #' details and examples. 19 | #' 20 | #' @importFrom magrittr "%>%" 21 | #' @importFrom rlang .data 22 | #' 23 | #' @export 24 | #' 25 | 26 | gg_conditional_surv <- function(basekm, 27 | at, 28 | main = NULL, 29 | xlab = "Years", 30 | ylab = "Survival probability", 31 | lwd = 1) { 32 | if (class(basekm) != "survfit") { 33 | stop( 34 | "Argument to basekm must be of class survfit" 35 | ) 36 | } 37 | 38 | if (max(at) > max(basekm$time)) { 39 | stop( 40 | paste( 41 | "Argument to at specifies value(s) outside the range of observed times;", 42 | "the maximum observed time is", round(max(basekm$time), 2) 43 | ) 44 | ) 45 | } 46 | 47 | nt <- length(at) 48 | 49 | fitkm <- list() 50 | fitkmdat <- list() 51 | 52 | for (i in 1:nt) { 53 | fitkm[[i]] <- survival::survfit( 54 | formula = stats::as.formula(basekm$call$formula), 55 | data = eval(basekm$call$data), 56 | start.time = at[i] 57 | ) 58 | 59 | fitkmdat[[i]] <- tibble::tibble( 60 | timept = fitkm[[i]]$time, 61 | prob = fitkm[[i]]$surv 62 | ) 63 | } 64 | 65 | condsurvdat <- fitkmdat %>% 66 | purrr::map_df(`[`, .id = "which_at") %>% 67 | dplyr::mutate(condtime = factor(which_at, levels = seq(1, nt), labels = at)) 68 | 69 | ggplot2::ggplot( 70 | condsurvdat, 71 | ggplot2::aes(x = timept, y = prob, color = condtime) 72 | ) + 73 | ggplot2::geom_step(lwd = lwd) + 74 | ggplot2::ylim(0, 1) + 75 | ggplot2::labs( 76 | x = xlab, 77 | y = ylab, 78 | title = main 79 | ) + 80 | ggplot2::labs(color = "x") + 81 | ggplot2::theme_bw() 82 | } 83 | -------------------------------------------------------------------------------- /vignettes/estimate_cs.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Estimate conditional survival" 3 | author: "Emily C. Zabor" 4 | date: "Last updated: `r Sys.Date()`" 5 | output: rmarkdown::html_vignette 6 | vignette: > 7 | %\VignetteIndexEntry{Estimate conditional survival} 8 | %\VignetteEngine{knitr::rmarkdown} 9 | %\VignetteEncoding{UTF-8} 10 | --- 11 | 12 | ```{r, include = FALSE} 13 | knitr::opts_chunk$set( 14 | collapse = TRUE, 15 | comment = "#>" 16 | ) 17 | ``` 18 | 19 | ```{r setup, message = FALSE, warning = FALSE} 20 | library(condsurv) 21 | library(dplyr) 22 | library(survival) 23 | ``` 24 | 25 | 26 | If $S(t)$ represents the survival function at time $t$, then conditional survival is defined as 27 | 28 | $$S(y|x) = \frac{S(x + y)}{S(x)}$$ 29 | 30 | where $y$ is the number of additional survival years of interest and $x$ is the number of years a subject has already survived. 31 | 32 | 33 | ## Generating conditional survival estimates 34 | 35 | The `conditional_surv_est` function will generate this estimate along with 95\% confidence intervals. 36 | 37 | The `lung` dataset from the `survival` package will be used to illustrate. 38 | 39 | ```{r message = FALSE} 40 | # Scale the time variable to be in years rather than days 41 | lung2 <- 42 | mutate( 43 | lung, 44 | os_yrs = time / 365.25 45 | ) 46 | ``` 47 | 48 | First generate a single conditional survival estimate. This is the conditional survival of surviving to 1 year conditioned on already having survived 6 months ($0.5$ year). This returns a list, where `cs_est` is the conditional survival estimate, `cs_lci` is the lower bound of the 95\% confidence interval and `cs_uci` is the upper bound of the 95\% confidence interval. 49 | 50 | ```{r} 51 | myfit <- survfit(Surv(os_yrs, status) ~ 1, data = lung2) 52 | 53 | conditional_surv_est( 54 | basekm = myfit, 55 | t1 = 0.5, 56 | t2 = 1 57 | ) 58 | ``` 59 | 60 | You can easily use `purrr::map_df` to get a table of estimates for multiple timepoints. For example we could get the conditional survival estimate of surviving to a variety of different time points given that the subject has already survived for 6 months (0.5 years). 61 | 62 | ```{r} 63 | prob_times <- seq(1, 2.5, 0.5) 64 | 65 | purrr::map_df( 66 | prob_times, 67 | ~conditional_surv_est( 68 | basekm = myfit, 69 | t1 = 0.5, 70 | t2 = .x) 71 | ) %>% 72 | dplyr::mutate(years = prob_times) %>% 73 | dplyr::select(years, everything()) %>% 74 | knitr::kable() 75 | ``` 76 | 77 | ## A note on confidence interval estimation 78 | 79 | The confidence intervals are based on a variation of the log-log transformation, also known as the "exponential" Greenwood formula, where the conditional survival estimate is substituted in for the traditional survival estimate in constructing the confidence interval. 80 | 81 | If $\hat{S}(y|x)$ is the estimated conditional survival to $y$ given having already survived to $x$, then 82 | 83 | $$\hat{S}(y|x)^{exp(\pm1.96\sqrt{\hat{L}(y|x)})}$$ 84 | 85 | where 86 | 87 | $$\hat{L}(y|x)=\frac{1}{\log(\hat{S}(y|x))^2}\sum_{j:x \leq \tau_j \leq y}\frac{d_j}{(r_j-d_j)r_j}$$ 88 | 89 | and 90 | 91 | $\tau_j$ = distinct death time $j$ 92 | 93 | $d_j$ = number of failures at death time $j$ 94 | 95 | $r_j$ = number at risk at death time $j$ 96 | -------------------------------------------------------------------------------- /docs/pkgdown.js: -------------------------------------------------------------------------------- 1 | /* http://gregfranko.com/blog/jquery-best-practices/ */ 2 | (function($) { 3 | $(function() { 4 | 5 | $('.navbar-fixed-top').headroom(); 6 | 7 | $('body').css('padding-top', $('.navbar').height() + 10); 8 | $(window).resize(function(){ 9 | $('body').css('padding-top', $('.navbar').height() + 10); 10 | }); 11 | 12 | $('[data-toggle="tooltip"]').tooltip(); 13 | 14 | var cur_path = paths(location.pathname); 15 | var links = $("#navbar ul li a"); 16 | var max_length = -1; 17 | var pos = -1; 18 | for (var i = 0; i < links.length; i++) { 19 | if (links[i].getAttribute("href") === "#") 20 | continue; 21 | // Ignore external links 22 | if (links[i].host !== location.host) 23 | continue; 24 | 25 | var nav_path = paths(links[i].pathname); 26 | 27 | var length = prefix_length(nav_path, cur_path); 28 | if (length > max_length) { 29 | max_length = length; 30 | pos = i; 31 | } 32 | } 33 | 34 | // Add class to parent
  • , and enclosing
  • if in dropdown 35 | if (pos >= 0) { 36 | var menu_anchor = $(links[pos]); 37 | menu_anchor.parent().addClass("active"); 38 | menu_anchor.closest("li.dropdown").addClass("active"); 39 | } 40 | }); 41 | 42 | function paths(pathname) { 43 | var pieces = pathname.split("/"); 44 | pieces.shift(); // always starts with / 45 | 46 | var end = pieces[pieces.length - 1]; 47 | if (end === "index.html" || end === "") 48 | pieces.pop(); 49 | return(pieces); 50 | } 51 | 52 | // Returns -1 if not found 53 | function prefix_length(needle, haystack) { 54 | if (needle.length > haystack.length) 55 | return(-1); 56 | 57 | // Special case for length-0 haystack, since for loop won't run 58 | if (haystack.length === 0) { 59 | return(needle.length === 0 ? 0 : -1); 60 | } 61 | 62 | for (var i = 0; i < haystack.length; i++) { 63 | if (needle[i] != haystack[i]) 64 | return(i); 65 | } 66 | 67 | return(haystack.length); 68 | } 69 | 70 | /* Clipboard --------------------------*/ 71 | 72 | function changeTooltipMessage(element, msg) { 73 | var tooltipOriginalTitle=element.getAttribute('data-original-title'); 74 | element.setAttribute('data-original-title', msg); 75 | $(element).tooltip('show'); 76 | element.setAttribute('data-original-title', tooltipOriginalTitle); 77 | } 78 | 79 | if(ClipboardJS.isSupported()) { 80 | $(document).ready(function() { 81 | var copyButton = ""; 82 | 83 | $("div.sourceCode").addClass("hasCopyButton"); 84 | 85 | // Insert copy buttons: 86 | $(copyButton).prependTo(".hasCopyButton"); 87 | 88 | // Initialize tooltips: 89 | $('.btn-copy-ex').tooltip({container: 'body'}); 90 | 91 | // Initialize clipboard: 92 | var clipboardBtnCopies = new ClipboardJS('[data-clipboard-copy]', { 93 | text: function(trigger) { 94 | return trigger.parentNode.textContent.replace(/\n#>[^\n]*/g, ""); 95 | } 96 | }); 97 | 98 | clipboardBtnCopies.on('success', function(e) { 99 | changeTooltipMessage(e.trigger, 'Copied!'); 100 | e.clearSelection(); 101 | }); 102 | 103 | clipboardBtnCopies.on('error', function() { 104 | changeTooltipMessage(e.trigger,'Press Ctrl+C or Command+C to copy'); 105 | }); 106 | }); 107 | } 108 | })(window.jQuery || window.$) 109 | -------------------------------------------------------------------------------- /docs/bootstrap-toc.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Table of Contents v0.4.1 (http://afeld.github.io/bootstrap-toc/) 3 | * Copyright 2015 Aidan Feldman 4 | * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */ 5 | (function() { 6 | 'use strict'; 7 | 8 | window.Toc = { 9 | helpers: { 10 | // return all matching elements in the set, or their descendants 11 | findOrFilter: function($el, selector) { 12 | // http://danielnouri.org/notes/2011/03/14/a-jquery-find-that-also-finds-the-root-element/ 13 | // http://stackoverflow.com/a/12731439/358804 14 | var $descendants = $el.find(selector); 15 | return $el.filter(selector).add($descendants).filter(':not([data-toc-skip])'); 16 | }, 17 | 18 | generateUniqueIdBase: function(el) { 19 | var text = $(el).text(); 20 | var anchor = text.trim().toLowerCase().replace(/[^A-Za-z0-9]+/g, '-'); 21 | return anchor || el.tagName.toLowerCase(); 22 | }, 23 | 24 | generateUniqueId: function(el) { 25 | var anchorBase = this.generateUniqueIdBase(el); 26 | for (var i = 0; ; i++) { 27 | var anchor = anchorBase; 28 | if (i > 0) { 29 | // add suffix 30 | anchor += '-' + i; 31 | } 32 | // check if ID already exists 33 | if (!document.getElementById(anchor)) { 34 | return anchor; 35 | } 36 | } 37 | }, 38 | 39 | generateAnchor: function(el) { 40 | if (el.id) { 41 | return el.id; 42 | } else { 43 | var anchor = this.generateUniqueId(el); 44 | el.id = anchor; 45 | return anchor; 46 | } 47 | }, 48 | 49 | createNavList: function() { 50 | return $(''); 51 | }, 52 | 53 | createChildNavList: function($parent) { 54 | var $childList = this.createNavList(); 55 | $parent.append($childList); 56 | return $childList; 57 | }, 58 | 59 | generateNavEl: function(anchor, text) { 60 | var $a = $(''); 61 | $a.attr('href', '#' + anchor); 62 | $a.text(text); 63 | var $li = $('
  • '); 64 | $li.append($a); 65 | return $li; 66 | }, 67 | 68 | generateNavItem: function(headingEl) { 69 | var anchor = this.generateAnchor(headingEl); 70 | var $heading = $(headingEl); 71 | var text = $heading.data('toc-text') || $heading.text(); 72 | return this.generateNavEl(anchor, text); 73 | }, 74 | 75 | // Find the first heading level (`

    `, then `

    `, etc.) that has more than one element. Defaults to 1 (for `

    `). 76 | getTopLevel: function($scope) { 77 | for (var i = 1; i <= 6; i++) { 78 | var $headings = this.findOrFilter($scope, 'h' + i); 79 | if ($headings.length > 1) { 80 | return i; 81 | } 82 | } 83 | 84 | return 1; 85 | }, 86 | 87 | // returns the elements for the top level, and the next below it 88 | getHeadings: function($scope, topLevel) { 89 | var topSelector = 'h' + topLevel; 90 | 91 | var secondaryLevel = topLevel + 1; 92 | var secondarySelector = 'h' + secondaryLevel; 93 | 94 | return this.findOrFilter($scope, topSelector + ',' + secondarySelector); 95 | }, 96 | 97 | getNavLevel: function(el) { 98 | return parseInt(el.tagName.charAt(1), 10); 99 | }, 100 | 101 | populateNav: function($topContext, topLevel, $headings) { 102 | var $context = $topContext; 103 | var $prevNav; 104 | 105 | var helpers = this; 106 | $headings.each(function(i, el) { 107 | var $newNav = helpers.generateNavItem(el); 108 | var navLevel = helpers.getNavLevel(el); 109 | 110 | // determine the proper $context 111 | if (navLevel === topLevel) { 112 | // use top level 113 | $context = $topContext; 114 | } else if ($prevNav && $context === $topContext) { 115 | // create a new level of the tree and switch to it 116 | $context = helpers.createChildNavList($prevNav); 117 | } // else use the current $context 118 | 119 | $context.append($newNav); 120 | 121 | $prevNav = $newNav; 122 | }); 123 | }, 124 | 125 | parseOps: function(arg) { 126 | var opts; 127 | if (arg.jquery) { 128 | opts = { 129 | $nav: arg 130 | }; 131 | } else { 132 | opts = arg; 133 | } 134 | opts.$scope = opts.$scope || $(document.body); 135 | return opts; 136 | } 137 | }, 138 | 139 | // accepts a jQuery object, or an options object 140 | init: function(opts) { 141 | opts = this.helpers.parseOps(opts); 142 | 143 | // ensure that the data attribute is in place for styling 144 | opts.$nav.attr('data-toggle', 'toc'); 145 | 146 | var $topContext = this.helpers.createChildNavList(opts.$nav); 147 | var topLevel = this.helpers.getTopLevel(opts.$scope); 148 | var $headings = this.helpers.getHeadings(opts.$scope, topLevel); 149 | this.helpers.populateNav($topContext, topLevel, $headings); 150 | } 151 | }; 152 | 153 | $(function() { 154 | $('nav[data-toggle="toc"]').each(function(i, el) { 155 | var $nav = $(el); 156 | Toc.init($nav); 157 | }); 158 | }); 159 | })(); 160 | -------------------------------------------------------------------------------- /docs/articles/index.html: -------------------------------------------------------------------------------- 1 | 2 | Articles • condsurv 6 | 7 | 8 |
    9 |
    56 | 57 | 58 | 59 |
    60 |
    61 | 64 | 65 |
    66 |

    All vignettes

    67 |

    68 | 69 |
    Estimate conditional survival
    70 |
    71 |
    Plot conditional survival curves
    72 |
    73 |
    74 |
    75 |
    76 | 77 | 78 |
    81 | 82 |
    83 |

    Site built with pkgdown 2.0.6.

    84 |
    85 | 86 |
    87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Page not found (404) • condsurv 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 |
    24 |
    79 | 80 | 81 | 82 | 83 |
    84 |
    85 | 88 | 89 | Content not found. Please use links in the navbar. 90 | 91 |
    92 | 93 | 97 | 98 |
    99 | 100 | 101 | 102 |
    106 | 107 |
    108 |

    109 |

    Site built with pkgdown 2.0.6.

    110 |
    111 | 112 |
    113 |
    114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /archive/condKMapp.R: -------------------------------------------------------------------------------- 1 | #' Interactive conditional survival Shiny app 2 | #' 3 | #' \code{condKMapp} produces an interactive Shiny app containing output from 4 | #' both the \code{condKMest} function and the \code{condKMplot} function. 5 | #' 6 | #' @param .basekm \code{survfit} object 7 | #' 8 | #' @return The output includes 1) a plot with one overall Kaplan-Meier survival 9 | #' curve and an additional Kaplan-Meier survival curve for each time on which 10 | #' you are conditioning and 2) a table with conditional survival estimates at 11 | #' the specified survival time along with their associated confidence intervals, 12 | #' with one row for each time on which you are conditioning. 13 | #' 14 | #' @export 15 | #' 16 | 17 | condKMapp <- function(.basekm) { 18 | library(shiny) 19 | library(survival) 20 | 21 | shinyApp( 22 | ui = fluidPage( 23 | pageWithSidebar( 24 | headerPanel("Conditional survival estimates"), 25 | sidebarPanel( 26 | helpText("This interactive interface allows you to adjust the 27 | survival time of interest (using the numeric entry box below) 28 | and to select a single survival time, or range of survival times, on 29 | which to condition (using the slider bar below)."), 30 | numericInput( 31 | inputId = "survtime", label = "Enter the survival time of interest (in years)", 32 | value = 5, min = 0, max = 10 33 | ), 34 | sliderInput( 35 | inputId = "condtime", label = "Select the range of times on which to condition (in years)", 36 | value = c(1, 4), min = 1, max = 10, step = 1 37 | ), 38 | helpText("The output includes 1) a plot with one overall Kaplan-Meier 39 | survival curve and an additional Kaplan-Meier survival curve for 40 | each time on which you are conditioning and 2) a table 41 | with conditional survival estimates at the specified 42 | survival time along with their associated confidence intervals, 43 | with one row for each time on which you are conditioning.") 44 | ), 45 | mainPanel( 46 | plotOutput(outputId = "condplot"), 47 | tableOutput(outputId = "condtab") 48 | ) 49 | ) 50 | ), 51 | 52 | server = function(input, output) { 53 | output$condplot <- renderPlot({ 54 | if (class(.basekm) != "survfit") { 55 | stop("Argument to .basekm must be of class survfit") 56 | } 57 | if (max(input$condtime) > max(.basekm$time)) { 58 | stop(paste( 59 | "The range of times on which to condition specifies value(s) outside the range of observed times;", 60 | "the maximum observed time is", round(max(.basekm$time), 2) 61 | )) 62 | } 63 | 64 | plot(.basekm, 65 | conf.int = F, xlab = "Years", ylab = "Survival probability", 66 | lwd = 1, mark.time = F, main = "Conditional survival curves" 67 | ) 68 | at <- seq(from = min(input$condtime), to = max(input$condtime), by = 1) 69 | nt <- length(at) 70 | fitkm <- list() 71 | for (i in 1:nt) { 72 | fitkm[[i]] <- survfit( 73 | formula = as.formula(.basekm$call$formula), 74 | data = eval(.basekm$call$data), 75 | start.time = at[i] 76 | ) 77 | lines(fitkm[[i]], conf.int = F, col = i + 1, lwd = 1, mark.time = F) 78 | abline(v = i, lty = 3) 79 | } 80 | }, height = 400, width = 500) 81 | 82 | output$condtab <- renderTable({ 83 | if (class(.basekm) != "survfit") { 84 | stop("Argument to .basekm must be of class survfit") 85 | } 86 | if (max(input$condtime) > max(.basekm$time)) { 87 | stop(paste( 88 | "The range of times on which to condition specifies value(s) outside the range of observed times;", 89 | "the maximum observed time is", round(max(.basekm$time), 2) 90 | )) 91 | } 92 | if (max(input$survtime) > max(.basekm$time)) { 93 | stop(paste( 94 | "The survival time of interest specifies a value outside the range of observed times;", 95 | "the maximum observed time is", round(max(.basekm$time), 2) 96 | )) 97 | } 98 | 99 | at <- seq(from = min(input$condtime), to = max(input$condtime), by = 1) 100 | nt <- length(at) 101 | cstab <- NULL 102 | for (i in 1:nt) { 103 | if (at[i] < input$survtime) { 104 | cs <- summary(.basekm, times = c(at[i], input$survtime))$surv[2] / 105 | summary(.basekm, times = c(at[i], input$survtime))$surv[1] 106 | cs.sq <- cs^2 107 | d <- .basekm$n.event[.basekm$time >= at[i] & .basekm$time <= input$survtime & 108 | .basekm$n.event > 0] 109 | r <- .basekm$n.risk[.basekm$time >= at[i] & .basekm$time <= input$survtime & 110 | .basekm$n.event > 0] 111 | dr <- d / (r * (r - d)) 112 | var.cs <- cs.sq * sum(dr) 113 | ci <- cs + c(-1, 1) * (qnorm(0.975) * sqrt(var.cs)) 114 | if (ci[1] < 0) { 115 | warning("Lower bound of CI has been truncated to 0") 116 | } 117 | if (ci[2] > 1) { 118 | warning("Upper bound of CI has been truncated to 1") 119 | } 120 | ci.cs <- round(ci, 2) 121 | if (ci.cs[1] < 0) { 122 | ci.cs[1] <- 0 123 | } 124 | if (ci.cs[2] > 1) { 125 | ci.cs[2] <- 1 126 | } 127 | cstab <- data.frame(rbind(cstab, c(round(at[i]), round(cs, 2), ci.cs[1], ci.cs[2])), 128 | stringsAsFactors = FALSE 129 | ) 130 | } 131 | } 132 | colnames(cstab) <- c("Conditional time", "Conditional survival", "Lower 95% CI", "Upper 95% CI") 133 | cstab 134 | }, digits = c(0, 0, 2, 2, 2)) 135 | } 136 | ) 137 | } 138 | -------------------------------------------------------------------------------- /docs/reference/index.html: -------------------------------------------------------------------------------- 1 | 2 | Function reference • condsurv 6 | 7 | 8 |
    9 |
    56 | 57 | 58 | 59 |
    60 |
    61 | 64 | 65 | 69 | 72 | 73 | 76 | 77 |
    66 |

    All functions

    67 |

    68 |
    70 |

    conditional_surv_est()

    71 |

    Estimate conditional survival with a 95% confidence interval

    74 |

    gg_conditional_surv()

    75 |

    Generate conditional survival plots using ggplot2

    78 | 79 | 82 |
    83 | 84 | 85 |
    88 | 89 |
    90 |

    Site built with pkgdown 2.0.6.

    91 |
    92 | 93 |
    94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /docs/authors.html: -------------------------------------------------------------------------------- 1 | 2 | Authors and Citation • condsurv 6 | 7 | 8 |
    9 |
    56 | 57 | 58 | 59 |
    60 |
    61 |
    62 | 65 | 66 | 67 |
    • 68 |

      Emily C. Zabor. Author, maintainer. 69 |

      70 |
    • 71 |
    • 72 |

      Mithat Gonen. Author. 73 |

      74 |
    • 75 |
    76 |
    77 |
    78 |

    Citation

    79 | 80 |
    81 |
    82 | 83 | 84 |

    Zabor E, Gonen M (2022). 85 | condsurv: Conditional survival estimates and plots. 86 | R package version 1.0.0, http://www.emilyzabor.com/condsurv/. 87 |

    88 |
    @Manual{,
     89 |   title = {condsurv: Conditional survival estimates and plots},
     90 |   author = {Emily C. Zabor and Mithat Gonen},
     91 |   year = {2022},
     92 |   note = {R package version 1.0.0},
     93 |   url = {http://www.emilyzabor.com/condsurv/},
     94 | }
    95 | 96 |
    97 | 98 |
    99 | 100 | 101 | 102 |
    105 | 106 |
    107 |

    Site built with pkgdown 2.0.6.

    108 |
    109 | 110 |
    111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /docs/reference/conditional_surv_est.html: -------------------------------------------------------------------------------- 1 | 2 | Estimate conditional survival with a 95% confidence interval — conditional_surv_est • condsurv 7 | 8 | 9 |
    10 |
    57 | 58 | 59 | 60 |
    61 |
    62 | 67 | 68 |
    69 |

    conditional_surv_est estimates the Kaplan-Meier conditional survival at 70 | fixed time points and produces a 95% confidence interval

    71 |
    72 | 73 |
    74 |
    conditional_surv_est(basekm, t1, t2)
    75 |
    76 | 77 |
    78 |

    Arguments

    79 |
    basekm
    80 |

    survfit object

    81 | 82 | 83 |
    t1
    84 |

    the time on which to condition

    85 | 86 | 87 |
    t2
    88 |

    the survival time to estimate

    89 | 90 |
    91 |
    92 |

    Value

    93 | 94 | 95 |

    A list where cs_est is the conditional survival estimate, 96 | cs_lci is the lower bound of the 95% confidence interval and 97 | cs_uci is the upper bound of the 95% confidence interval

    98 |
    99 |
    100 |

    Details

    101 |

    For example, if t1 = 2 and t2 = 5, the function 102 | will return the probability of surviving to year 5 conditioned on having 103 | already survived to year 2. See the vignette 104 | at http://www.emilyzabor.com/condsurv/articles/estimate_cs.html for 105 | details on calculations of conditional survival estimates and confidence 106 | intervals, and examples.

    107 |
    108 | 109 |
    110 | 113 |
    114 | 115 | 116 |
    119 | 120 |
    121 |

    Site built with pkgdown 2.0.6.

    122 |
    123 | 124 |
    125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /docs/reference/gg_conditional_surv.html: -------------------------------------------------------------------------------- 1 | 2 | Generate conditional survival plots using ggplot2 — gg_conditional_surv • condsurv 7 | 8 | 9 |
    10 |
    57 | 58 | 59 | 60 |
    61 |
    62 | 67 | 68 |
    69 |

    gg_conditional_surv produces a Kaplan-Meier plot for a variety of times on 70 | which to condition using ggplot2

    71 |
    72 | 73 |
    74 |
    gg_conditional_surv(
     75 |   basekm,
     76 |   at,
     77 |   main = NULL,
     78 |   xlab = "Years",
     79 |   ylab = "Survival probability",
     80 |   lwd = 1
     81 | )
    82 |
    83 | 84 |
    85 |

    Arguments

    86 |
    basekm
    87 |

    survfit object

    88 | 89 | 90 |
    at
    91 |

    vector of times on which to condition

    92 | 93 | 94 |
    main
    95 |

    plot title

    96 | 97 | 98 |
    xlab
    99 |

    x-axis label

    100 | 101 | 102 |
    ylab
    103 |

    y-axis label, defaults to "Survival probability"

    104 | 105 | 106 |
    lwd
    107 |

    plot line width, defaults to 1

    108 | 109 |
    110 |
    111 |

    Value

    112 | 113 | 114 |

    A ggplot with a line for the overall Kaplan-Meier plot and one 115 | additional line for each value in at

    116 | 117 | 118 | 119 |

    #' @details See the vignette 120 | at http://www.emilyzabor.com/condsurv/articles/plot_cs.html for 121 | details and examples.

    122 |
    123 | 124 |
    125 | 128 |
    129 | 130 | 131 |
    134 | 135 |
    136 |

    Site built with pkgdown 2.0.6.

    137 |
    138 | 139 |
    140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Conditional survival estimates and plots • condsurv 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 20 | 21 | 22 | 23 | 24 |
    25 |
    80 | 81 | 82 | 83 | 84 |
    85 |
    86 |
    87 | 89 |

    The condsurv package contains a function for generating conditional survival estimates with associated confidence intervals, and a function for plotting conditional survival curves.

    90 |
    91 |

    Installation 92 |

    93 |

    Install the package using

    94 |
     95 | remotes::install_github("zabore/condsurv")
    96 |
    97 |
    98 |

    Documentation 99 |

    100 |

    This package is documented using pkgdown, and the resulting website is available at http://www.emilyzabor.com/condsurv/, where detailed Tutorials can be found covering all of the package functionality.

    101 |

    See http://www.emilyzabor.com/condsurv/reference/ for detailed function documentation.

    102 |
    103 |
    104 |
    105 | 106 | 133 |
    134 | 135 | 136 |
    140 | 141 |
    142 |

    143 |

    Site built with pkgdown 2.0.6.

    144 |
    145 | 146 |
    147 |
    148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /docs/pkgdown.css: -------------------------------------------------------------------------------- 1 | /* Sticky footer */ 2 | 3 | /** 4 | * Basic idea: https://philipwalton.github.io/solved-by-flexbox/demos/sticky-footer/ 5 | * Details: https://github.com/philipwalton/solved-by-flexbox/blob/master/assets/css/components/site.css 6 | * 7 | * .Site -> body > .container 8 | * .Site-content -> body > .container .row 9 | * .footer -> footer 10 | * 11 | * Key idea seems to be to ensure that .container and __all its parents__ 12 | * have height set to 100% 13 | * 14 | */ 15 | 16 | html, body { 17 | height: 100%; 18 | } 19 | 20 | body { 21 | position: relative; 22 | } 23 | 24 | body > .container { 25 | display: flex; 26 | height: 100%; 27 | flex-direction: column; 28 | } 29 | 30 | body > .container .row { 31 | flex: 1 0 auto; 32 | } 33 | 34 | footer { 35 | margin-top: 45px; 36 | padding: 35px 0 36px; 37 | border-top: 1px solid #e5e5e5; 38 | color: #666; 39 | display: flex; 40 | flex-shrink: 0; 41 | } 42 | footer p { 43 | margin-bottom: 0; 44 | } 45 | footer div { 46 | flex: 1; 47 | } 48 | footer .pkgdown { 49 | text-align: right; 50 | } 51 | footer p { 52 | margin-bottom: 0; 53 | } 54 | 55 | img.icon { 56 | float: right; 57 | } 58 | 59 | /* Ensure in-page images don't run outside their container */ 60 | .contents img { 61 | max-width: 100%; 62 | height: auto; 63 | } 64 | 65 | /* Fix bug in bootstrap (only seen in firefox) */ 66 | summary { 67 | display: list-item; 68 | } 69 | 70 | /* Typographic tweaking ---------------------------------*/ 71 | 72 | .contents .page-header { 73 | margin-top: calc(-60px + 1em); 74 | } 75 | 76 | dd { 77 | margin-left: 3em; 78 | } 79 | 80 | /* Section anchors ---------------------------------*/ 81 | 82 | a.anchor { 83 | display: none; 84 | margin-left: 5px; 85 | width: 20px; 86 | height: 20px; 87 | 88 | background-image: url(./link.svg); 89 | background-repeat: no-repeat; 90 | background-size: 20px 20px; 91 | background-position: center center; 92 | } 93 | 94 | h1:hover .anchor, 95 | h2:hover .anchor, 96 | h3:hover .anchor, 97 | h4:hover .anchor, 98 | h5:hover .anchor, 99 | h6:hover .anchor { 100 | display: inline-block; 101 | } 102 | 103 | /* Fixes for fixed navbar --------------------------*/ 104 | 105 | .contents h1, .contents h2, .contents h3, .contents h4 { 106 | padding-top: 60px; 107 | margin-top: -40px; 108 | } 109 | 110 | /* Navbar submenu --------------------------*/ 111 | 112 | .dropdown-submenu { 113 | position: relative; 114 | } 115 | 116 | .dropdown-submenu>.dropdown-menu { 117 | top: 0; 118 | left: 100%; 119 | margin-top: -6px; 120 | margin-left: -1px; 121 | border-radius: 0 6px 6px 6px; 122 | } 123 | 124 | .dropdown-submenu:hover>.dropdown-menu { 125 | display: block; 126 | } 127 | 128 | .dropdown-submenu>a:after { 129 | display: block; 130 | content: " "; 131 | float: right; 132 | width: 0; 133 | height: 0; 134 | border-color: transparent; 135 | border-style: solid; 136 | border-width: 5px 0 5px 5px; 137 | border-left-color: #cccccc; 138 | margin-top: 5px; 139 | margin-right: -10px; 140 | } 141 | 142 | .dropdown-submenu:hover>a:after { 143 | border-left-color: #ffffff; 144 | } 145 | 146 | .dropdown-submenu.pull-left { 147 | float: none; 148 | } 149 | 150 | .dropdown-submenu.pull-left>.dropdown-menu { 151 | left: -100%; 152 | margin-left: 10px; 153 | border-radius: 6px 0 6px 6px; 154 | } 155 | 156 | /* Sidebar --------------------------*/ 157 | 158 | #pkgdown-sidebar { 159 | margin-top: 30px; 160 | position: -webkit-sticky; 161 | position: sticky; 162 | top: 70px; 163 | } 164 | 165 | #pkgdown-sidebar h2 { 166 | font-size: 1.5em; 167 | margin-top: 1em; 168 | } 169 | 170 | #pkgdown-sidebar h2:first-child { 171 | margin-top: 0; 172 | } 173 | 174 | #pkgdown-sidebar .list-unstyled li { 175 | margin-bottom: 0.5em; 176 | } 177 | 178 | /* bootstrap-toc tweaks ------------------------------------------------------*/ 179 | 180 | /* All levels of nav */ 181 | 182 | nav[data-toggle='toc'] .nav > li > a { 183 | padding: 4px 20px 4px 6px; 184 | font-size: 1.5rem; 185 | font-weight: 400; 186 | color: inherit; 187 | } 188 | 189 | nav[data-toggle='toc'] .nav > li > a:hover, 190 | nav[data-toggle='toc'] .nav > li > a:focus { 191 | padding-left: 5px; 192 | color: inherit; 193 | border-left: 1px solid #878787; 194 | } 195 | 196 | nav[data-toggle='toc'] .nav > .active > a, 197 | nav[data-toggle='toc'] .nav > .active:hover > a, 198 | nav[data-toggle='toc'] .nav > .active:focus > a { 199 | padding-left: 5px; 200 | font-size: 1.5rem; 201 | font-weight: 400; 202 | color: inherit; 203 | border-left: 2px solid #878787; 204 | } 205 | 206 | /* Nav: second level (shown on .active) */ 207 | 208 | nav[data-toggle='toc'] .nav .nav { 209 | display: none; /* Hide by default, but at >768px, show it */ 210 | padding-bottom: 10px; 211 | } 212 | 213 | nav[data-toggle='toc'] .nav .nav > li > a { 214 | padding-left: 16px; 215 | font-size: 1.35rem; 216 | } 217 | 218 | nav[data-toggle='toc'] .nav .nav > li > a:hover, 219 | nav[data-toggle='toc'] .nav .nav > li > a:focus { 220 | padding-left: 15px; 221 | } 222 | 223 | nav[data-toggle='toc'] .nav .nav > .active > a, 224 | nav[data-toggle='toc'] .nav .nav > .active:hover > a, 225 | nav[data-toggle='toc'] .nav .nav > .active:focus > a { 226 | padding-left: 15px; 227 | font-weight: 500; 228 | font-size: 1.35rem; 229 | } 230 | 231 | /* orcid ------------------------------------------------------------------- */ 232 | 233 | .orcid { 234 | font-size: 16px; 235 | color: #A6CE39; 236 | /* margins are required by official ORCID trademark and display guidelines */ 237 | margin-left:4px; 238 | margin-right:4px; 239 | vertical-align: middle; 240 | } 241 | 242 | /* Reference index & topics ----------------------------------------------- */ 243 | 244 | .ref-index th {font-weight: normal;} 245 | 246 | .ref-index td {vertical-align: top; min-width: 100px} 247 | .ref-index .icon {width: 40px;} 248 | .ref-index .alias {width: 40%;} 249 | .ref-index-icons .alias {width: calc(40% - 40px);} 250 | .ref-index .title {width: 60%;} 251 | 252 | .ref-arguments th {text-align: right; padding-right: 10px;} 253 | .ref-arguments th, .ref-arguments td {vertical-align: top; min-width: 100px} 254 | .ref-arguments .name {width: 20%;} 255 | .ref-arguments .desc {width: 80%;} 256 | 257 | /* Nice scrolling for wide elements --------------------------------------- */ 258 | 259 | table { 260 | display: block; 261 | overflow: auto; 262 | } 263 | 264 | /* Syntax highlighting ---------------------------------------------------- */ 265 | 266 | pre, code, pre code { 267 | background-color: #f8f8f8; 268 | color: #333; 269 | } 270 | pre, pre code { 271 | white-space: pre-wrap; 272 | word-break: break-all; 273 | overflow-wrap: break-word; 274 | } 275 | 276 | pre { 277 | border: 1px solid #eee; 278 | } 279 | 280 | pre .img, pre .r-plt { 281 | margin: 5px 0; 282 | } 283 | 284 | pre .img img, pre .r-plt img { 285 | background-color: #fff; 286 | } 287 | 288 | code a, pre a { 289 | color: #375f84; 290 | } 291 | 292 | a.sourceLine:hover { 293 | text-decoration: none; 294 | } 295 | 296 | .fl {color: #1514b5;} 297 | .fu {color: #000000;} /* function */ 298 | .ch,.st {color: #036a07;} /* string */ 299 | .kw {color: #264D66;} /* keyword */ 300 | .co {color: #888888;} /* comment */ 301 | 302 | .error {font-weight: bolder;} 303 | .warning {font-weight: bolder;} 304 | 305 | /* Clipboard --------------------------*/ 306 | 307 | .hasCopyButton { 308 | position: relative; 309 | } 310 | 311 | .btn-copy-ex { 312 | position: absolute; 313 | right: 0; 314 | top: 0; 315 | visibility: hidden; 316 | } 317 | 318 | .hasCopyButton:hover button.btn-copy-ex { 319 | visibility: visible; 320 | } 321 | 322 | /* headroom.js ------------------------ */ 323 | 324 | .headroom { 325 | will-change: transform; 326 | transition: transform 200ms linear; 327 | } 328 | .headroom--pinned { 329 | transform: translateY(0%); 330 | } 331 | .headroom--unpinned { 332 | transform: translateY(-100%); 333 | } 334 | 335 | /* mark.js ----------------------------*/ 336 | 337 | mark { 338 | background-color: rgba(255, 255, 51, 0.5); 339 | border-bottom: 2px solid rgba(255, 153, 51, 0.3); 340 | padding: 1px; 341 | } 342 | 343 | /* vertical spacing after htmlwidgets */ 344 | .html-widget { 345 | margin-bottom: 10px; 346 | } 347 | 348 | /* fontawesome ------------------------ */ 349 | 350 | .fab { 351 | font-family: "Font Awesome 5 Brands" !important; 352 | } 353 | 354 | /* don't display links in code chunks when printing */ 355 | /* source: https://stackoverflow.com/a/10781533 */ 356 | @media print { 357 | code a:link:after, code a:visited:after { 358 | content: ""; 359 | } 360 | } 361 | 362 | /* Section anchors --------------------------------- 363 | Added in pandoc 2.11: https://github.com/jgm/pandoc-templates/commit/9904bf71 364 | */ 365 | 366 | div.csl-bib-body { } 367 | div.csl-entry { 368 | clear: both; 369 | } 370 | .hanging-indent div.csl-entry { 371 | margin-left:2em; 372 | text-indent:-2em; 373 | } 374 | div.csl-left-margin { 375 | min-width:2em; 376 | float:left; 377 | } 378 | div.csl-right-inline { 379 | margin-left:2em; 380 | padding-left:1em; 381 | } 382 | div.csl-indent { 383 | margin-left: 2em; 384 | } 385 | -------------------------------------------------------------------------------- /docs/articles/plot_cs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Plot conditional survival curves • condsurv 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 20 | 21 | 22 | 23 | 24 |
    25 |
    80 | 81 | 82 | 83 | 84 |
    85 |
    86 | 97 | 98 | 99 | 100 |
    101 | library(condsurv)
    102 | library(survival)
    103 | #> Warning: package 'survival' was built under R version 4.2.1
    104 | library(ggplot2)
    105 | library(dplyr)
    106 | #> Warning: package 'dplyr' was built under R version 4.2.1
    107 |

    To plot the conditional survival curves at baseline, and for those 108 | who have survived 6 months, 1 year, 1.5 years, and 2 years, we use the 109 | gg_conditional_surv function.

    110 |

    The lung dataset from the survival package 111 | will be used to illustrate.

    112 |
    113 | # Scale the time variable to be in years rather than days
    114 | lung2 <- 
    115 |   mutate(
    116 |     lung,
    117 |     os_yrs = time / 365.25
    118 |   )
    119 |
    120 | myfit <- survfit(Surv(os_yrs, status) ~ 1, data = lung2)
    121 | 
    122 | cond_times <- seq(0, 2, 0.5)
    123 | 
    124 | gg_conditional_surv(
    125 |   basekm = myfit, 
    126 |   at = cond_times,
    127 |   main = "Conditional survival in lung data"
    128 |   ) 
    129 |

    130 |
    131 | 132 | 135 | 136 |
    137 | 138 | 139 | 140 |
    144 | 145 |
    146 |

    147 |

    Site built with pkgdown 2.0.6.

    148 |
    149 | 150 |
    151 |
    152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /docs/docsearch.css: -------------------------------------------------------------------------------- 1 | /* Docsearch -------------------------------------------------------------- */ 2 | /* 3 | Source: https://github.com/algolia/docsearch/ 4 | License: MIT 5 | */ 6 | 7 | .algolia-autocomplete { 8 | display: block; 9 | -webkit-box-flex: 1; 10 | -ms-flex: 1; 11 | flex: 1 12 | } 13 | 14 | .algolia-autocomplete .ds-dropdown-menu { 15 | width: 100%; 16 | min-width: none; 17 | max-width: none; 18 | padding: .75rem 0; 19 | background-color: #fff; 20 | background-clip: padding-box; 21 | border: 1px solid rgba(0, 0, 0, .1); 22 | box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .175); 23 | } 24 | 25 | @media (min-width:768px) { 26 | .algolia-autocomplete .ds-dropdown-menu { 27 | width: 175% 28 | } 29 | } 30 | 31 | .algolia-autocomplete .ds-dropdown-menu::before { 32 | display: none 33 | } 34 | 35 | .algolia-autocomplete .ds-dropdown-menu [class^=ds-dataset-] { 36 | padding: 0; 37 | background-color: rgb(255,255,255); 38 | border: 0; 39 | max-height: 80vh; 40 | } 41 | 42 | .algolia-autocomplete .ds-dropdown-menu .ds-suggestions { 43 | margin-top: 0 44 | } 45 | 46 | .algolia-autocomplete .algolia-docsearch-suggestion { 47 | padding: 0; 48 | overflow: visible 49 | } 50 | 51 | .algolia-autocomplete .algolia-docsearch-suggestion--category-header { 52 | padding: .125rem 1rem; 53 | margin-top: 0; 54 | font-size: 1.3em; 55 | font-weight: 500; 56 | color: #00008B; 57 | border-bottom: 0 58 | } 59 | 60 | .algolia-autocomplete .algolia-docsearch-suggestion--wrapper { 61 | float: none; 62 | padding-top: 0 63 | } 64 | 65 | .algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column { 66 | float: none; 67 | width: auto; 68 | padding: 0; 69 | text-align: left 70 | } 71 | 72 | .algolia-autocomplete .algolia-docsearch-suggestion--content { 73 | float: none; 74 | width: auto; 75 | padding: 0 76 | } 77 | 78 | .algolia-autocomplete .algolia-docsearch-suggestion--content::before { 79 | display: none 80 | } 81 | 82 | .algolia-autocomplete .ds-suggestion:not(:first-child) .algolia-docsearch-suggestion--category-header { 83 | padding-top: .75rem; 84 | margin-top: .75rem; 85 | border-top: 1px solid rgba(0, 0, 0, .1) 86 | } 87 | 88 | .algolia-autocomplete .ds-suggestion .algolia-docsearch-suggestion--subcategory-column { 89 | display: block; 90 | padding: .1rem 1rem; 91 | margin-bottom: 0.1; 92 | font-size: 1.0em; 93 | font-weight: 400 94 | /* display: none */ 95 | } 96 | 97 | .algolia-autocomplete .algolia-docsearch-suggestion--title { 98 | display: block; 99 | padding: .25rem 1rem; 100 | margin-bottom: 0; 101 | font-size: 0.9em; 102 | font-weight: 400 103 | } 104 | 105 | .algolia-autocomplete .algolia-docsearch-suggestion--text { 106 | padding: 0 1rem .5rem; 107 | margin-top: -.25rem; 108 | font-size: 0.8em; 109 | font-weight: 400; 110 | line-height: 1.25 111 | } 112 | 113 | .algolia-autocomplete .algolia-docsearch-footer { 114 | width: 110px; 115 | height: 20px; 116 | z-index: 3; 117 | margin-top: 10.66667px; 118 | float: right; 119 | font-size: 0; 120 | line-height: 0; 121 | } 122 | 123 | .algolia-autocomplete .algolia-docsearch-footer--logo { 124 | background-image: url("data:image/svg+xml;utf8,"); 125 | background-repeat: no-repeat; 126 | background-position: 50%; 127 | background-size: 100%; 128 | overflow: hidden; 129 | text-indent: -9000px; 130 | width: 100%; 131 | height: 100%; 132 | display: block; 133 | transform: translate(-8px); 134 | } 135 | 136 | .algolia-autocomplete .algolia-docsearch-suggestion--highlight { 137 | color: #FF8C00; 138 | background: rgba(232, 189, 54, 0.1) 139 | } 140 | 141 | 142 | .algolia-autocomplete .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight { 143 | box-shadow: inset 0 -2px 0 0 rgba(105, 105, 105, .5) 144 | } 145 | 146 | .algolia-autocomplete .ds-suggestion.ds-cursor .algolia-docsearch-suggestion--content { 147 | background-color: rgba(192, 192, 192, .15) 148 | } 149 | -------------------------------------------------------------------------------- /docs/articles/estimate_cs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Estimate conditional survival • condsurv 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 20 | 21 | 22 | 23 | 24 |
    25 |
    80 | 81 | 82 | 83 | 84 |
    85 |
    86 | 97 | 98 | 99 | 100 |
    101 | library(condsurv)
    102 | library(dplyr)
    103 | #> Warning: package 'dplyr' was built under R version 4.2.1
    104 | library(survival)
    105 | #> Warning: package 'survival' was built under R version 4.2.1
    106 |

    If \(S(t)\) represents the survival 107 | function at time \(t\), then 108 | conditional survival is defined as

    109 |

    \[S(y|x) = \frac{S(x + 110 | y)}{S(x)}\]

    111 |

    where \(y\) is the number of 112 | additional survival years of interest and \(x\) is the number of years a subject has 113 | already survived.

    114 |
    115 |

    Generating conditional survival estimates 116 |

    117 |

    The conditional_surv_est function will generate this 118 | estimate along with 95% confidence intervals.

    119 |

    The lung dataset from the survival package 120 | will be used to illustrate.

    121 |
    122 | # Scale the time variable to be in years rather than days
    123 | lung2 <- 
    124 |   mutate(
    125 |     lung,
    126 |     os_yrs = time / 365.25
    127 |   )
    128 |

    First generate a single conditional survival estimate. This is the 129 | conditional survival of surviving to 1 year conditioned on already 130 | having survived 6 months (\(0.5\) 131 | year). This returns a list, where cs_est is the conditional 132 | survival estimate, cs_lci is the lower bound of the 95% 133 | confidence interval and cs_uci is the upper bound of the 134 | 95% confidence interval.

    135 |
    136 | myfit <- survfit(Surv(os_yrs, status) ~ 1, data = lung2)
    137 | 
    138 | conditional_surv_est(
    139 |   basekm = myfit,
    140 |   t1 = 0.5, 
    141 |   t2 = 1
    142 | )
    143 | #> $cs_est
    144 | #> [1] 0.58
    145 | #> 
    146 | #> $cs_lci
    147 | #> [1] 0.49
    148 | #> 
    149 | #> $cs_uci
    150 | #> [1] 0.66
    151 |

    You can easily use purrr::map_df to get a table of 152 | estimates for multiple timepoints. For example we could get the 153 | conditional survival estimate of surviving to a variety of different 154 | time points given that the subject has already survived for 6 months 155 | (0.5 years).

    156 |
    157 | prob_times <- seq(1, 2.5, 0.5)
    158 | 
    159 | purrr::map_df(
    160 |   prob_times, 
    161 |   ~conditional_surv_est(
    162 |     basekm = myfit, 
    163 |     t1 = 0.5, 
    164 |     t2 = .x) 
    165 |   ) %>% 
    166 |   dplyr::mutate(years = prob_times) %>% 
    167 |   dplyr::select(years, everything()) %>% 
    168 |   knitr::kable()
    169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 |
    yearscs_estcs_lcics_uci
    1.00.580.490.66
    1.50.360.270.45
    2.00.160.100.25
    2.50.070.020.15
    203 |
    204 |
    205 |

    A note on confidence interval estimation 206 |

    207 |

    The confidence intervals are based on a variation of the log-log 208 | transformation, also known as the “exponential” Greenwood formula, where 209 | the conditional survival estimate is substituted in for the traditional 210 | survival estimate in constructing the confidence interval.

    211 |

    If \(\hat{S}(y|x)\) is the estimated 212 | conditional survival to \(y\) given 213 | having already survived to \(x\), 214 | then

    215 |

    \[\hat{S}(y|x)^{exp(\pm1.96\sqrt{\hat{L}(y|x)})}\]

    216 |

    where

    217 |

    \[\hat{L}(y|x)=\frac{1}{\log(\hat{S}(y|x))^2}\sum_{j:x 218 | \leq \tau_j \leq y}\frac{d_j}{(r_j-d_j)r_j}\]

    219 |

    and

    220 |

    \(\tau_j\) = distinct death time 221 | \(j\)

    222 |

    \(d_j\) = number of failures at 223 | death time \(j\)

    224 |

    \(r_j\) = number at risk at death 225 | time \(j\)

    226 |
    227 |
    228 | 229 | 234 | 235 |
    236 | 237 | 238 | 239 | 250 |
    251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | --------------------------------------------------------------------------------