diff --git a/DESCRIPTION b/DESCRIPTION index 76f4fdaf..3117d87d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -53,6 +53,7 @@ Suggests: ggVennDiagram (>= 1.5.0), ggupset, ggpubr, + ggbeeswarm, ggforce, ggraph, ggridges, diff --git a/NAMESPACE b/NAMESPACE index 446ccf3a..d535e6df 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -6,6 +6,7 @@ S3method(element_grob,element_textbox) export(AlluvialPlot) export(AreaPlot) export(BarPlot) +export(BeeswarmPlot) export(BoxPlot) export(ChordPlot) export(CircosPlot) diff --git a/R/boxviolinplot.R b/R/boxviolinplot.R index e7c508fa..d2a7ad7a 100644 --- a/R/boxviolinplot.R +++ b/R/boxviolinplot.R @@ -7,7 +7,7 @@ #' When `in_form` is "wide", `x` columns will not be concatenated. #' @param y A character string of the column name to plot on the y-axis. A numeric column is expected. #' When `in_form` is "wide", `y` is not required. The values under `x` columns will be used as y-values. -#' @param base A character string to specify the base plot type. Either "box" or "violin". +#' @param base A character string to specify the base plot type. Either "box", "violin" or "none" (used by BeeswarmPlot). #' @param in_form A character string to specify the input data type. Either "long" or "wide". #' @param sort_x A character string to specify the sorting of x-axis, chosen from "none", "mean_asc", "mean_desc", "mean", "median_asc", "median_desc", "median". #' * `none` means no sorting (as-is). @@ -32,6 +32,17 @@ #' @param jitter_width A numeric value to specify the width of the jitter. #' Defaults to 0.5, but when paired_by is provided, it will be set to 0. #' @param jitter_height A numeric value to specify the height of the jitter. +#' @param add_beeswarm A logical value to add beeswarm points to the plot instead of jittered points. +#' When TRUE, points are positioned using the beeswarm algorithm to avoid overlap while showing density. +#' Requires the ggbeeswarm package to be installed. +#' @param beeswarm_method A character string to specify the beeswarm method. Either "swarm", "compactswarm", "hex", +#' "square", or "center". Default is "swarm". See ggbeeswarm::geom_beeswarm for details. +#' @param beeswarm_cex A numeric value to specify the scaling for adjusting point spacing in beeswarm. +#' Default is 1. Larger values space out points more. +#' @param beeswarm_priority A character string to specify point layout priority. Either "ascending", "descending", +#' "density", or "random". Default is "ascending". +#' @param beeswarm_dodge A numeric value to specify the dodge width for beeswarm points when group_by is provided. +#' Default is 0.9 #' @param stack A logical value whether to stack the facetted plot by 'facet_by'. #' @param y_max A numeric value or a character string to specify the maximum value of the y-axis. #' You can also use quantile notation like "q95" to specify the 95th percentile. @@ -109,16 +120,17 @@ #' @importFrom ggplot2 labs theme element_line element_text position_dodge position_jitter coord_flip layer_scales #' @importFrom ggplot2 position_jitterdodge scale_shape_identity scale_size_manual scale_alpha_manual scale_y_continuous BoxViolinPlotAtomic <- function( - data, x, x_sep = "_", y = NULL, base = c("box", "violin"), in_form = c("long", "wide"), + data, x, x_sep = "_", y = NULL, base = c("box", "violin", "none"), in_form = c("long", "wide"), sort_x = c("none", "mean_asc", "mean_desc", "mean", "median_asc", "median_desc", "median"), flip = FALSE, keep_empty = FALSE, group_by = NULL, group_by_sep = "_", group_name = NULL, paired_by = NULL, x_text_angle = ifelse(isTRUE(flip) && isTRUE(stack), 90, 45), step_increase = 0.1, fill_mode = ifelse(!is.null(group_by), "dodge", "x"), fill_reverse = FALSE, symnum_args = NULL, theme = "theme_this", theme_args = list(), palette = "Paired", palcolor = NULL, alpha = 1, aspect.ratio = NULL, legend.position = "right", legend.direction = "vertical", - add_point = FALSE, pt_color = "grey30", pt_size = NULL, pt_alpha = 1, y_nbreaks = 4, + add_point = FALSE, pt_color = if (isTRUE(add_beeswarm)) NULL else "grey30", pt_size = NULL, pt_alpha = 1, y_nbreaks = 4, jitter_width = NULL, jitter_height = 0, stack = FALSE, y_max = NULL, y_min = NULL, y_trans = "identity", - add_box = FALSE, box_color = "black", box_width = 0.1, box_ptsize = 2.5, + add_beeswarm = FALSE, beeswarm_method = "swarm", beeswarm_cex = 1, beeswarm_priority = "ascending", + beeswarm_dodge = 0.9, add_box = FALSE, box_color = "black", box_width = 0.1, box_ptsize = 2.5, add_trend = FALSE, trend_color = NULL, trend_linewidth = 1, trend_ptsize = 2, add_stat = NULL, stat_name = NULL, stat_color = "black", stat_size = 1, stat_stroke = 1, stat_shape = 25, add_bg = FALSE, bg_palette = "stripe", bg_palcolor = NULL, bg_alpha = 0.2, @@ -152,6 +164,19 @@ BoxViolinPlotAtomic <- function( paired_by <- check_columns(data, paired_by, force_factor = TRUE) base_size <- theme_args$base_size %||% 12 sig_labelsize <- sig_labelsize * base_size / 12 + + # Validate beeswarm parameters + if (isTRUE(add_beeswarm)) { + if (!requireNamespace("ggbeeswarm", quietly = TRUE)) { + stop("Package 'ggbeeswarm' is required for beeswarm plots. Please install it with: install.packages('ggbeeswarm')") + } + add_point <- TRUE + if (!is.null(paired_by)) { + warning("'add_beeswarm' is not fully compatible with 'paired_by'. Using jittered points instead for paired data.") + add_beeswarm <- FALSE + } + } + if (!is.null(paired_by)) { if (!isTRUE(add_point)) { warning("Forcing 'add_point' = TRUE when 'paired_by' is provided.") @@ -321,11 +346,11 @@ BoxViolinPlotAtomic <- function( p <- p + bg_layer(data, x, bg_palette, bg_palcolor, bg_alpha, keep_empty, facet_by) } - if (base == "box") { + if (base == "box" || (base == "none" && isTRUE(add_box))) { p <- p + geom_boxplot( position = position_dodge(width = 0.9), color = "black", width = 0.8, outlier.shape = NA ) - } else { + } else if (base == "violin") { p <- p + geom_violin( # There is a bug in ggplot2 with preserve = "single" for violin plots # See https://github.com/tidyverse/ggplot2/issues/2801 @@ -354,7 +379,8 @@ BoxViolinPlotAtomic <- function( ) } - if (isTRUE(add_box)) { + # when base is none, boxes are added as base + if (isTRUE(add_box) && base != "none") { p <- p + new_scale_fill() + geom_boxplot( @@ -722,18 +748,60 @@ BoxViolinPlotAtomic <- function( ) } } - p <- p + - geom_point( - aes(fill = !!sym(fill_by), color = !!sym(".highlight"), size = !!sym(".highlight"), alpha = !!sym(".highlight")), - position = position_jitterdodge( - jitter.width = jitter_width %||% ifelse(!is.null(paired_by), 0, 0.5), - jitter.height = jitter_height, dodge.width = 0.9, seed = seed - ), - show.legend = FALSE - ) + - scale_color_manual(values = c("TRUE" = highlight_color, "FALSE" = pt_color)) + - scale_size_manual(values = c("TRUE" = highlight_size, "FALSE" = pt_size %||% min(3000 / nrow(data), 0.6))) + - scale_alpha_manual(values = c("TRUE" = highlight_alpha, "FALSE" = pt_alpha)) + + # Use beeswarm or jittered points + if (isTRUE(add_beeswarm)) { + # Use ggbeeswarm for non-overlapping point layout + if (!is.null(pt_color)) { + p <- p + + ggbeeswarm::geom_beeswarm( + aes(size = !!sym(".highlight"), alpha = !!sym(".highlight")), + color = pt_color, + method = beeswarm_method, + cex = beeswarm_cex, + priority = beeswarm_priority, + dodge.width = beeswarm_dodge, + show.legend = FALSE + ) + + } else { + p <- p + + ggbeeswarm::geom_beeswarm( + aes(color = !!sym(fill_by), size = !!sym(".highlight"), alpha = !!sym(".highlight")), + method = beeswarm_method, + cex = beeswarm_cex, + priority = beeswarm_priority, + dodge.width = beeswarm_dodge + ) + + scale_color_manual( + values = palette_this(levels(data[[fill_by]]), palette = palette, palcolor = palcolor), + guide = "legend" + ) + } + p <- p + + scale_size_manual( + values = c("TRUE" = highlight_size, "FALSE" = pt_size %||% min(3000 / nrow(data), 0.6)), + guide = "none" + ) + + scale_alpha_manual( + values = c("TRUE" = highlight_alpha, "FALSE" = pt_alpha), + guide = "none" + ) + } else { + # Use regular jittered points + p <- p + + geom_point( + aes(fill = !!sym(fill_by), color = !!sym(".highlight"), size = !!sym(".highlight"), alpha = !!sym(".highlight")), + position = position_jitterdodge( + jitter.width = jitter_width %||% ifelse(!is.null(paired_by), 0, 0.5), + jitter.height = jitter_height, dodge.width = 0.9, seed = seed + ), + show.legend = FALSE + ) + + scale_color_manual(values = c("TRUE" = highlight_color, "FALSE" = pt_color)) + + scale_size_manual(values = c("TRUE" = highlight_size, "FALSE" = pt_size %||% min(3000 / nrow(data), 0.6))) + + scale_alpha_manual(values = c("TRUE" = highlight_alpha, "FALSE" = pt_alpha)) + } } if (isTRUE(add_trend)) { @@ -909,9 +977,10 @@ BoxViolinPlot <- function( fill_mode = ifelse(!is.null(group_by), "dodge", "x"), fill_reverse = FALSE, theme = "theme_this", theme_args = list(), palette = "Paired", palcolor = NULL, alpha = 1, aspect.ratio = NULL, legend.position = "right", legend.direction = "vertical", - add_point = FALSE, pt_color = "grey30", pt_size = NULL, pt_alpha = 1, + add_point = FALSE, pt_color = if(isTRUE(add_beeswarm)) NULL else "grey30", pt_size = NULL, pt_alpha = 1, jitter_width = NULL, jitter_height = 0, stack = FALSE, y_max = NULL, y_min = NULL, - add_box = FALSE, box_color = "black", box_width = 0.1, box_ptsize = 2.5, + add_beeswarm = FALSE, beeswarm_method = "swarm", beeswarm_cex = 1, beeswarm_priority = "ascending", + beeswarm_dodge = 0.9, add_box = FALSE, box_color = "black", box_width = 0.1, box_ptsize = 2.5, add_trend = FALSE, trend_color = NULL, trend_linewidth = 1, trend_ptsize = 2, add_stat = NULL, stat_name = NULL, stat_color = "black", stat_size = 1, stat_stroke = 1, stat_shape = 25, add_bg = FALSE, bg_palette = "stripe", bg_palcolor = NULL, bg_alpha = 0.2, @@ -960,7 +1029,8 @@ BoxViolinPlot <- function( aspect.ratio = aspect.ratio, legend.position = legend.position[[nm]], legend.direction = legend.direction[[nm]], add_point = add_point, pt_color = pt_color, pt_size = pt_size, pt_alpha = pt_alpha, symnum_args = symnum_args, jitter_width = jitter_width, jitter_height = jitter_height, stack = stack, y_max = y_max, y_min = y_min, - add_box = add_box, box_color = box_color, box_width = box_width, box_ptsize = box_ptsize, + add_beeswarm = add_beeswarm, beeswarm_method = beeswarm_method, beeswarm_cex = beeswarm_cex, beeswarm_priority = beeswarm_priority, + beeswarm_dodge = beeswarm_dodge, add_box = add_box, box_color = box_color, box_width = box_width, box_ptsize = box_ptsize, add_trend = add_trend, trend_color = trend_color, trend_linewidth = trend_linewidth, trend_ptsize = trend_ptsize, add_stat = add_stat, stat_name = stat_name, stat_color = stat_color, stat_size = stat_size, stat_stroke = stat_stroke, stat_shape = stat_shape, add_bg = add_bg, bg_palette = bg_palette, bg_palcolor = bg_palcolor, bg_alpha = bg_alpha, @@ -1001,6 +1071,7 @@ BoxViolinPlot <- function( #' ) #' #' BoxPlot(data, x = "x", y = "y") +#' BoxPlot(data, x = "x", y = "y", add_beeswarm = TRUE, pt_color = "grey30") #' BoxPlot(data, #' x = "x", y = "y", #' stack = TRUE, flip = TRUE, facet_by = "group1", @@ -1054,9 +1125,10 @@ BoxPlot <- function( fill_mode = ifelse(!is.null(group_by), "dodge", "x"), fill_reverse = FALSE, theme = "theme_this", theme_args = list(), palette = "Paired", palcolor = NULL, alpha = 1, aspect.ratio = NULL, legend.position = "right", legend.direction = "vertical", - add_point = FALSE, pt_color = "grey30", pt_size = NULL, pt_alpha = 1, + add_point = FALSE, pt_color = if(isTRUE(add_beeswarm)) NULL else "grey30", pt_size = NULL, pt_alpha = 1, jitter_width = NULL, jitter_height = 0, stack = FALSE, y_max = NULL, y_min = NULL, - add_trend = FALSE, trend_color = NULL, trend_linewidth = 1, trend_ptsize = 2, + add_beeswarm = FALSE, beeswarm_method = "swarm", beeswarm_cex = 1, beeswarm_priority = "ascending", + beeswarm_dodge = 0.9, add_trend = FALSE, trend_color = NULL, trend_linewidth = 1, trend_ptsize = 2, add_stat = NULL, stat_name = NULL, stat_color = "black", stat_size = 1, stat_stroke = 1, stat_shape = 25, add_bg = FALSE, bg_palette = "stripe", bg_palcolor = NULL, bg_alpha = 0.2, add_line = NULL, line_color = "red2", line_width = .6, line_type = 2, @@ -1078,7 +1150,8 @@ BoxPlot <- function( aspect.ratio = aspect.ratio, legend.position = legend.position, legend.direction = legend.direction, add_point = add_point, pt_color = pt_color, pt_size = pt_size, pt_alpha = pt_alpha, symnum_args = symnum_args, jitter_width = jitter_width, jitter_height = jitter_height, stack = stack, y_max = y_max, y_min = y_min, - add_trend = add_trend, trend_color = trend_color, trend_linewidth = trend_linewidth, trend_ptsize = trend_ptsize, + add_beeswarm = add_beeswarm, beeswarm_method = beeswarm_method, beeswarm_cex = beeswarm_cex, beeswarm_priority = beeswarm_priority, + beeswarm_dodge = beeswarm_dodge, add_trend = add_trend, trend_color = trend_color, trend_linewidth = trend_linewidth, trend_ptsize = trend_ptsize, add_stat = add_stat, stat_name = stat_name, stat_color = stat_color, stat_size = stat_size, stat_stroke = stat_stroke, stat_shape = stat_shape, add_bg = add_bg, bg_palette = bg_palette, bg_palcolor = bg_palcolor, bg_alpha = bg_alpha, add_line = add_line, line_color = line_color, line_width = line_width, line_type = line_type, @@ -1098,6 +1171,7 @@ BoxPlot <- function( #' @examples #' \donttest{ #' ViolinPlot(data, x = "x", y = "y") +#' ViolinPlot(data, x = "x", y = "y", add_beeswarm = TRUE, pt_color = "grey30") #' ViolinPlot(data, x = "x", y = "y", add_box = TRUE) #' ViolinPlot(data, x = "x", y = "y", add_point = TRUE) #' ViolinPlot(data, x = "x", y = "y", add_trend = TRUE) @@ -1142,9 +1216,10 @@ ViolinPlot <- function( fill_mode = ifelse(!is.null(group_by), "dodge", "x"), fill_reverse = FALSE, theme = "theme_this", theme_args = list(), palette = "Paired", palcolor = NULL, alpha = 1, aspect.ratio = NULL, legend.position = "right", legend.direction = "vertical", - add_point = FALSE, pt_color = "grey30", pt_size = NULL, pt_alpha = 1, + add_point = FALSE, pt_color = if(isTRUE(add_beeswarm)) NULL else "grey30", pt_size = NULL, pt_alpha = 1, jitter_width = NULL, jitter_height = 0, stack = FALSE, y_max = NULL, y_min = NULL, - add_box = FALSE, box_color = "black", box_width = 0.1, box_ptsize = 2.5, + add_beeswarm = FALSE, beeswarm_method = "swarm", beeswarm_cex = 1, beeswarm_priority = "ascending", + beeswarm_dodge = 0.9, add_box = FALSE, box_color = "black", box_width = 0.1, box_ptsize = 2.5, add_trend = FALSE, trend_color = NULL, trend_linewidth = 1, trend_ptsize = 2, add_stat = NULL, stat_name = NULL, stat_color = "black", stat_size = 1, stat_stroke = 1, stat_shape = 25, add_bg = FALSE, bg_palette = "stripe", bg_palcolor = NULL, bg_alpha = 0.2, @@ -1167,7 +1242,86 @@ ViolinPlot <- function( aspect.ratio = aspect.ratio, legend.position = legend.position, legend.direction = legend.direction, add_point = add_point, pt_color = pt_color, pt_size = pt_size, pt_alpha = pt_alpha, symnum_args = symnum_args, jitter_width = jitter_width, jitter_height = jitter_height, stack = stack, y_max = y_max, y_min = y_min, - add_box = add_box, box_color = box_color, box_width = box_width, box_ptsize = box_ptsize, + add_beeswarm = add_beeswarm, beeswarm_method = beeswarm_method, beeswarm_cex = beeswarm_cex, beeswarm_priority = beeswarm_priority, + beeswarm_dodge = beeswarm_dodge, add_box = add_box, box_color = box_color, box_width = box_width, box_ptsize = box_ptsize, + add_trend = add_trend, trend_color = trend_color, trend_linewidth = trend_linewidth, trend_ptsize = trend_ptsize, + add_stat = add_stat, stat_name = stat_name, stat_color = stat_color, stat_size = stat_size, stat_stroke = stat_stroke, stat_shape = stat_shape, + add_bg = add_bg, bg_palette = bg_palette, bg_palcolor = bg_palcolor, bg_alpha = bg_alpha, + add_line = add_line, line_color = line_color, line_width = line_width, line_type = line_type, + highlight = highlight, highlight_color = highlight_color, highlight_size = highlight_size, highlight_alpha = highlight_alpha, + comparisons = comparisons, ref_group = ref_group, pairwise_method = pairwise_method, + multiplegroup_comparisons = multiplegroup_comparisons, multiple_method = multiple_method, + sig_label = sig_label, sig_labelsize = sig_labelsize, hide_ns = hide_ns, + facet_by = facet_by, facet_scales = facet_scales, facet_ncol = facet_ncol, facet_nrow = facet_nrow, facet_byrow = facet_byrow, + title = title, subtitle = subtitle, xlab = xlab, ylab = ylab, seed = seed, combine = combine, nrow = nrow, ncol = ncol, byrow = byrow, + axes = axes, axis_titles = axis_titles, guides = guides, ... + ) +} + +#' @rdname boxviolinplot +#' @export +#' @inheritParams BoxViolinPlot +#' @param add_violin Logical, whether to add violin plot behind the beeswarm points. +#' Adding violin to a beeswarm plot is actually not supported. A message will be shown to +#' remind users to use `ViolinPlot(..., add_beeswarm = TRUE)` instead. +#' @examples +#' \donttest{ +#' # Beeswarm plot examples +#' BeeswarmPlot(data, x = "x", y = "y") +#' BeeswarmPlot(data, x = "x", y = "y", pt_size = 1) +#' BeeswarmPlot(data, x = "x", y = "y", add_box = TRUE, pt_color = "grey30") +#' # Equivalent to: +#' # BoxPlot(data, x = "x", y = "y", add_beeswarm = TRUE, pt_color = "grey30") +#' +#' BeeswarmPlot(data, x = "x", y = "y", group_by = "group1") +#' # no dodging +#' BeeswarmPlot(data, x = "x", y = "y", group_by = "group1", beeswarm_dodge = NULL) +#' +#' BeeswarmPlot(data, +#' x = "x", y = "y", beeswarm_method = "hex", +#' beeswarm_cex = 2 +#' ) +#' } +BeeswarmPlot <- function( + data, x, x_sep = "_", y = NULL, in_form = c("long", "wide"), + split_by = NULL, split_by_sep = "_", symnum_args = NULL, + sort_x = c("none", "mean_asc", "mean_desc", "mean", "median_asc", "median_desc", "median"), + flip = FALSE, keep_empty = FALSE, group_by = NULL, group_by_sep = "_", group_name = NULL, + paired_by = NULL, x_text_angle = ifelse(isTRUE(flip) && isTRUE(stack), 90, 45), step_increase = 0.1, + fill_mode = ifelse(!is.null(group_by), "dodge", "x"), fill_reverse = FALSE, + theme = "theme_this", theme_args = list(), palette = "Paired", palcolor = NULL, alpha = 1, + aspect.ratio = NULL, legend.position = "right", legend.direction = "vertical", + pt_color = NULL, pt_size = NULL, pt_alpha = 1, + jitter_width = NULL, jitter_height = 0, stack = FALSE, y_max = NULL, y_min = NULL, add_violin = FALSE, + beeswarm_method = "swarm", beeswarm_cex = 1, beeswarm_priority = "ascending", beeswarm_dodge = 0.9, + add_box = FALSE, box_color = "black", box_width = 0.1, box_ptsize = 2.5, + add_trend = FALSE, trend_color = NULL, trend_linewidth = 1, trend_ptsize = 2, + add_stat = NULL, stat_name = NULL, stat_color = "black", stat_size = 1, stat_stroke = 1, stat_shape = 25, + add_bg = FALSE, bg_palette = "stripe", bg_palcolor = NULL, bg_alpha = 0.2, + add_line = NULL, line_color = "red2", line_width = .6, line_type = 2, + highlight = NULL, highlight_color = "red2", highlight_size = 1, highlight_alpha = 1, + comparisons = NULL, ref_group = NULL, pairwise_method = "wilcox.test", + multiplegroup_comparisons = FALSE, multiple_method = "kruskal.test", + sig_label = "p.format", sig_labelsize = 3.5, hide_ns = FALSE, + facet_by = NULL, facet_scales = "fixed", facet_ncol = NULL, facet_nrow = NULL, facet_byrow = TRUE, + title = NULL, subtitle = NULL, xlab = NULL, ylab = NULL, seed = 8525, + combine = TRUE, nrow = NULL, ncol = NULL, byrow = TRUE, + axes = NULL, axis_titles = axes, guides = NULL, ...) { + if (isTRUE(add_violin)) { + stop("Adding violin to a beeswarm plot is not supported. Please use ViolinPlot(..., add_beeswarm = TRUE) instead.") + } + stat_name <- stat_name %||% paste0(y, " (", deparse(substitute(add_stat)), ")") + BoxViolinPlot( + data = data, x = x, x_sep = x_sep, y = y, base = "none", in_form = in_form, + split_by = split_by, split_by_sep = split_by_sep, + sort_x = sort_x, flip = flip, keep_empty = keep_empty, group_by = group_by, group_by_sep = group_by_sep, group_name = group_name, + paired_by = paired_by, x_text_angle = x_text_angle, fill_mode = fill_mode, fill_reverse = fill_reverse, step_increase = step_increase, + theme = theme, theme_args = theme_args, palette = palette, palcolor = palcolor, alpha = alpha, + aspect.ratio = aspect.ratio, legend.position = legend.position, legend.direction = legend.direction, + add_point = TRUE, pt_color = pt_color, pt_size = pt_size, pt_alpha = pt_alpha, symnum_args = symnum_args, + jitter_width = jitter_width, jitter_height = jitter_height, stack = stack, y_max = y_max, y_min = y_min, + add_beeswarm = TRUE, beeswarm_method = beeswarm_method, beeswarm_cex = beeswarm_cex, beeswarm_priority = beeswarm_priority, + beeswarm_dodge = beeswarm_dodge, add_box = add_box, box_color = box_color, box_width = box_width, box_ptsize = box_ptsize, add_trend = add_trend, trend_color = trend_color, trend_linewidth = trend_linewidth, trend_ptsize = trend_ptsize, add_stat = add_stat, stat_name = stat_name, stat_color = stat_color, stat_size = stat_size, stat_stroke = stat_stroke, stat_shape = stat_shape, add_bg = add_bg, bg_palette = bg_palette, bg_palcolor = bg_palcolor, bg_alpha = bg_alpha, diff --git a/_pkgdown.yml b/_pkgdown.yml index 79b9b6c7..97b38753 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -9,6 +9,7 @@ reference: - AlluvialPlot - AreaPlot - BarPlot + - BeeswarmPlot - BoxPlot - ChordPlot - CircosPlot diff --git a/man/BoxViolinPlot-internal.Rd b/man/BoxViolinPlot-internal.Rd index 44f9dbc4..ea3b4c00 100644 --- a/man/BoxViolinPlot-internal.Rd +++ b/man/BoxViolinPlot-internal.Rd @@ -35,7 +35,7 @@ BoxViolinPlot( legend.position = "right", legend.direction = "vertical", add_point = FALSE, - pt_color = "grey30", + pt_color = if (isTRUE(add_beeswarm)) NULL else "grey30", pt_size = NULL, pt_alpha = 1, jitter_width = NULL, @@ -43,6 +43,11 @@ BoxViolinPlot( stack = FALSE, y_max = NULL, y_min = NULL, + add_beeswarm = FALSE, + beeswarm_method = "swarm", + beeswarm_cex = 1, + beeswarm_priority = "ascending", + beeswarm_dodge = 0.9, add_box = FALSE, box_color = "black", box_width = 0.1, @@ -108,7 +113,7 @@ When \code{in_form} is "wide", \code{x} columns will not be concatenated.} \item{y}{A character string specifying the column name of the data frame to plot for the y-axis.} -\item{base}{A character string to specify the base plot type. Either "box" or "violin".} +\item{base}{A character string to specify the base plot type. Either "box", "violin" or "none" (used by BeeswarmPlot).} \item{in_form}{A character string to specify the input data type. Either "long" or "wide".} @@ -206,6 +211,22 @@ the significance labels.} \item{y_min}{A numeric value or a character string to specify the minimum value of the y-axis. You can also use quantile notation like "q5" to specify the 5th percentile.} +\item{add_beeswarm}{A logical value to add beeswarm points to the plot instead of jittered points. +When TRUE, points are positioned using the beeswarm algorithm to avoid overlap while showing density. +Requires the ggbeeswarm package to be installed.} + +\item{beeswarm_method}{A character string to specify the beeswarm method. Either "swarm", "compactswarm", "hex", +"square", or "center". Default is "swarm". See ggbeeswarm::geom_beeswarm for details.} + +\item{beeswarm_cex}{A numeric value to specify the scaling for adjusting point spacing in beeswarm. +Default is 1. Larger values space out points more.} + +\item{beeswarm_priority}{A character string to specify point layout priority. Either "ascending", "descending", +"density", or "random". Default is "ascending".} + +\item{beeswarm_dodge}{A numeric value to specify the dodge width for beeswarm points when group_by is provided. +Default is 0.9} + \item{add_box}{A logical value to add box plot to the plot.} \item{box_color}{A character string to specify the color of the box plot.} diff --git a/man/BoxViolinPlotAtomic.Rd b/man/BoxViolinPlotAtomic.Rd index 215f7ba0..7bb6e64a 100644 --- a/man/BoxViolinPlotAtomic.Rd +++ b/man/BoxViolinPlotAtomic.Rd @@ -9,7 +9,7 @@ BoxViolinPlotAtomic( x, x_sep = "_", y = NULL, - base = c("box", "violin"), + base = c("box", "violin", "none"), in_form = c("long", "wide"), sort_x = c("none", "mean_asc", "mean_desc", "mean", "median_asc", "median_desc", "median"), @@ -33,7 +33,7 @@ BoxViolinPlotAtomic( legend.position = "right", legend.direction = "vertical", add_point = FALSE, - pt_color = "grey30", + pt_color = if (isTRUE(add_beeswarm)) NULL else "grey30", pt_size = NULL, pt_alpha = 1, y_nbreaks = 4, @@ -43,6 +43,11 @@ BoxViolinPlotAtomic( y_max = NULL, y_min = NULL, y_trans = "identity", + add_beeswarm = FALSE, + beeswarm_method = "swarm", + beeswarm_cex = 1, + beeswarm_priority = "ascending", + beeswarm_dodge = 0.9, add_box = FALSE, box_color = "black", box_width = 0.1, @@ -102,7 +107,7 @@ When \code{in_form} is "wide", \code{x} columns will not be concatenated.} \item{y}{A character string of the column name to plot on the y-axis. A numeric column is expected. When \code{in_form} is "wide", \code{y} is not required. The values under \code{x} columns will be used as y-values.} -\item{base}{A character string to specify the base plot type. Either "box" or "violin".} +\item{base}{A character string to specify the base plot type. Either "box", "violin" or "none" (used by BeeswarmPlot).} \item{in_form}{A character string to specify the input data type. Either "long" or "wide".} @@ -197,6 +202,22 @@ You can also use quantile notation like "q5" to specify the 5th percentile.} \item{y_trans}{A character string to specify the transformation of the y-axis.} +\item{add_beeswarm}{A logical value to add beeswarm points to the plot instead of jittered points. +When TRUE, points are positioned using the beeswarm algorithm to avoid overlap while showing density. +Requires the ggbeeswarm package to be installed.} + +\item{beeswarm_method}{A character string to specify the beeswarm method. Either "swarm", "compactswarm", "hex", +"square", or "center". Default is "swarm". See ggbeeswarm::geom_beeswarm for details.} + +\item{beeswarm_cex}{A numeric value to specify the scaling for adjusting point spacing in beeswarm. +Default is 1. Larger values space out points more.} + +\item{beeswarm_priority}{A character string to specify point layout priority. Either "ascending", "descending", +"density", or "random". Default is "ascending".} + +\item{beeswarm_dodge}{A numeric value to specify the dodge width for beeswarm points when group_by is provided. +Default is 0.9} + \item{add_box}{A logical value to add box plot to the plot.} \item{box_color}{A character string to specify the color of the box plot.} diff --git a/man/boxviolinplot.Rd b/man/boxviolinplot.Rd index 43117db8..102fabd0 100644 --- a/man/boxviolinplot.Rd +++ b/man/boxviolinplot.Rd @@ -3,6 +3,7 @@ \name{BoxPlot} \alias{BoxPlot} \alias{ViolinPlot} +\alias{BeeswarmPlot} \title{Box / Violin Plot} \usage{ BoxPlot( @@ -35,7 +36,7 @@ BoxPlot( legend.position = "right", legend.direction = "vertical", add_point = FALSE, - pt_color = "grey30", + pt_color = if (isTRUE(add_beeswarm)) NULL else "grey30", pt_size = NULL, pt_alpha = 1, jitter_width = NULL, @@ -43,6 +44,11 @@ BoxPlot( stack = FALSE, y_max = NULL, y_min = NULL, + add_beeswarm = FALSE, + beeswarm_method = "swarm", + beeswarm_cex = 1, + beeswarm_priority = "ascending", + beeswarm_dodge = 0.9, add_trend = FALSE, trend_color = NULL, trend_linewidth = 1, @@ -123,7 +129,7 @@ ViolinPlot( legend.position = "right", legend.direction = "vertical", add_point = FALSE, - pt_color = "grey30", + pt_color = if (isTRUE(add_beeswarm)) NULL else "grey30", pt_size = NULL, pt_alpha = 1, jitter_width = NULL, @@ -131,6 +137,107 @@ ViolinPlot( stack = FALSE, y_max = NULL, y_min = NULL, + add_beeswarm = FALSE, + beeswarm_method = "swarm", + beeswarm_cex = 1, + beeswarm_priority = "ascending", + beeswarm_dodge = 0.9, + add_box = FALSE, + box_color = "black", + box_width = 0.1, + box_ptsize = 2.5, + add_trend = FALSE, + trend_color = NULL, + trend_linewidth = 1, + trend_ptsize = 2, + add_stat = NULL, + stat_name = NULL, + stat_color = "black", + stat_size = 1, + stat_stroke = 1, + stat_shape = 25, + add_bg = FALSE, + bg_palette = "stripe", + bg_palcolor = NULL, + bg_alpha = 0.2, + add_line = NULL, + line_color = "red2", + line_width = 0.6, + line_type = 2, + highlight = NULL, + highlight_color = "red2", + highlight_size = 1, + highlight_alpha = 1, + comparisons = NULL, + ref_group = NULL, + pairwise_method = "wilcox.test", + multiplegroup_comparisons = FALSE, + multiple_method = "kruskal.test", + sig_label = "p.format", + sig_labelsize = 3.5, + hide_ns = FALSE, + facet_by = NULL, + facet_scales = "fixed", + facet_ncol = NULL, + facet_nrow = NULL, + facet_byrow = TRUE, + title = NULL, + subtitle = NULL, + xlab = NULL, + ylab = NULL, + seed = 8525, + combine = TRUE, + nrow = NULL, + ncol = NULL, + byrow = TRUE, + axes = NULL, + axis_titles = axes, + guides = NULL, + ... +) + +BeeswarmPlot( + data, + x, + x_sep = "_", + y = NULL, + in_form = c("long", "wide"), + split_by = NULL, + split_by_sep = "_", + symnum_args = NULL, + sort_x = c("none", "mean_asc", "mean_desc", "mean", "median_asc", "median_desc", + "median"), + flip = FALSE, + keep_empty = FALSE, + group_by = NULL, + group_by_sep = "_", + group_name = NULL, + paired_by = NULL, + x_text_angle = ifelse(isTRUE(flip) && isTRUE(stack), 90, 45), + step_increase = 0.1, + fill_mode = ifelse(!is.null(group_by), "dodge", "x"), + fill_reverse = FALSE, + theme = "theme_this", + theme_args = list(), + palette = "Paired", + palcolor = NULL, + alpha = 1, + aspect.ratio = NULL, + legend.position = "right", + legend.direction = "vertical", + pt_color = NULL, + pt_size = NULL, + pt_alpha = 1, + jitter_width = NULL, + jitter_height = 0, + stack = FALSE, + y_max = NULL, + y_min = NULL, + add_violin = FALSE, + beeswarm_method = "swarm", + beeswarm_cex = 1, + beeswarm_priority = "ascending", + beeswarm_dodge = 0.9, add_box = FALSE, box_color = "black", box_width = 0.1, @@ -291,6 +398,22 @@ the significance labels.} \item{y_min}{A numeric value or a character string to specify the minimum value of the y-axis. You can also use quantile notation like "q5" to specify the 5th percentile.} +\item{add_beeswarm}{A logical value to add beeswarm points to the plot instead of jittered points. +When TRUE, points are positioned using the beeswarm algorithm to avoid overlap while showing density. +Requires the ggbeeswarm package to be installed.} + +\item{beeswarm_method}{A character string to specify the beeswarm method. Either "swarm", "compactswarm", "hex", +"square", or "center". Default is "swarm". See ggbeeswarm::geom_beeswarm for details.} + +\item{beeswarm_cex}{A numeric value to specify the scaling for adjusting point spacing in beeswarm. +Default is 1. Larger values space out points more.} + +\item{beeswarm_priority}{A character string to specify point layout priority. Either "ascending", "descending", +"density", or "random". Default is "ascending".} + +\item{beeswarm_dodge}{A numeric value to specify the dodge width for beeswarm points when group_by is provided. +Default is 0.9} + \item{add_trend}{A logical value to add trend line to the plot.} \item{trend_color}{A character string to specify the color of the trend line. @@ -437,6 +560,10 @@ Options are: \item{box_width}{A numeric value to specify the width of the box plot.} \item{box_ptsize}{A numeric value to specify the size of the box plot points in the middle.} + +\item{add_violin}{Logical, whether to add violin plot behind the beeswarm points. +Adding violin to a beeswarm plot is actually not supported. A message will be shown to +remind users to use \code{ViolinPlot(..., add_beeswarm = TRUE)} instead.} } \value{ The Box / Violin plot(s). @@ -458,6 +585,7 @@ data <- data.frame( ) BoxPlot(data, x = "x", y = "y") +BoxPlot(data, x = "x", y = "y", add_beeswarm = TRUE, pt_color = "grey30") BoxPlot(data, x = "x", y = "y", stack = TRUE, flip = TRUE, facet_by = "group1", @@ -504,6 +632,7 @@ BoxPlot( } \donttest{ ViolinPlot(data, x = "x", y = "y") +ViolinPlot(data, x = "x", y = "y", add_beeswarm = TRUE, pt_color = "grey30") ViolinPlot(data, x = "x", y = "y", add_box = TRUE) ViolinPlot(data, x = "x", y = "y", add_point = TRUE) ViolinPlot(data, x = "x", y = "y", add_trend = TRUE) @@ -539,4 +668,21 @@ ViolinPlot(data, bg_palette = "Paired" ) } +\donttest{ +# Beeswarm plot examples +BeeswarmPlot(data, x = "x", y = "y") +BeeswarmPlot(data, x = "x", y = "y", pt_size = 1) +BeeswarmPlot(data, x = "x", y = "y", add_box = TRUE, pt_color = "grey30") +# Equivalent to: +# BoxPlot(data, x = "x", y = "y", add_beeswarm = TRUE, pt_color = "grey30") + +BeeswarmPlot(data, x = "x", y = "y", group_by = "group1") +# no dodging +BeeswarmPlot(data, x = "x", y = "y", group_by = "group1", beeswarm_dodge = NULL) + +BeeswarmPlot(data, + x = "x", y = "y", beeswarm_method = "hex", + beeswarm_cex = 2 +) +} }