#' Plot for direct evidence proportions in a network meta-analysis using \code{netmeta} #' #' This function plots relevant measures quantifying the direct evidence proportion, mean path length #' and aggregated minimal parallelism of a frequentist network meta-analysis model generated by #' \code{\link[netmeta]{netmeta}}. #' #' @usage direct.evidence.plot(x, random=FALSE, comparison.label.size=2, #' numeric.label.size=3, subplot.ratio=c(5, 1.3, 1.3)) #' #' @param x An object of class \code{netmeta} containing the results of a network meta-analysis #' using the \code{\link[netmeta]{netmeta}} function. #' @param random Logical. If set to \code{TRUE}, results for the random-effects model are displayed. #' If set to \code{FALSE}, results for the fixed-effect model are displayed. \code{FALSE} by default. #' @param comparison.label.size A numeric value for the size of comparison labels #' to be used in the plot. Default is \code{2}. #' @param numeric.label.size A numeric value for the label size of numeric values #' to be used in the plot. Default is \code{3}. #' @param subplot.ratio A numeric vector containing three numbers. Defines the width for each of #' the three subplots included in the plot (from left to right). Default is \code{c(5,1.3,1.3)}. #' #' @details #' The function generates a plot containing three subplots displaying relevant characteristics #' to evaluate the reliability of effect size estimates within a network meta-analysis model. #' \itemize{ #' \item \strong{Direct Evidence Proportion}. This bar chart displays the proportion of direct #' evidence (orange) contained in each network estimate. It is of note that both direct and indirect #' evidence may contribute to the violation of the assumption of consistency underlying network #' meta-analysis models. Nevertheless, this plot allows to distinguish comparison estimates #' for which direct evidence was used, and to what extent, and comparisons which had to be inferred #' by indirect evidence alone. #' \item \strong{Minimal Parallelism}. This bar chart displays the minimum number of independent paths #' contributing to the effect estimate on an aggregated level. Large values of parallelism can be #' interpreted as supporting the robustness of the estimate. #' \item \strong{Mean Path Length}. This bar chart displays the mean path length, which characterizes #' the degree of indirectness of an estimate. Higher mean path lengths indicate less reliable #' estimates, given that more similarity assumptions have to be made when serially combining #' direct comparisons. Following König, Krahn and Binder #' (\href{https://www.ncbi.nlm.nih.gov/pubmed/24123165}{2013}), comparisons with mean path lengths #' greater than two should be interpreted with caution. This threshold is displayed as a blue vertical #' line in the plot. #' } #' #' #' @references Harrer, M., Cuijpers, P., Furukawa, T.A, & Ebert, D. D. (2019). #' \emph{Doing Meta-Analysis in R: A Hands-on Guide}. DOI: 10.5281/zenodo.2551803. \href{https://bookdown.org/MathiasHarrer/Doing_Meta_Analysis_in_R/frequentist-network-meta-analysis.html}{Chapter 11.1} #' #' König J., Krahn U., Binder H. (2013): Visualizing the flow of evidence in network meta-analysis and #' characterizing mixed treatment comparisons. \emph{Statistics in Medicine, 32}, 5414–29 #' #' @author Mathias Harrer & David Daniel Ebert #' #' @import ggplot2 netmeta #' @importFrom gridExtra grid.arrange arrangeGrob #' @importFrom scales percent #' @importFrom graphics abline axis lines mtext par plot points rect segments text #' @importFrom stats as.formula hat influence ks.test optimize pbinom pchisq pf pnorm pt punif qchisq qf qnorm qt reformulate reorder setNames uniroot #' #' @return #' \itemize{ #' \item \code{data}: A \code{data.frame} containing columns for the proportion of direct and indirect #' evidence of each comparison (\code{proportion.direct} and \code{proportion.indirect}), the #' mean path length (\code{meanpath}) and the minimal parallelism (\code{minpar}) #' for each comparison. #' \item \code{plot}: The generated plot (if the function output was saved to an object). #' } #' #' @export direct.evidence.plot #' #' @seealso #' \code{\link[netmeta]{netmeta}}, \code{\link[netmeta]{netmeasures}} #' #' @examples #' \dontrun{ #' # Load Senn2013 data from netmeta #' suppressPackageStartupMessages(library(netmeta)) #' data(Senn2013) #' #' # Conduct network meta-analysis (fixed-effects model) #' nma = netmeta(TE, seTE, treat1, treat2, studlab, #' data=Senn2013, sm='MD', comb.random=FALSE) #' #' # Generate the plot #' dep = direct.evidence.plot(nma, random=FALSE, comparison.label.size = 1, #' numeric.label.size=1, subplot.ratio=c(3,1,1)) #' dep} direct.evidence.plot = function(x, random = FALSE, comparison.label.size = 2, numeric.label.size = 3, subplot.ratio = c(5,1.3, 1.3)) { # Validate x = x random = random cts = comparison.label.size nts = numeric.label.size spr = subplot.ratio if (class(x) != "netmeta") { stop("Input to this function has to be an object of class 'netmeta' created by the 'netmeta::netmeta' function.") } # PLOT 1: Direct and Indirect Evidence #### # Get Measures measures = netmeasures(x, random = random)$proportion indirect = 1 - measures measures = data.frame(comparison = names(measures), direct = measures, indirect = indirect) rownames(measures) = c() measures$direct = round(measures$direct, 4) measures$indirect = round(measures$indirect, 4) measures.reshape = with(measures, { data.frame(comparison = rep(comparison, 2), variable = rep(c("direct", "indirect"), each = nrow(measures)), value = c(direct, indirect)) }) names = measures.reshape[measures.reshape$variable == "direct", ]$comparison direct = measures.reshape[measures.reshape$variable == "direct", ]$value names = names[order(match(names, direct))] # Reorder Label measures$comparison = factor(measures$comparison, levels = measures$comparison[rev(order(measures$direct))]) levels = levels(measures$comparison) measures.reshape$comparison = factor(measures.reshape$comparison, levels = levels) # Plot PlotDirectEvidence = ggplot2::ggplot(measures.reshape, aes(x = factor(comparison, levels = rev(levels(comparison))), fill = factor(variable, levels = c("indirect", "direct")), y = value)) + geom_bar(stat = "identity", position = "fill") + coord_flip() + theme_minimal() + theme(legend.position = "left") + scale_y_continuous(labels = scales::percent) + ylab("Percentage") + xlab("Network Estimate") + guides(fill = guide_legend(title = "Evidence")) + scale_fill_manual(values = c("lightblue", "orange")) + geom_hline(aes(yintercept = 0.25), color = "white") + geom_hline(aes(yintercept = 0.5), color = "white") + geom_hline(aes(yintercept = 0.75), color = "white") # PLOT 2: Mean Path Length #### # Get Measures mpath = netmeasures(x, random = random)$meanpath path.df = data.frame(comparison = names(mpath), mpath = mpath) rownames(path.df) = c() path.df$comparison = factor(path.df$comparison, levels = levels) # Plot for summary plot PlotMeanPathLength_s = ggplot2::ggplot(path.df, aes(x = factor(comparison, levels = rev(levels(comparison))), y = mpath)) + geom_bar(stat = "identity", fill = "lightgray") + coord_flip() + geom_hline(aes(yintercept = 2), color = "blue") + geom_text(aes(x = comparison, y = 0.4, label = comparison), color = "gray23", size = cts) + geom_text(aes(x = comparison, y = mpath + 0.1, label = round(mpath, 1)), size = nts) + ylab("Mean Path Length") + theme(axis.title.y = element_blank(), axis.text.y = element_blank(), axis.ticks.y = element_blank(), axis.ticks.x = element_blank(), panel.background = element_blank()) + scale_x_discrete(position = "top") # PLOT 3: Parallelism #### # Get Measures mpar = netmeasures(x, random = random)$minpar mpar.df = data.frame(comparison = names(mpar), mpar = mpar) rownames(mpar.df) = c() mpar.df$comparison = factor(mpar.df$comparison, levels = levels) # Plot for summary plot PlotMinimalParallelism_s = ggplot2::ggplot(mpar.df, aes(x = factor(comparison, levels = rev(levels(comparison))), y = mpar)) + geom_bar(stat = "identity", fill = "lightgray") + coord_flip() + geom_text(aes(x = comparison, y = mpar + 0.1, label = round(mpar, 1)), size = nts) + geom_text(aes(x = comparison, y = 0.4, label = comparison), color = "gray23", size = cts) + ylab("Minimal Parallelism") + theme(axis.ticks.y = element_blank(), axis.ticks.x = element_blank(), axis.title.y = element_blank(), axis.text.y = element_blank(), panel.background = element_blank()) # Process for return #### # Save data used for plotting in df data = data.frame(proportion.direct = measures$direct, proportion.indirect = measures$indirect, meanpath = mpath, minpar = mpar) # Set title if (random == FALSE) { plot_title = "Direct evidence proportion for each network estimate (fixed-effect model)" } else { plot_title = "Direct evidence proportion for each network estimate (random-effects model)" } grid = gridExtra::arrangeGrob(PlotDirectEvidence, PlotMinimalParallelism_s, PlotMeanPathLength_s, ncol = 3, widths = spr, heights = c(4), top = plot_title) returnlist = list(data = data, plot = grid) class(returnlist) = "direct.evidence.plot" invisible(returnlist) returnlist }