From 10241adac7493d07e405516b224203fdf007a931 Mon Sep 17 00:00:00 2001 From: Christian Roever <christian.roever@med.uni-goettingen.de> Date: Thu, 13 Oct 2016 08:29:34 +0200 Subject: [PATCH] fixed log-axis bug and added "prediction" argument for forestplot() function --- DESCRIPTION | 2 +- R/bayesmeta.R | 125 ++++++++++++++++-------------------- man/bayesmeta-package.Rd | 2 +- man/forestplot.bayesmeta.Rd | 32 +++++---- 4 files changed, 77 insertions(+), 84 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 5933ba1..ad13260 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,7 +2,7 @@ Package: bayesmeta Type: Package Title: Bayesian Random-Effects Meta-Analysis Version: 1.3 -Date: 2016-10-08 +Date: 2016-10-13 Authors@R: c(person("Christian", "Roever", role=c("aut","cre"), email="christian.roever@med.uni-goettingen.de"), person("Tim", "Friede", role="ctb", diff --git a/R/bayesmeta.R b/R/bayesmeta.R index 0bcc5e9..aa75aa5 100644 --- a/R/bayesmeta.R +++ b/R/bayesmeta.R @@ -1534,22 +1534,23 @@ forest.bayesmeta <- function(x, xlab="effect size", refline=0, cex=1,...) forestplot.bayesmeta <- function(x, labeltext, exponentiate=FALSE, + prediction=TRUE, shrinkage=TRUE, digits=2, plot=TRUE, fn.ci_norm, col, legend, boxsize = 0.25, ...) -# forest plot for a "bayesmeta" object -# based on the "forestplot" package's plotting function # # ARGUMENTS: -# exponentiate : flag indicating whether to exponentiate table numbers. +# x : a "bayesmeta" object. +# labeltext : you may provide an alternative "labeltext" argument here +# (see also the "forestplot()" help). +# exponentiate : flag indicating whether to exponentiate numbers (figure and table). +# prediction : flag indicating whether to show prediction interval. # shrinkage : flag indicating whether to show shrinkage estimates. # digits : number of significant digits to be shown (based on standard deviations). -# labeltext : you may (try to) provide an alternative "labeltext" argument here -# (see also the "forestplot" help). # plot : flag you can use to suppress actual plotting. -# ... : further arguments passes to the "forestplot" function. +# ... : further arguments passed to the "forestplot" function. # # VALUE: # a list with components @@ -1563,25 +1564,31 @@ forestplot.bayesmeta <- function(x, labeltext, stop("required 'forestplot' package not available!") #stopifnot(require("forestplot")) stopifnot(is.element("bayesmeta", class(x)), - length(digits)==1, digits==round(digits), digits>=0) + length(digits)==1, digits==round(digits), digits>=0, + length(prediction)==1, is.logical(prediction), + length(shrinkage)==1, is.logical(shrinkage)) # the plotting data-- # the quoted estimates: q95 <- qnorm(0.975) ma.dat <- rbind(NA, - cbind("mean"=exp(x$y), - "lower"=exp(x$y - q95*x$sigma), - "upper"=exp(x$y + q95*x$sigma)), - exp(x$summary[c("median", "95% lower", "95% upper"),"mu"]), - exp(x$summary[c("median", "95% lower", "95% upper"),"theta"])) + cbind(x$y, x$y - q95*x$sigma, x$y + q95*x$sigma), + x$summary[c("median", "95% lower", "95% upper"),"mu"], + x$summary[c("median", "95% lower", "95% upper"),"theta"]) colnames(ma.dat) <- c("estimate", "lower", "upper") rownames(ma.dat) <- c("", x$label, "mean", "prediction") + if (! prediction) ma.dat <- ma.dat[-(x$k+3),] # the shrinkage estimates: ma.shrink <- rbind(NA, - exp(t(x$theta)[,c("median","95% lower","95% upper")]), + t(x$theta)[,c("median","95% lower","95% upper")], NA,NA) colnames(ma.shrink) <- c("estimate", "lower", "upper") rownames(ma.shrink) <- c("", x$label, "mean", "prediction") - # generate data table (if not provided): + if (! prediction) ma.shrink <- ma.shrink[-(x$k+3),] + if (exponentiate) { + ma.dat <- exp(ma.dat) + ma.shrink <- exp(ma.shrink) + } + # generate data table (unless provided): if (missing(labeltext)) { decplaces <- function(x, signifdigits=3) # number of decimal places (after decimal point) @@ -1589,89 +1596,65 @@ forestplot.bayesmeta <- function(x, labeltext, { return(max(c(0, -(floor(log10(x))-(signifdigits-1))))) } + # determine numbers of digits based on standard deviations: if (exponentiate) { - # determine numbers of digits based on standard deviations: stdevs <- c(exp(x$y)*x$sigma, - exp(x$summary["median","mu"])*x$summary["sd","mu"], - exp(x$summary["median","theta"])*x$summary["sd","theta"]) + exp(x$summary["median","mu"])*x$summary["sd","mu"]) + if (prediction) stdevs <- c(stdevs, exp(x$summary["median","theta"])*x$summary["sd","theta"]) } else { - stdevs <- c(x$sigma, x$summary["sd","mu"], x$summary["sd","theta"]) + stdevs <- c(x$sigma, x$summary["sd","mu"]) + if (prediction) stdevs <- c(stdevs, x$summary["sd","theta"]) } stdevs <- abs(stdevs[is.finite(stdevs) & (stdevs != 0)]) formatstring <- paste0("%.", decplaces(stdevs, digits), "f") - if (exponentiate) { - labeltext <- cbind(x$labels, - sprintf(formatstring, exp(x$y)), - paste0("[", sprintf(formatstring, exp(x$y-q95*x$sigma)), - ", ", sprintf(formatstring, exp(x$y+q95*x$sigma)), "]")) - # add summary rows: - labeltext <- rbind(labeltext, - c("mean", - sprintf(formatstring, exp(x$summary["median","mu"])), - paste0("[",sprintf(formatstring, exp(x$summary["95% lower","mu"])), - ", ", sprintf(formatstring, exp(x$summary["95% upper","mu"])), "]")), - c("prediction", - sprintf(formatstring, exp(x$summary["median","theta"])), - paste0("[",sprintf(formatstring, exp(x$summary["95% lower","theta"])), - ", ", sprintf(formatstring, exp(x$summary["95% upper","theta"])), "]"))) - - } else { - labeltext <- cbind(x$labels, - sprintf(formatstring, x$y), - paste0("[", sprintf(formatstring, x$y-q95*x$sigma), - ", ", sprintf(formatstring, x$y+q95*x$sigma), "]")) - # add summary rows: - labeltext <- rbind(labeltext, - c("mean", - sprintf(formatstring, x$summary["median","mu"]), - paste0("[",sprintf(formatstring, x$summary["95% lower","mu"]), - ", ", sprintf(formatstring, x$summary["95% upper","mu"]), "]")), - c("prediction", - sprintf(formatstring, x$summary["median","theta"]), - paste0("[",sprintf(formatstring, x$summary["95% lower","theta"]), - ", ", sprintf(formatstring, x$summary["95% upper","theta"]), "]"))) + # fill data table: + labeltext <- matrix(NA_character_, nrow=nrow(ma.dat), ncol=3) + labeltext[1,] <- c("study","estimate", "95% CI") + labeltext[,1] <- c("study", x$labels, "mean", "prediction")[1:nrow(ma.dat)] + for (i in 2:(nrow(ma.dat))) { + labeltext[i,2] <- sprintf(formatstring, ma.dat[i,"estimate"]) + labeltext[i,3] <- paste0("[", sprintf(formatstring, ma.dat[i,"lower"]), + ", ", sprintf(formatstring, ma.dat[i,"upper"]), "]") } - # add header row: - labeltext <- rbind(c("study","estimate", "95% CI"), labeltext) } horizl <- list(grid::gpar(col="grey"), grid::gpar(col="grey")) names(horizl) <- as.character(c(2,x$k+2)) if (shrinkage) { + # default settings for estimate and shrinkage estimate plotting style: if (missing(legend)) legend <- c("quoted estimate", "shrinkage estimate") if (missing(col)) - col <- forestplot::fpColors(box=c("black", "grey45"), - lines=c("black","grey45"), - summary="grey30") + col <- fpColors(box=c("black", "grey45"), + lines=c("black","grey45"), + summary="grey30") if (missing(fn.ci_norm)) - fn.ci_norm <- c(function(...) {forestplot::fpDrawPointCI(pch=15,...)}, - function(...) {forestplot::fpDrawPointCI(pch=18,...)}) + fn.ci_norm <- c(function(...) {fpDrawPointCI(pch=15,...)}, + function(...) {fpDrawPointCI(pch=18,...)}) mean.arg <- cbind(ma.dat[,1], ma.shrink[,1]) lower.arg <- cbind(ma.dat[,2], ma.shrink[,2]) upper.arg <- cbind(ma.dat[,3], ma.shrink[,3]) } else { + # default settings for estimate plotting style: if (missing(col)) - col <- forestplot::fpColors(box="black", lines="black", summary="grey30") + col <- fpColors(box="black", lines="black", summary="grey30") if (missing(fn.ci_norm)) - fn.ci_norm <- function(...) {forestplot::fpDrawPointCI(pch=15,...)} + fn.ci_norm <- function(...) {fpDrawPointCI(pch=15,...)} mean.arg <- ma.dat[,1] lower.arg <- ma.dat[,2] upper.arg <- ma.dat[,3] } fp <- NULL - if (plot) { - fp <- forestplot::forestplot(labeltext = labeltext, - mean = mean.arg, - lower = lower.arg, - upper = upper.arg, - is.summary = c(TRUE,rep(FALSE,x$k),TRUE,TRUE), - hrzl_lines = horizl, - fn.ci_norm = fn.ci_norm, - #fn.ci_sum = fpDrawBarCI, - col = col, - boxsize = boxsize, - legend = legend, ...) - } + if (plot) fp <- forestplot::forestplot(labeltext = labeltext, + mean = mean.arg, + lower = lower.arg, + upper = upper.arg, + is.summary = c(TRUE,rep(FALSE,x$k),TRUE,TRUE), + hrzl_lines = horizl, + fn.ci_norm = fn.ci_norm, + #fn.ci_sum = fpDrawBarCI, + col = col, + boxsize = boxsize, + legend = legend, ...) invisible(list("data" = ma.dat[-1,], "shrinkage" = ma.shrink[2:(x$k+1),], "labeltext" = labeltext, diff --git a/man/bayesmeta-package.Rd b/man/bayesmeta-package.Rd index 4beaab3..a990e3d 100644 --- a/man/bayesmeta-package.Rd +++ b/man/bayesmeta-package.Rd @@ -16,7 +16,7 @@ Package: \tab bayesmeta\cr Type: \tab Package\cr Version: \tab 1.3\cr - Date: \tab 2016-10-08\cr + Date: \tab 2016-10-13\cr License: \tab GPL (>=2) } The main functionality is provided by the \code{\link{bayesmeta}()} diff --git a/man/forestplot.bayesmeta.Rd b/man/forestplot.bayesmeta.Rd index 2f01f13..39556ab 100644 --- a/man/forestplot.bayesmeta.Rd +++ b/man/forestplot.bayesmeta.Rd @@ -11,7 +11,7 @@ } \usage{ \method{forestplot}{bayesmeta}(x, labeltext, exponentiate=FALSE, - shrinkage=TRUE, digits=2, plot=TRUE, + prediction=TRUE, shrinkage=TRUE, digits=2, plot=TRUE, fn.ci_norm, col, legend, boxsize=0.25, ...) } \arguments{ @@ -26,6 +26,10 @@ a logical flag indicating whether to exponentiate numbers (effect sizes) in the table. } + \item{prediction}{ + a logical flag indicating whether to show the prediction interval + below the mean estimate estimate. + } \item{shrinkage}{ a logical flag indicating whether to show shrinkage intervals along with the quoted estimates. @@ -48,7 +52,8 @@ resulting estimates (effect estimate and prediction interval, as well as shrinkage estimates and intervals). } -\note{This function requires the \pkg{forestplot} package to be installed. +\note{This function is based on the \pkg{forestplot} package's + \dQuote{\code{\link[forestplot]{forestplot}()}} function. } \author{ Christian Roever \email{christian.roever@med.uni-goettingen.de} @@ -65,14 +70,16 @@ \seealso{ \code{\link{bayesmeta}}, \code{\link[forestplot]{forestplot}}, - \code{\link{forest.bayesmeta}}. + \code{\link{forest.bayesmeta}}, + \code{\link{plot.bayesmeta}}. } \examples{ \dontrun{ # load data: data("CrinsEtAl2014") -# compute effect sizes (log-ORs): +# compute effect sizes (log-ORs) +# (requires "metafor" package to be installed): require("metafor") es.crins <- escalc(measure="OR", ai=exp.AR.events, n1i=exp.total, @@ -89,21 +96,24 @@ require("forestplot") # default options: forestplot.bayesmeta(crins.ma) -# exponentiate values shown in table, show vertical line at OR=1: -forestplot.bayesmeta(crins.ma, zero=1, expo=TRUE) +# exponentiate values (shown in table and plot), show vertical line at OR=1: +forestplot.bayesmeta(crins.ma, expo=TRUE, zero=1) # logarithmic x-axis: -forestplot.bayesmeta(crins.ma, xlog=TRUE) +forestplot.bayesmeta(crins.ma, expo=TRUE, xlog=TRUE) + +# omit prediction interval: +forestplot.bayesmeta(crins.ma, predict=FALSE) # omit shrinkage intervals: -forestplot.bayesmeta(crins.ma, zero=1, shrink=FALSE) +forestplot.bayesmeta(crins.ma, shrink=FALSE) # show more decimal places: -forestplot.bayesmeta(crins.ma, zero=1, digits=3) +forestplot.bayesmeta(crins.ma, digits=3) # change table values: # (here: add columns for event counts) -fp <- forestplot.bayesmeta(crins.ma, plot=FALSE, expo=TRUE) +fp <- forestplot.bayesmeta(crins.ma, expo=TRUE, plot=FALSE) labtext <- fp$labeltext labtext <- cbind(labtext[,1], c("treatment", @@ -116,7 +126,7 @@ labtext <- cbind(labtext[,1], labtext[1,4] <- "OR" print(fp$labeltext) # before print(labtext) # after -forestplot.bayesmeta(crins.ma, labeltext=labtext, xlog=TRUE) +forestplot.bayesmeta(crins.ma, labeltext=labtext, expo=TRUE, xlog=TRUE) # (see also the "forestplot" help for more arguments that you may change) -- GitLab