Nothing
# ==============================================================================
# S4 Classes: GWASForestPlots and MRForestPlots
#
# Typed containers for the ggplot objects produced by GWAS_forest() and
# MR_forest() respectively. Each class stores plots alongside outcome and
# exposure metadata so that filtering and export work on structured fields
# rather than string-parsing of list names.
#
# Both classes share the same slot structure and export interface, but are
# export() can generate the correct filename prefixes automatically.
# ==============================================================================
# ------------------------------------------------------------------------------
# GWASForestPlots — instrument-level forest plots from GWAS_forest()
# ------------------------------------------------------------------------------
#' S4 class to store instrument-level MR forest plots
#'
#' A \code{GWASForestPlots} object is returned by \code{GWAS_forest()}.
#' It holds one \code{ggplot} object per outcome-exposure pair — each plot
#' shows per-instrument (SNP-level) causal estimates alongside a pooled IVW
#' estimate — together with the metadata needed to filter and export subsets.
#'
#' @slot plots Named list of \code{ggplot} objects. Names follow the
#' pattern \code{"Outcome::Exposure"}.
#' @slot outcomes Character vector of outcome labels, parallel to
#' \code{plots}.
#' @slot exposures Character vector of exposure labels, parallel to
#' \code{plots}.
#'
#' @section Methods:
#' \describe{
#' \item{\code{export_forest_plots(object, save_dir, file_type, width,
#' height, dpi, outcome, exposure)}}{
#' Saves plots to disk. Optionally filter by \code{outcome},
#' \code{exposure}, or both to export a subset.}
#' }
#' @importFrom methods setClass setValidity new
#' @keywords internal
#' @export
setClass("GWASForestPlots", representation(
plots = "list",
outcomes = "character",
exposures = "character"
))
setValidity("GWASForestPlots", function(object) {
n <- length(object@plots)
if (length(object@outcomes) != n)
return("'outcomes' must have the same length as 'plots'.")
if (length(object@exposures) != n)
return("'exposures' must have the same length as 'plots'.")
if (n > 0 && is.null(names(object@plots)))
return("'plots' must be a named list.")
TRUE
})
# ------------------------------------------------------------------------------
# MRForestPlots — method-level forest plots from MR_forest()
# ------------------------------------------------------------------------------
#' S4 class to store method-level MR forest plots
#'
#' An \code{MRForestPlots} object is returned by \code{MR_forest()}.
#' It holds one \code{ggplot} object per outcome-exposure pair — each plot
#' compares causal estimates across MR methods (IVW, RAPS, Egger, etc.) —
#' together with the metadata needed to filter and export subsets.
#'
#' @slot plots Named list of \code{ggplot} objects. Names follow the
#' pattern \code{"Outcome::Exposure"}.
#' @slot outcomes Character vector of outcome labels, parallel to
#' \code{plots}.
#' @slot exposures Character vector of exposure labels, parallel to
#' \code{plots}.
#'
#' @section Methods:
#' \describe{
#' \item{\code{export_forest_plots(object, save_dir, file_type, width,
#' height, dpi, outcome, exposure)}}{
#' Saves plots to disk. Optionally filter by \code{outcome},
#' \code{exposure}, or both to export a subset.}
#' }
#' @keywords internal
#' @export
setClass("MRForestPlots", representation(
plots = "list",
outcomes = "character",
exposures = "character"
))
setValidity("MRForestPlots", function(object) {
n <- length(object@plots)
if (length(object@outcomes) != n)
return("'outcomes' must have the same length as 'plots'.")
if (length(object@exposures) != n)
return("'exposures' must have the same length as 'plots'.")
if (n > 0 && is.null(names(object@plots)))
return("'plots' must be a named list.")
TRUE
})
# ------------------------------------------------------------------------------
# Internal constructors (not exported)
# ------------------------------------------------------------------------------
#' @noRd
new_gwas_forest_plots <- function(plots, outcomes, exposures) {
new("GWASForestPlots",
plots = plots,
outcomes = outcomes,
exposures = exposures)
}
#' @noRd
new_mr_forest_plots <- function(plots, outcomes, exposures) {
new("MRForestPlots",
plots = plots,
outcomes = outcomes,
exposures = exposures)
}
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# export_forest_plots generic and methods
# ------------------------------------------------------------------------------
#' Export MR Forest Plots to Disk
#'
#' Saves plots stored in a \code{GWASForestPlots} or
#' \code{MRForestPlots} object to a directory using
#' \code{ggplot2::ggsave()}. You can export all plots or filter to a specific
#' outcome, a specific exposure, or a specific outcome-exposure pair.
#'
#' @param object A \code{GWASForestPlots} or \code{MRForestPlots} object
#' returned by \code{GWAS_forest()} or \code{MR_forest()}.
#' @param save_dir Character string; directory to write files into.
#' Must already exist. Defaults to \code{tempdir()}.
#' @param file_type Character string; output format passed to
#' \code{ggplot2::ggsave()}. One of \code{"pdf"}, \code{"png"},
#' \code{"jpeg"}, or \code{"tiff"}. Default is \code{"png"}.
#' @param width Numeric; plot width in inches. Default is \code{8}.
#' @param height Numeric; plot height in inches. Default is \code{6}.
#' @param dpi Integer; resolution in dots per inch (ignored for PDF).
#' Default is \code{300}.
#' @param outcome Optional character string; if supplied, only plots
#' whose outcome matches this value are exported.
#' @param exposure Optional character string; if supplied, only plots
#' whose exposure matches this value are exported.
#'
#' @details Filtering logic:
#' \itemize{
#' \item \strong{Both \code{outcome} and \code{exposure} supplied} — exports
#' the single plot for that exact pair.
#' \item \strong{\code{outcome} only} — exports all exposures for that
#' outcome.
#' \item \strong{\code{exposure} only} — exports all outcomes for that
#' exposure.
#' \item \strong{Neither supplied} — exports every plot in the object.
#' }
#' File names are prefixed with \code{Instrument_forest_} for
#' \code{GWASForestPlots} objects and \code{Method_forest_} for
#' \code{MRForestPlots} objects, followed by
#' \code{<Outcome>_<Exposure>.<file_type>} with spaces replaced by
#' underscores.
#'
#' @return Invisibly returns \code{object} so calls can be chained.
#'
#' @examples
#' \donttest{
#' data("fi_49item")
#' input1 <- harmonize_mr_data(df = fi_49item)$input_df
#' outcome1 <- run_mr_analysis(MR_input_data = input1)
#'
#' gwas_plots <- GWAS_forest(MR_input_data = input1, report_form = "Beta")
#' mr_plots <- MR_forest(summary_df = outcome1, effect = "Beta")
#'
#' # Inspect what is stored; for fi_49item both objects print:
#' # [1] fi_49item :: Zn
#'
#' # Retrieve the exact outcome/exposure labels stored in the object
#' gwas_out <- gwas_plots@outcomes[1] # "fi_49item"
#' gwas_exp <- gwas_plots@exposures[1] # "Zn"
#' mr_out <- mr_plots@outcomes[1] # "fi_49item"
#' mr_exp <- mr_plots@exposures[1] # "Zn"
#'
#' # Export all instrument-level plots as PDF (commented — writes to disk)
#' export_forest_plots(gwas_plots, save_dir = tempdir())
#'
#' # Export one outcome only
#' export_forest_plots(gwas_plots, save_dir = tempdir(), outcome = gwas_out)
#'
#' # Export one exposure only
#' export_forest_plots(mr_plots, save_dir = tempdir(), exposure = mr_exp)
#'
#' # Export one specific pair
#' export_forest_plots(mr_plots, save_dir = tempdir(), outcome = mr_out, exposure = mr_exp)
#' }
#' @importFrom methods setGeneric setMethod
#' @importFrom ggplot2 ggsave
#' @export
setGeneric("export_forest_plots",
function(object, save_dir = tempdir(), file_type = "png",
width = 8, height = 6, dpi = 300,
outcome = NULL, exposure = NULL)
standardGeneric("export_forest_plots"))
# Shared filtering + saving logic used by both methods below
.export_forest_impl <- function(object, save_dir, file_type, width, height,
dpi, outcome, exposure, fname_prefix) {
file_type <- match.arg(file_type, c("pdf", "png", "jpeg", "tiff"))
# ---- build index of plots to export ----
keys <- names(object@plots)
outcomes <- object@outcomes
exposures <- object@exposures
idx <- seq_along(keys)
if (!is.null(outcome)) {
idx <- idx[outcomes[idx] == outcome]
}
if (!is.null(exposure)) {
idx <- idx[exposures[idx] == exposure]
}
if (length(idx) == 0) {
message("No plots matched the supplied outcome/exposure filter.")
return(invisible(object))
}
# ---- save each selected plot ----
for (i in idx) {
safe_out <- gsub("\\s+", "_", outcomes[i])
safe_exp <- gsub("\\s+", "_", exposures[i])
fname <- paste0(fname_prefix, safe_out, "_", safe_exp, ".", file_type)
filepath <- file.path(save_dir, fname)
ggplot2::ggsave(
filename = filepath,
plot = object@plots[[i]],
width = width,
height = height,
dpi = dpi
)
message("Saved: ", filepath)
}
invisible(object)
}
#' S4 methods for export_forest_plots
#'
#' Internal aliases used to document the concrete S4 methods for
#' \code{export_forest_plots}. Users should normally consult
#' \code{?export_forest_plots}.
#'
#' @name export_forest_plots-methods
#' @aliases export_forest_plots,GWASForestPlots-method
#' @aliases export_forest_plots,MRForestPlots-method
#' @keywords internal
NULL
setMethod("export_forest_plots", "GWASForestPlots",
function(object, save_dir = tempdir(), file_type = "png",
width = 8, height = 6, dpi = 300,
outcome = NULL, exposure = NULL) {
.export_forest_impl(object, save_dir, file_type, width, height,
dpi, outcome, exposure,
fname_prefix = "Instrument_forest_")
})
setMethod("export_forest_plots", "MRForestPlots",
function(object, save_dir = tempdir(), file_type = "png",
width = 8, height = 6, dpi = 300,
outcome = NULL, exposure = NULL) {
.export_forest_impl(object, save_dir, file_type, width, height,
dpi, outcome, exposure,
fname_prefix = "Method_forest_")
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.