diff --git a/NEWS.md b/NEWS.md index 32462021..c2fc49f8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,7 @@ ## MOSuite development version - Harmonize Volcano Plot output dimensions as physical inches with 300 DPI defaults for Enhanced and Summary plots. (#274, @TJoshMeyer) +- Build Venn and Intersection plots directly from per-contrast differential expression results, with configurable strict significance and fold-change thresholds and optional contrast selection. (#278, @TJoshMeyer) ## MOSuite 0.4.2 diff --git a/R/plot_venn_diagram.R b/R/plot_venn_diagram.R index 3a176fe2..3bf5c1e0 100644 --- a/R/plot_venn_diagram.R +++ b/R/plot_venn_diagram.R @@ -4,9 +4,18 @@ #' tested contrasts). This Venn diagram is available for up to five sets; Intersection plot is available for any number #' of sets. Specific sets can be selected for the visualizations and the returned dataset may include all (default) or #' specified intersections. -#' An S7 generic with methods for `multiOmicDataSet` and `data.frame`. +#' An S7 generic for `multiOmicDataSet` inputs. #' -#' @param moo_diff_summary_dat multiOmicDataSet or summarized differential expression analysis data frame. +#' @param moo_diff_summary_dat multiOmicDataSet containing per-contrast differential expression results. +#' @param feature_id_colname Feature identifier column in each contrast result. Defaults to the first column. +#' @param signif_colname Significance column in each contrast result. Default: `"adjpval"`. +#' @param signif_threshold Features pass the significance threshold when their value is less than this cutoff +#' (exclusive). Default: `0.05`. +#' @param change_colname Log fold-change column in each contrast result. Default: `"logFC"`. +#' @param change_threshold Features pass the fold-change threshold when their absolute value is greater than this +#' cutoff (exclusive). Default: `1.0`. +#' @param select_contrasts Contrast names to include. If empty, all available contrasts are used. +#' @inheritParams plot_venn_sets #' #' @export plot_venn_diagram <- S7::new_generic( @@ -15,7 +24,10 @@ plot_venn_diagram <- S7::new_generic( function( moo_diff_summary_dat, feature_id_colname = NULL, - contrasts_colname = "Contrast", + signif_colname = "adjpval", + signif_threshold = 0.05, + change_colname = "logFC", + change_threshold = 1.0, select_contrasts = c(), plot_type = "Venn diagram", intersection_ids = c(), @@ -60,7 +72,10 @@ plot_venn_diagram <- S7::new_generic( S7::method(plot_venn_diagram, multiOmicDataSet) <- function( moo_diff_summary_dat, feature_id_colname = NULL, - contrasts_colname = "Contrast", + signif_colname = "adjpval", + signif_threshold = 0.05, + change_colname = "logFC", + change_threshold = 1.0, select_contrasts = c(), plot_type = "Venn diagram", intersection_ids = c(), @@ -97,55 +112,80 @@ S7::method(plot_venn_diagram, multiOmicDataSet) <- function( save_plots = options::opt("save_plots"), plots_subdir = "diff" ) { + diff_results <- moo_diff_summary_dat@analyses$diff + if (!is.list(diff_results) || length(diff_results) == 0) { + stop( + "No per-contrast differential expression results found in moo@analyses$diff" + ) + } + if (length(select_contrasts) > 0) { + missing_contrasts <- setdiff(select_contrasts, names(diff_results)) + if (length(missing_contrasts) > 0) { + stop(glue::glue( + "Selected contrasts not found: {paste(missing_contrasts, collapse = ', ')}" + )) + } + diff_results <- diff_results[select_contrasts] + } + if (is.null(feature_id_colname)) { + feature_id_colname <- names(diff_results[[1]])[1] + } + + venn_sets <- lapply(diff_results, function(diff_result) { + required_columns <- c(feature_id_colname, signif_colname, change_colname) + missing_columns <- setdiff(required_columns, names(diff_result)) + if (length(missing_columns) > 0) { + stop(glue::glue( + "Required DEG columns not found: {paste(missing_columns, collapse = ', ')}" + )) + } + is_significant <- !is.na(diff_result[[signif_colname]]) & + diff_result[[signif_colname]] < signif_threshold + has_change <- !is.na(diff_result[[change_colname]]) & + abs(diff_result[[change_colname]]) > change_threshold + return(unique(diff_result[[feature_id_colname]][ + is_significant & has_change + ])) + }) + return( - moo_diff_summary_dat@analyses$diff |> - join_dfs_wide() |> - plot_volcano_summary(print_plots = FALSE, save_plots = FALSE) |> - plot_venn_diagram( - feature_id_colname, - contrasts_colname, - select_contrasts, - plot_type, - intersection_ids, - venn_force_unique, - venn_numbers_format, - venn_significant_digits, - venn_fill_colors, - venn_fill_transparency, - venn_border_colors, - venn_font_size_for_category_names, - venn_category_names_distance, - venn_category_names_position, - venn_font_size_for_counts, - venn_outer_margin, - intersections_order, - display_empty_intersections, - intersection_bar_color, - intersection_point_size, - intersection_line_width, - table_font_size, - table_content, - graphics_device, - dpi, - image_width, - image_height, - plot_filename, - print_plots, - save_plots, - plots_subdir, - ) + plot_venn_sets( + venn_sets = venn_sets, + plot_type = plot_type, + intersection_ids = intersection_ids, + venn_force_unique = venn_force_unique, + venn_numbers_format = venn_numbers_format, + venn_significant_digits = venn_significant_digits, + venn_fill_colors = venn_fill_colors, + venn_fill_transparency = venn_fill_transparency, + venn_border_colors = venn_border_colors, + venn_font_size_for_category_names = venn_font_size_for_category_names, + venn_category_names_distance = venn_category_names_distance, + venn_category_names_position = venn_category_names_position, + venn_font_size_for_counts = venn_font_size_for_counts, + venn_outer_margin = venn_outer_margin, + intersections_order = intersections_order, + display_empty_intersections = display_empty_intersections, + intersection_bar_color = intersection_bar_color, + intersection_point_size = intersection_point_size, + intersection_line_width = intersection_line_width, + table_font_size = table_font_size, + table_content = table_content, + graphics_device = graphics_device, + dpi = dpi, + image_width = image_width, + image_height = image_height, + plot_filename = plot_filename, + print_plots = print_plots, + save_plots = save_plots, + plots_subdir = plots_subdir + ) ) } -#' @inheritParams option_params -#' @inheritParams filter_counts -#' @inheritParams plot_volcano_enhanced -#' @inheritParams plot_volcano_summary +#' Build Venn or UpSet intersections from feature sets #' -#' @param moo_diff_summary_dat Summarized differential expression analysis -#' @param contrasts_colname Name of the column in `moo_diff_summary_dat` that contains the contrast names (default: -#' "Contrast") -#' @param select_contrasts A vector of contrast names to select for the plot. If empty, all contrasts are used. +#' @param venn_sets Named list of feature identifier vectors, one per contrast. #' @param plot_type Type of plot to generate: "Venn diagram" or "Intersection plot". Default: "Venn diagram" #' @param intersection_ids A vector of intersection IDs to select for the plot. If empty, all intersections are used. #' @param venn_force_unique If TRUE, forces unique elements in the Venn diagram. Default: TRUE @@ -169,18 +209,19 @@ S7::method(plot_venn_diagram, multiOmicDataSet) <- function( #' @param intersection_line_width Width of the lines in the intersection plot. Default: 0.5 #' @param table_font_size Font size for the table in the plot. Default: 3 #' @param table_content Content of the table in the plot. Default: NULL +#' @param graphics_device Graphics device used to save the plot. Default: `grDevices::png` +#' @param dpi Dots per inch of the saved plot. Default: 300 +#' @param image_width Saved plot width in pixels. Default: 4000 +#' @param image_height Saved plot height in pixels. Default: 3000 +#' @param plot_filename Filename for the saved plot. Default: `"venn_diagram.png"` +#' @param print_plots Whether to print plots. Default: `options::opt("print_plots")` +#' @param save_plots Whether to save plots. Default: `options::opt("save_plots")` +#' @param plots_subdir Subdirectory for saved plots. Default: `"diff"` #' -#' @keywords plotters -#' -#' @examples -#' plot_venn_diagram(nidap_volcano_summary_dat, print_plots = TRUE) +#' @keywords internal #' -#' @rdname plot_venn_diagram -S7::method(plot_venn_diagram, S7::class_data.frame) <- function( - moo_diff_summary_dat, - feature_id_colname = NULL, - contrasts_colname = "Contrast", - select_contrasts = c(), +plot_venn_sets <- function( + venn_sets, plot_type = "Venn diagram", intersection_ids = c(), venn_force_unique = TRUE, @@ -224,34 +265,12 @@ S7::method(plot_venn_diagram, S7::class_data.frame) <- function( "UpSetR" )) - if (nrow(moo_diff_summary_dat) == 0) { - stop("Dataframe is empty") - } - - ### PH: - # Input - DEG table from Volcano Summary, I think we need to make this function more generic. - # The input should be the Limma DEG table and maybe be used with the DEG Gene List Template - # Output - Venn Diagram Figure + Venn table - # Purpose - compare DEGS from different Comparisons - - input_dataset <- as.data.frame(moo_diff_summary_dat) - if (is.null(feature_id_colname)) { - feature_id_colname <- colnames(moo_diff_summary_dat)[1] - } - - ### PH: Create venn Table from DEG table - - # SET INPUT ==== - - # select required columns - set_elements <- input_dataset[, feature_id_colname] - set_names <- input_dataset[, contrasts_colname] - - # prepare format - R list - vlist <- split(set_elements, set_names) - if (!is.null(select_contrasts)) { - vlist <- vlist[select_contrasts] + if ( + !is.list(venn_sets) || length(venn_sets) == 0 || is.null(names(venn_sets)) + ) { + stop("venn_sets must be a non-empty named list") } + vlist <- venn_sets num_categories <- length(vlist) if (num_categories == 0) { stop("Zero categories found") @@ -279,11 +298,7 @@ S7::method(plot_venn_diagram, S7::class_data.frame) <- function( if (num_categories > 1) { sets <- fromList(vlist) - if (!is.null(select_contrasts)) { - Intersection <- sets[, match(select_contrasts, colnames(sets))] - } else { - Intersection <- sets - } + Intersection <- sets # generate intersection frequency table and gene list (all intersections for the output dataset/table not the plot) intersection_matrix <- Intersection @@ -334,7 +349,7 @@ S7::method(plot_venn_diagram, S7::class_data.frame) <- function( # returned intersections - if (!is.null(intersection_ids)) { + if (length(intersection_ids) > 0) { intersection_ids <- sort(as.numeric(intersection_ids)) tabsel <- tab[tab$Id %in% intersection_ids, ] Intersectionsel <- Intersection[Intersection$Id %in% intersection_ids, ] @@ -397,7 +412,7 @@ S7::method(plot_venn_diagram, S7::class_data.frame) <- function( pSet <- UpSetR::upset( sets, nsets = num_categories, - sets = select_contrasts, + sets = names(vlist), order.by = intersections_order, nintersects = NA, text.scale = 2, diff --git a/inst/quarto/report.qmd b/inst/quarto/report.qmd index d5090eef..fbee2392 100644 --- a/inst/quarto/report.qmd +++ b/inst/quarto/report.qmd @@ -160,6 +160,6 @@ dat_volcano_enhanced <- moo@analyses$diff |> ### Venn Diagram ```{r venn_diagram} -venn_dat <- dat_volcano_summary |> plot_venn_diagram() +venn_dat <- moo |> plot_venn_diagram() head(venn_dat) ``` diff --git a/man/plot_venn_diagram.Rd b/man/plot_venn_diagram.Rd index 228ea032..fe499203 100644 --- a/man/plot_venn_diagram.Rd +++ b/man/plot_venn_diagram.Rd @@ -3,13 +3,15 @@ \name{plot_venn_diagram} \alias{plot_venn_diagram} \alias{plot_venn_diagram,MOSuite::multiOmicDataSet-method} -\alias{plot_venn_diagram,data.frame-method} \title{Plot a venn diagram, UpSet plot, or table of intersections} \usage{ plot_venn_diagram( moo_diff_summary_dat, feature_id_colname = NULL, - contrasts_colname = "Contrast", + signif_colname = "adjpval", + signif_threshold = 0.05, + change_colname = "logFC", + change_threshold = 1, select_contrasts = c(), plot_type = "Venn diagram", intersection_ids = c(), @@ -46,44 +48,10 @@ plot_venn_diagram( plot_venn_diagram( moo_diff_summary_dat, feature_id_colname = NULL, - contrasts_colname = "Contrast", - select_contrasts = c(), - plot_type = "Venn diagram", - intersection_ids = c(), - venn_force_unique = TRUE, - venn_numbers_format = "raw", - venn_significant_digits = 2, - venn_fill_colors = c("darkgoldenrod2", "darkolivegreen2", "mediumpurple3", - "darkorange2", "lightgreen"), - venn_fill_transparency = 0.2, - venn_border_colors = "fill colors", - venn_font_size_for_category_names = 3, - venn_category_names_distance = c(), - venn_category_names_position = c(), - venn_font_size_for_counts = 6, - venn_outer_margin = 0, - intersections_order = "degree", - display_empty_intersections = FALSE, - intersection_bar_color = "steelblue4", - intersection_point_size = 2.2, - intersection_line_width = 0.7, - table_font_size = 0.7, - table_content = "all intersections", - graphics_device = grDevices::png, - dpi = 300, - image_width = 4000, - image_height = 3000, - plot_filename = "venn_diagram.png", - print_plots = options::opt("print_plots"), - save_plots = options::opt("save_plots"), - plots_subdir = "diff" -) - -## S7 method for class -plot_venn_diagram( - moo_diff_summary_dat, - feature_id_colname = NULL, - contrasts_colname = "Contrast", + signif_colname = "adjpval", + signif_threshold = 0.05, + change_colname = "logFC", + change_threshold = 1, select_contrasts = c(), plot_type = "Venn diagram", intersection_ids = c(), @@ -117,17 +85,21 @@ plot_venn_diagram( ) } \arguments{ -\item{moo_diff_summary_dat}{Summarized differential expression analysis} +\item{moo_diff_summary_dat}{multiOmicDataSet containing per-contrast differential expression results.} -\item{feature_id_colname}{The column from the counts data containing the Feature IDs (Usually Gene or Protein ID). -This is usually the first column of your input Counts Matrix. Only columns of Text type from your input Counts -Matrix will be available to select for this parameter. (Default: \code{NULL} - first column in the counts matrix will be -used.)} +\item{feature_id_colname}{Feature identifier column in each contrast result. Defaults to the first column.} -\item{contrasts_colname}{Name of the column in \code{moo_diff_summary_dat} that contains the contrast names (default: -"Contrast")} +\item{signif_colname}{Significance column in each contrast result. Default: \code{"adjpval"}.} -\item{select_contrasts}{A vector of contrast names to select for the plot. If empty, all contrasts are used.} +\item{signif_threshold}{Features pass the significance threshold when their value is less than this cutoff +(exclusive). Default: \code{0.05}.} + +\item{change_colname}{Log fold-change column in each contrast result. Default: \code{"logFC"}.} + +\item{change_threshold}{Features pass the fold-change threshold when their absolute value is greater than this +cutoff (exclusive). Default: \code{1.0}.} + +\item{select_contrasts}{Contrast names to include. If empty, all available contrasts are used.} \item{plot_type}{Type of plot to generate: "Venn diagram" or "Intersection plot". Default: "Venn diagram"} @@ -172,32 +144,26 @@ same colors as \code{venn_fill_colors})} \item{table_content}{Content of the table in the plot. Default: NULL} -\item{graphics_device}{passed to \code{ggsave(device)}. Default: \code{grDevices::png}} +\item{graphics_device}{Graphics device used to save the plot. Default: \code{grDevices::png}} -\item{dpi}{dots-per-inch of the output image (see \code{ggsave()}) - only used if save_plots is TRUE} +\item{dpi}{Dots per inch of the saved plot. Default: 300} -\item{image_width}{output image width in inches - only used if save_plots is TRUE. Default: 10} +\item{image_width}{Saved plot width in pixels. Default: 4000} -\item{image_height}{output image height in inches - only used if save_plots is TRUE. Default: 10} +\item{image_height}{Saved plot height in pixels. Default: 3000} -\item{plot_filename}{plot output filename - only used if save_plots is TRUE. When multiple comparisons are saved -separately, the comparison name is appended before the file extension.} +\item{plot_filename}{Filename for the saved plot. Default: \code{"venn_diagram.png"}} -\item{print_plots}{Whether to print plots during analysis (Defaults to \code{FALSE}, overwritable using option 'moo_print_plots' or environment variable 'MOO_PRINT_PLOTS')} +\item{print_plots}{Whether to print plots. Default: \code{options::opt("print_plots")}} -\item{save_plots}{Whether to save plots to files during analysis (Defaults to \code{TRUE}, overwritable using option 'moo_save_plots' or environment variable 'MOO_SAVE_PLOTS')} +\item{save_plots}{Whether to save plots. Default: \code{options::opt("save_plots")}} -\item{plots_subdir}{subdirectory in \verb{figures/} where plots will be saved if \code{save_plots} is \code{TRUE}} +\item{plots_subdir}{Subdirectory for saved plots. Default: \code{"diff"}} } \description{ Generates Venn diagram of intersections across a series of sets (e.g., intersections of significant genes across tested contrasts). This Venn diagram is available for up to five sets; Intersection plot is available for any number of sets. Specific sets can be selected for the visualizations and the returned dataset may include all (default) or specified intersections. -An S7 generic with methods for \code{multiOmicDataSet} and \code{data.frame}. -} -\examples{ -plot_venn_diagram(nidap_volcano_summary_dat, print_plots = TRUE) - +An S7 generic for \code{multiOmicDataSet} inputs. } -\keyword{plotters} diff --git a/man/plot_venn_sets.Rd b/man/plot_venn_sets.Rd new file mode 100644 index 00000000..e5fff535 --- /dev/null +++ b/man/plot_venn_sets.Rd @@ -0,0 +1,105 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/plot_venn_diagram.R +\name{plot_venn_sets} +\alias{plot_venn_sets} +\title{Build Venn or UpSet intersections from feature sets} +\usage{ +plot_venn_sets( + venn_sets, + plot_type = "Venn diagram", + intersection_ids = c(), + venn_force_unique = TRUE, + venn_numbers_format = "raw", + venn_significant_digits = 2, + venn_fill_colors = c("darkgoldenrod2", "darkolivegreen2", "mediumpurple3", + "darkorange2", "lightgreen"), + venn_fill_transparency = 0.2, + venn_border_colors = "fill colors", + venn_font_size_for_category_names = 3, + venn_category_names_distance = c(), + venn_category_names_position = c(), + venn_font_size_for_counts = 6, + venn_outer_margin = 0, + intersections_order = "degree", + display_empty_intersections = FALSE, + intersection_bar_color = "steelblue4", + intersection_point_size = 2.2, + intersection_line_width = 0.7, + table_font_size = 0.7, + table_content = "all intersections", + graphics_device = grDevices::png, + dpi = 300, + image_width = 4000, + image_height = 3000, + plot_filename = "venn_diagram.png", + print_plots = options::opt("print_plots"), + save_plots = options::opt("save_plots"), + plots_subdir = "diff" +) +} +\arguments{ +\item{venn_sets}{Named list of feature identifier vectors, one per contrast.} + +\item{plot_type}{Type of plot to generate: "Venn diagram" or "Intersection plot". Default: "Venn diagram"} + +\item{intersection_ids}{A vector of intersection IDs to select for the plot. If empty, all intersections are used.} + +\item{venn_force_unique}{If TRUE, forces unique elements in the Venn diagram. Default: TRUE} + +\item{venn_numbers_format}{Format for the numbers in the Venn diagram. Options: "raw", "percent", "raw-percent", +"percent-raw". Default: "raw"} + +\item{venn_significant_digits}{Number of significant digits for the Venn diagram numbers. Default: 2} + +\item{venn_fill_colors}{A vector of colors to fill the Venn diagram categories. Default: c("darkgoldenrod2", +"darkolivegreen2", "mediumpurple3", "darkorange2", "lightgreen")} + +\item{venn_fill_transparency}{Transparency level for the Venn diagram fill colors. Default: 0.2} + +\item{venn_border_colors}{Colors for the borders of the Venn diagram categories. Default: "fill colors" (uses the +same colors as \code{venn_fill_colors})} + +\item{venn_font_size_for_category_names}{Font size for the category names in the Venn diagram. Default: 3} + +\item{venn_category_names_distance}{Distance of the category names from the Venn diagram circles. Default: c()} + +\item{venn_category_names_position}{Position of the category names in the Venn diagram. Default: c()} + +\item{venn_font_size_for_counts}{Font size for the counts in the Venn diagram. Default: 6} + +\item{venn_outer_margin}{Outer margin for the Venn diagram. Default: 0} + +\item{intersections_order}{Order of the intersections in the plot. Default: "by size"} + +\item{display_empty_intersections}{If TRUE, displays empty intersections in the plot. Default: FALSE} + +\item{intersection_bar_color}{Color for the intersection bars in the plot. Default: "lightgray"} + +\item{intersection_point_size}{Size of the points in the intersection plot. Default: 2} + +\item{intersection_line_width}{Width of the lines in the intersection plot. Default: 0.5} + +\item{table_font_size}{Font size for the table in the plot. Default: 3} + +\item{table_content}{Content of the table in the plot. Default: NULL} + +\item{graphics_device}{Graphics device used to save the plot. Default: \code{grDevices::png}} + +\item{dpi}{Dots per inch of the saved plot. Default: 300} + +\item{image_width}{Saved plot width in pixels. Default: 4000} + +\item{image_height}{Saved plot height in pixels. Default: 3000} + +\item{plot_filename}{Filename for the saved plot. Default: \code{"venn_diagram.png"}} + +\item{print_plots}{Whether to print plots. Default: \code{options::opt("print_plots")}} + +\item{save_plots}{Whether to save plots. Default: \code{options::opt("save_plots")}} + +\item{plots_subdir}{Subdirectory for saved plots. Default: \code{"diff"}} +} +\description{ +Build Venn or UpSet intersections from feature sets +} +\keyword{internal} diff --git a/tests/testthat/_snaps/plot_venn_diagram.md b/tests/testthat/_snaps/plot_venn_diagram.md deleted file mode 100644 index b6e1eb0e..00000000 --- a/tests/testthat/_snaps/plot_venn_diagram.md +++ /dev/null @@ -1,9 +0,0 @@ -# plot_venn_diagram works with defaults - - Code - p <- plot_venn_diagram(nidap_volcano_summary_dat, print_plots = FALSE, - save_plots = TRUE) - Message - All intersections: 1:7,c(1, 2, 3, 4, 5, 6, 7),c(163, 237, 518, 780, 225, 379, 766),c("Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes") - Intersections returned: 1:7,c(1, 2, 3, 4, 5, 6, 7),c(163, 237, 518, 780, 225, 379, 766) - diff --git a/tests/testthat/test-plot_venn_diagram.R b/tests/testthat/test-plot_venn_diagram.R index 9c637156..0136cb97 100644 --- a/tests/testthat/test-plot_venn_diagram.R +++ b/tests/testthat/test-plot_venn_diagram.R @@ -1,56 +1,89 @@ -test_that("plot_venn_diagram works with defaults", { - expect_snapshot( - p <- plot_venn_diagram( - nidap_volcano_summary_dat, - print_plots = FALSE, - save_plots = TRUE +make_venn_test_moo <- function() { + contrast_ba <- data.frame( + Gene = c("both", "ba_only", "p_boundary", "fc_boundary"), + logFC = c(2, 2, 2, 1), + adjpval = c(0.01, 0.01, 0.05, 0.01) + ) + contrast_ca <- data.frame( + Gene = c("both", "ca_only", "p_boundary", "fc_boundary"), + logFC = c(-2, -2, -2, -1), + adjpval = c(0.01, 0.01, 0.05, 0.01) + ) + return( + multiOmicDataSet( + sample_metadata = data.frame(Sample = "S1"), + anno_dat = data.frame(), + counts_lst = list(raw = data.frame(Gene = "seed", S1 = 1)), + analyses_lst = list(diff = list("B-A" = contrast_ba, "C-A" = contrast_ca)) ) ) - expect_equal( - plot_venn_diagram(nidap_volcano_summary_dat), - as.data.frame(nidap_venn_diagram_dat) +} + +test_that("plot_venn_diagram builds intersections directly from DEG MOO results", { + result <- plot_venn_diagram( + make_venn_test_moo(), + print_plots = FALSE, + save_plots = FALSE ) + + expect_s3_class(result, "data.frame") + expect_setequal(result$Gene, c("both", "ba_only", "ca_only")) + expect_false(any(result$Gene %in% c("p_boundary", "fc_boundary"))) + expect_true("(B-A ∩ C-A)" %in% result$Intersection) }) -test_that("plot_venn_diagram raises condition for empty df", { - expect_error( - plot_venn_diagram(structure( - list( - GeneName = character(0), - Contrast = character(0), - FC = numeric(0), - logFC = numeric(0), - tstat = numeric(0), - pval = numeric(0), - adjpval = numeric(0) - ), - class = "data.frame", - row.names = integer(0) - )), - "Dataframe is empty" + +test_that("plot_venn_diagram respects a subset of MOO contrasts", { + moo <- make_venn_test_moo() + moo@analyses$diff[["D-A"]] <- data.frame( + Gene = c("both", "da_only"), + logFC = c(2, 2), + adjpval = c(0.01, 0.01) + ) + + result <- plot_venn_diagram( + moo, + select_contrasts = c("B-A", "C-A"), + print_plots = FALSE, + save_plots = FALSE ) + + expect_setequal(result$Gene, c("both", "ba_only", "ca_only")) + expect_false("da_only" %in% result$Gene) + expect_false(any(grepl("D-A", result$Intersection, fixed = TRUE))) }) -test_that("intersection matrix assignment avoids recursive evaluation error", { - # This test demonstrates the fix for the recursive default argument reference error - # The error occurred with this pattern: - # Intersection <- sapply(colnames(Intersection), function(x) Intersection[, x]) - # The fix uses a temporary variable: - # intersection_matrix <- Intersection; - # Intersection <- sapply(colnames(intersection_matrix), ...) - - # Call plot_venn_diagram directly to ensure the fix works in practice - expect_no_error({ - result <- plot_venn_diagram( - nidap_volcano_summary_dat, +test_that("plot_venn_diagram errors for missing contrast DEG columns", { + expect_error( + plot_venn_diagram( + make_venn_test_moo(), + signif_colname = "missing_column", print_plots = FALSE, save_plots = FALSE + ), + "Required DEG columns not found" + ) +}) + +test_that("plot_venn_diagram uses an Intersection plot for more than five contrasts", { + moo <- make_venn_test_moo() + moo@analyses$diff <- rep(moo@analyses$diff["B-A"], 6) + names(moo@analyses$diff) <- paste0("contrast_", seq_len(6)) + for (contrast_name in names(moo@analyses$diff)) { + moo@analyses$diff[[contrast_name]] <- data.frame( + Gene = c("both", contrast_name), + logFC = c(2, 2), + adjpval = c(0.01, 0.01) ) - }) + } - # Verify the result has the expected structure - expect_s3_class(result, "data.frame") - expect_true("Gene" %in% colnames(result)) - expect_true("Intersection" %in% colnames(result)) - expect_true("Id" %in% colnames(result)) - expect_true("Size" %in% colnames(result)) + expect_no_error( + suppressWarnings( + plot_venn_diagram( + moo, + plot_type = "Venn diagram", + print_plots = FALSE, + save_plots = FALSE + ) + ) + ) }) diff --git a/vignettes/visualization.Rmd b/vignettes/visualization.Rmd index ffa10732..9352f0f8 100644 --- a/vignettes/visualization.Rmd +++ b/vignettes/visualization.Rmd @@ -153,7 +153,7 @@ head(dat_volcano_summary) ### Venn Diagram ```{r venn_diagram, fig.width=7, fig.height=7} -venn_dat <- dat_volcano_summary |> plot_venn_diagram() +venn_dat <- moo |> plot_venn_diagram() head(venn_dat) ```