|
174 | 174 | #' probabilities, \code{\link{ps_check}} for for graphical checks of the estimated
|
175 | 175 | #' survival function, and \code{\link{posterior_traj}} for estimating the
|
176 | 176 | #' marginal or subject-specific longitudinal trajectories, and
|
177 |
| -#' \code{\link{plot_stack}} for combining plots of the estimated subject-specific |
| 177 | +#' \code{\link{plot_stack_jm}} for combining plots of the estimated subject-specific |
178 | 178 | #' longitudinal trajectory and survival function.
|
179 | 179 | #'
|
180 | 180 | #' @references
|
@@ -540,6 +540,7 @@ posterior_survfit <- function(object, newdataLong = NULL, newdataEvent = NULL,
|
540 | 540 | offset = offset, b_new = if (!is.null(newdataEvent)) b_new else NULL)
|
541 | 541 | }
|
542 | 542 |
|
| 543 | + |
543 | 544 | #' Plot the estimated subject-specific or marginal survival function
|
544 | 545 | #'
|
545 | 546 | #' This generic \code{plot} method for \code{survfit.stanjm} objects will
|
@@ -576,11 +577,12 @@ posterior_survfit <- function(object, newdataLong = NULL, newdataEvent = NULL,
|
576 | 577 | #' \code{\link[ggplot2]{geom_line}} and used to control features
|
577 | 578 | #' of the plotted survival function.
|
578 | 579 | #'
|
579 |
| -#' @return A \code{ggplot} object, also of class \code{plot.survfit.stanjm}. |
580 |
| -#' This object can be further customised using the \pkg{ggplot2} package. |
581 |
| -#' It can also be passed to the function \code{\link{plot_stack}}. |
| 580 | +#' @return The plot method returns a \code{ggplot} object, also of class |
| 581 | +#' \code{plot.survfit.stanjm}. This object can be further customised using the |
| 582 | +#' \pkg{ggplot2} package. It can also be passed to the function |
| 583 | +#' \code{plot_stack_jm}. |
582 | 584 | #'
|
583 |
| -#' @seealso \code{\link{posterior_survfit}}, \code{\link{plot_stack}}, |
| 585 | +#' @seealso \code{\link{posterior_survfit}}, \code{\link{plot_stack_jm}}, |
584 | 586 | #' \code{\link{posterior_traj}}, \code{\link{plot.predict.stanjm}}
|
585 | 587 | #'
|
586 | 588 | #' @examples
|
@@ -619,7 +621,7 @@ posterior_survfit <- function(object, newdataLong = NULL, newdataEvent = NULL,
|
619 | 621 | #' pt1 <- posterior_traj(example_jm, , ids = c(7,13,15))
|
620 | 622 | #' plot_surv <- plot(ps1)
|
621 | 623 | #' plot_traj <- plot(pt1, vline = TRUE, plot_observed = TRUE)
|
622 |
| -#' plot_stack(plot_traj, plot_surv) |
| 624 | +#' plot_stack_jm(plot_traj, plot_surv) |
623 | 625 | #'
|
624 | 626 | #' # Lastly, let us plot the standardised survival function
|
625 | 627 | #' # based on all individuals in our estimation dataset
|
@@ -694,6 +696,124 @@ plot.survfit.stanjm <- function(x, ids = NULL,
|
694 | 696 | }
|
695 | 697 |
|
696 | 698 |
|
| 699 | +#' @rdname plot.survfit.stanjm |
| 700 | +#' @export |
| 701 | +#' @importFrom ggplot2 ggplot_build facet_wrap aes_string expand_limits |
| 702 | +#' |
| 703 | +#' @description The \code{plot_stack_jm} function takes arguments containing the plots of the estimated |
| 704 | +#' subject-specific longitudinal trajectory (or trajectories if a multivariate |
| 705 | +#' joint model was estimated) and the plot of the estimated subject-specific |
| 706 | +#' survival function and combines them into a single figure. This is most |
| 707 | +#' easily understood by running the \strong{Examples} below. |
| 708 | +#' |
| 709 | +#' @param yplot An object of class \code{plot.predict.stanjm}, returned by a |
| 710 | +#' call to the generic \code{\link[=plot.predict.stanjm]{plot}} method for |
| 711 | +#' objects of class \code{predict.stanjm}. If there is more than one |
| 712 | +#' longitudinal outcome, then a list of such objects can be provided. |
| 713 | +#' @param survplot An object of class \code{plot.survfit.stanjm}, returned by a |
| 714 | +#' call to the generic \code{\link[=plot.survfit.stanjm]{plot}} method for |
| 715 | +#' objects of class \code{survfit.stanjm}. |
| 716 | +#' |
| 717 | +#' @return \code{plot_stack_jm} returns an object of class |
| 718 | +#' \code{\link[bayesplot]{bayesplot_grid}} that includes plots of the |
| 719 | +#' estimated subject-specific longitudinal trajectories stacked on top of the |
| 720 | +#' associated subject-specific survival curve. |
| 721 | +#' |
| 722 | +#' @seealso \code{\link{plot.predict.stanjm}}, \code{\link{plot.survfit.stanjm}}, |
| 723 | +#' \code{\link{posterior_predict}}, \code{\link{posterior_survfit}} |
| 724 | +#' |
| 725 | +#' @examples |
| 726 | +#' \donttest{ |
| 727 | +#' if (!exists("example_jm")) example(example_jm) |
| 728 | +#' ps1 <- posterior_survfit(example_jm, ids = c(7,13,15)) |
| 729 | +#' pt1 <- posterior_traj(example_jm, ids = c(7,13,15), extrapolate = TRUE) |
| 730 | +#' plot_surv <- plot(ps1) |
| 731 | +#' plot_traj <- plot(pt1, vline = TRUE, plot_observed = TRUE) |
| 732 | +#' plot_stack_jm(plot_traj, plot_surv) |
| 733 | +#' } |
| 734 | +#' |
| 735 | +plot_stack_jm <- function(yplot, survplot) { |
| 736 | + |
| 737 | + if (!is(yplot, "list")) yplot <- list(yplot) |
| 738 | + |
| 739 | + lapply(yplot, function(x) { |
| 740 | + if (!is(x, "plot.predict.stanjm")) |
| 741 | + stop("'yplot' should be an object of class 'plot.predict.stanjm', ", |
| 742 | + "or a list of such objects.", call. = FALSE) |
| 743 | + }) |
| 744 | + if (!is(survplot, "plot.survfit.stanjm")) |
| 745 | + stop("'survplot' should be an object of class 'plot.survfit.stanjm'.", |
| 746 | + call. = FALSE) |
| 747 | + |
| 748 | + y_build <- lapply(yplot, ggplot_build) |
| 749 | + y_layout <- lapply(y_build, function(x) x$layout$panel_layout) |
| 750 | + y_ids <- lapply(y_layout, function(x) |
| 751 | + if (!"id" %in% colnames(x)) NULL else x[["id"]]) |
| 752 | + |
| 753 | + e_build <- ggplot_build(survplot) |
| 754 | + e_layout <- e_build$layout$panel_layout |
| 755 | + e_ids <- if (!"id" %in% colnames(e_layout)) NULL else e_layout[["id"]] |
| 756 | + |
| 757 | + lapply(y_ids, function(x, e_ids) { |
| 758 | + if (!all(sort(x) == sort(e_ids))) |
| 759 | + stop("The individuals in the 'yplot' and 'survplot' appear to differ. Please ", |
| 760 | + "reestimate the plots using a common 'ids' argument.", call. = FALSE) |
| 761 | + }, e_ids = e_ids) |
| 762 | + |
| 763 | + vline <- lapply(seq_along(y_build), function(m) { |
| 764 | + L <- length(y_build[[m]]$data) |
| 765 | + dat <- y_build[[m]]$data[[L]] |
| 766 | + if (!"xintercept" %in% colnames(dat)) { |
| 767 | + found <- FALSE |
| 768 | + } else { |
| 769 | + found <- TRUE |
| 770 | + dat <- dat[, c("PANEL", "xintercept"), drop = FALSE] |
| 771 | + if (NROW(y_layout[[m]]) > 1) { |
| 772 | + panel_id_map <- y_layout[[m]][, c("PANEL", "id"), drop = FALSE] |
| 773 | + dat <- merge(dat, panel_id_map, by = "PANEL") |
| 774 | + } |
| 775 | + dat <- dat[, grep("PANEL", colnames(dat), invert = TRUE), drop = FALSE] |
| 776 | + colnames(dat) <- gsub("xintercept", paste0("xintercept", m), colnames(dat), fixed = TRUE) |
| 777 | + } |
| 778 | + list(dat = dat, found = found) |
| 779 | + }) |
| 780 | + vline_found <- any(sapply(vline, function(x) x$found)) |
| 781 | + if (!vline_found) |
| 782 | + cat("Could not find vertical line indicating last observation time in the", |
| 783 | + "plot of the longitudinal trajectory; you may wish to plot the longitudinal", |
| 784 | + "trajectories again with 'vline = TRUE' to aid interpretation.") |
| 785 | + vline_dat <- lapply(vline, function(x) x$dat) |
| 786 | + vline_alldat <- Reduce(function(...) merge(..., all = TRUE), vline_dat) |
| 787 | + vline_alldat$xintercept_max <- |
| 788 | + apply(vline_alldat[, grep("id", colnames(vline_alldat), invert = TRUE), drop = FALSE], 1, max) |
| 789 | + |
| 790 | + xmax <- max(sapply(c(y_build, list(e_build)), function(i) max(i$data[[1]]$x))) |
| 791 | + |
| 792 | + if ((!is.null(e_ids)) && (length(e_ids) > 20L)) { |
| 793 | + stop("Unable to generate 'plot_stack_jm' for this many individuals.", call. = FALSE) |
| 794 | + } else if ((!is.null(e_ids)) && (length(e_ids) > 3L)) { |
| 795 | + warning("'plot_stack_jm' is unlikely to be legible with more than a few individuals.", |
| 796 | + immediate. = TRUE, call. = FALSE) |
| 797 | + } |
| 798 | + |
| 799 | + graph_facet <- if (!is.null(e_ids)) |
| 800 | + facet_wrap(~ id, scales = "free", nrow = 1) else NULL |
| 801 | + survplot_updated <- survplot + expand_limits(x = c(0, xmax)) + graph_facet + if (vline_found) |
| 802 | + geom_vline(aes_string(xintercept = "xintercept_max"), vline_alldat, linetype = 2) |
| 803 | + |
| 804 | + if (!is.null(e_ids)) |
| 805 | + yplot <- lapply(yplot, function(x) x + expand_limits(x = c(0, xmax)) + |
| 806 | + facet_wrap(~ id, scales = "free", nrow = 1)) |
| 807 | + |
| 808 | + bayesplot::bayesplot_grid( |
| 809 | + plots = c(yplot, list(survplot_updated)), |
| 810 | + grid_args = list(ncol = 1) |
| 811 | + ) |
| 812 | +} |
| 813 | + |
| 814 | + |
| 815 | + |
| 816 | + |
697 | 817 | # ------------------ exported but doc kept internal
|
698 | 818 |
|
699 | 819 | #' Generic print method for \code{survfit.stanjm} objects
|
|
0 commit comments