From baad851fdf60a507293d2c2d67f9a624956c8817 Mon Sep 17 00:00:00 2001 From: Abel Vertesy Date: Thu, 27 Aug 2026 01:12:12 +0200 Subject: [PATCH 1/5] Make numeric col a palette index in qboxplot, qviolin and qstripchart All three contained the identical no-op line: if (is.numeric(col) & col < length(vars)) col <- col so a numeric `col` was never interpreted at all. It fell through to `fill <- col` and reached ggpubr as a bare number, which ggplot2 reads as a base R colour code - qboxplot(tg, col = 2) produced #DF536B. That matched neither the documented behaviour ("the index or name of the column") nor qbarplot() and qhistogram(), where a number is a palette index. PR #87 previously proposed reading it as a *column* index; that was closed. Per the design discussion, a number is now a *palette* index everywhere, which is also what the Q1 answer specified. Adds one internal resolver, .resolveColToFill(), used by all three functions so the semantics cannot drift apart again: 1. a character matching a column name -> returned unchanged, so ggpubr groups by that column and colours it through palette_use (unchanged behaviour) 2. a single positive whole number, or a digit-only string such as "2" -> looked up in palette_use 3. anything else -> returned unchanged and used as a literal colour (unchanged behaviour) The index detection reuses the hardened predicate from the edit to #97: length 1, non-NA, >= 1, and whole-number numeric or digit-only string. The palette is sized to cover the requested index so pal[i] cannot yield NA. qboxplot needed one extra detail: by the time `col` is resolved, its `palette_use` has already been overwritten with the recycled `fill` colour, so the resolver is given `palette_use_bac` (the palette as requested) instead. Without this, every index resolved to the fill colour ("gold"). Behaviour change, deliberate and documented: col = now yields a palette colour rather than a base R colour code. col = NULL, column names, and literal colours are all unchanged. Verified for all three functions across col = NULL, 1, 2, 5, "2", a column name, and a literal colour, plus a custom palette vector and list input. All three now return identical colours for identical input. --- R/ggExpress.R | 85 +++++++++++++++++++++++++++++++------ man/dot-resolveColToFill.Rd | 34 +++++++++++++++ man/qboxplot.Rd | 6 ++- man/qstripchart.Rd | 6 ++- man/qviolin.Rd | 6 ++- 5 files changed, 122 insertions(+), 15 deletions(-) create mode 100644 man/dot-resolveColToFill.Rd diff --git a/R/ggExpress.R b/R/ggExpress.R index 6c54ca1..e86c058 100644 --- a/R/ggExpress.R +++ b/R/ggExpress.R @@ -1473,7 +1473,11 @@ qscatter <- function( #' @param df_XYcol_or_list Data as a two-column data frame, where column 1 is the X axis, alternatively a uniquely named list of values. #' @param x The index or name of the column to be plotted on the X axis. Default: `1`. #' @param y The index or name of the column to be plotted on the Y axis. Default: `2`. -#' @param col The index or name of the column to be used for coloring the plot. Default: `NULL`. +#' @param col How to color the plot. Either (1) the *name* of a column to color (group) by, +#' colored through `palette_use`; (2) a single palette index - a positive whole number, or a +#' digit-only string such as `"2"` - looked up in `palette_use`; or (3) a literal color such +#' as `"red"` or `"#FF0000"`. A number is always a *palette* index, never a column index, +#' consistent with `qbarplot()` and `qhistogram()`. Default: `NULL`, which uses `fill`. #' @param fill Fill color of the plot. Default: `gold`. #' #' @param plotname The title of the plot and the name of the file (unless specified in `filename`). @@ -1608,9 +1612,10 @@ qboxplot <- function( if (!is.null(col)) { - if (is.numeric(col) & col < length(vars)) col <- col - if (col %in% vars) col <- vars[col] - fill <- col # if col (color as a column name) is provided, fill is set to col + # `col` is either a column name to colour by, a palette index, or a literal colour. + # Resolve against palette_use_bac: by this point palette_use has been overwritten + # with the recycled `fill` colour above, and is no longer the requested palette. + fill <- .resolveColToFill(col, vars, palette_use_bac) palette_use <- palette_use_bac } @@ -1666,7 +1671,11 @@ qboxplot <- function( #' @param df_XYcol_or_list Data as a two-column data frame, where column 1 is the X axis, alternatively a uniquely named list of values. #' @param x The index or name of the column to be plotted on the X axis. Default: `1`. #' @param y The index or name of the column to be plotted on the Y axis. Default: `2`. -#' @param col The index or name of the column to be used for coloring the plot. Default: `NULL`. +#' @param col How to color the plot. Either (1) the *name* of a column to color (group) by, +#' colored through `palette_use`; (2) a single palette index - a positive whole number, or a +#' digit-only string such as `"2"` - looked up in `palette_use`; or (3) a literal color such +#' as `"red"` or `"#FF0000"`. A number is always a *palette* index, never a column index, +#' consistent with `qbarplot()` and `qhistogram()`. Default: `NULL`, which uses `fill`. #' @param fill Fill color of the plot. Default: `gold`. #' @param plotname Name of the plot #' @param subtitle Optional subtitle text added below the title. Default is NULL. @@ -1748,9 +1757,8 @@ qviolin <- function( is.numeric(df_XYcol[[vars[y]]]) ) if (!is.null(col)) { - if (is.numeric(col) & col < length(vars)) col <- col - if (col %in% vars) col <- vars[col] - fill <- col # if col (color as a column name) is provided, fill is set to col + # `col` is either a column name to colour by, a palette index, or a literal colour. + fill <- .resolveColToFill(col, vars, palette_use) } pobj <- ggpubr::ggviolin( @@ -1798,7 +1806,11 @@ qviolin <- function( #' @param df_XYcol_or_list Data as a two-column data frame, where column 1 is the X axis, alternatively a uniquely named list of values. #' @param x The index or name of the column to be plotted on the X axis. Default: `1`. #' @param y The index or name of the column to be plotted on the Y axis. Default: `2`. -#' @param col The index or name of the column to be used for coloring the plot. Default: `NULL`. +#' @param col How to color the plot. Either (1) the *name* of a column to color (group) by, +#' colored through `palette_use`; (2) a single palette index - a positive whole number, or a +#' digit-only string such as `"2"` - looked up in `palette_use`; or (3) a literal color such +#' as `"red"` or `"#FF0000"`. A number is always a *palette* index, never a column index, +#' consistent with `qbarplot()` and `qhistogram()`. Default: `NULL`, which uses `fill`. #' @param fill Fill color of the plot. Default: `gold`. #' #' @param plotname Name of the plot @@ -1906,9 +1918,8 @@ qstripchart <- function( ) if (!is.null(col)) { - if (is.numeric(col) & col < length(vars)) col <- col - if (col %in% vars) col <- vars[col] - fill <- col # if col (color as a column name) is provided, fill is set to col + # `col` is either a column name to colour by, a palette index, or a literal colour. + fill <- .resolveColToFill(col, vars, palette_use) } pobj <- ggpubr::ggstripchart( @@ -2714,6 +2725,56 @@ qqqAnnotateTopLabels <- function(df, x, y, labels = NULL, fun = mean, digits = 2 } +# _________________________________________________________________________________________________ +#' @title Resolve the col argument to a ggpubr fill value +#' +#' @description Shared `col` resolution for the data frame / list based plotting functions +#' (`qboxplot`, `qviolin`, `qstripchart`). `col` may be: +#' (1) the name of a column to color (group) by, which is returned unchanged so ggpubr maps +#' it through `palette_use`; +#' (2) a single palette index - a positive whole number, or a digit-only string such as +#' `"2"` - which is looked up in `palette_use` and returned as a literal color; +#' (3) any other value, returned unchanged and used by ggpubr as a literal color. +#' A numeric `col` is always a *palette* index, never a column index, which keeps these +#' functions consistent with `qbarplot()` and `qhistogram()`. +#' +#' @param col The `col` argument as supplied by the caller. Must not be NULL. +#' @param vars Column names of the data frame being plotted. +#' @param palette_use The palette in use, passed through from the caller. +#' @return A single string: either a column name for ggpubr to map, or a literal color. +#' +#' @examples +#' ggExpress:::.resolveColToFill("dose", c("dose", "len"), "jco") # -> "dose" +#' ggExpress:::.resolveColToFill(2, c("dose", "len"), "jco") # -> 2nd palette color +#' ggExpress:::.resolveColToFill("red", c("dose", "len"), "jco") # -> "red" +.resolveColToFill <- function(col, vars, palette_use) { + # (1) A column name colors by that column; ggpubr maps it through the palette. + if (is.character(col) && length(col) == 1L && col %in% vars) return(col) + + # (2) A single positive whole number (or digit-only string) is a palette index. + col_i <- suppressWarnings(as.integer(col)) + col.is.index <- length(col) == 1L && + !is.na(col_i) && col_i >= 1L && + ( + (is.numeric(col) && col == col_i) || # whole-number numeric + (is.character(col) && grepl("^[0-9]+$", col)) # digit-only string + ) + + if (isTRUE(col.is.index)) { + # Size the palette to cover the requested index; pal[i] yields NA for i > length(pal). + pal <- if (length(palette_use) == 1) { + ggpubr::get_palette(palette_use, max(2L, col_i)) + } else { + palette_use + } + return(pal[col_i]) + } + + # (3) Anything else is used as a literal color. + col +} + + # _________________________________________________________________________________________________ #' @title Summarize Mean and Median per Group diff --git a/man/dot-resolveColToFill.Rd b/man/dot-resolveColToFill.Rd new file mode 100644 index 0000000..6e3ea48 --- /dev/null +++ b/man/dot-resolveColToFill.Rd @@ -0,0 +1,34 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ggExpress.R +\name{.resolveColToFill} +\alias{.resolveColToFill} +\title{Resolve the col argument to a ggpubr fill value} +\usage{ +.resolveColToFill(col, vars, palette_use) +} +\arguments{ +\item{col}{The \code{col} argument as supplied by the caller. Must not be NULL.} + +\item{vars}{Column names of the data frame being plotted.} + +\item{palette_use}{The palette in use, passed through from the caller.} +} +\value{ +A single string: either a column name for ggpubr to map, or a literal color. +} +\description{ +Shared \code{col} resolution for the data frame / list based plotting functions +(\code{qboxplot}, \code{qviolin}, \code{qstripchart}). \code{col} may be: +(1) the name of a column to color (group) by, which is returned unchanged so ggpubr maps +it through \code{palette_use}; +(2) a single palette index - a positive whole number, or a digit-only string such as +\code{"2"} - which is looked up in \code{palette_use} and returned as a literal color; +(3) any other value, returned unchanged and used by ggpubr as a literal color. +A numeric \code{col} is always a \emph{palette} index, never a column index, which keeps these +functions consistent with \code{qbarplot()} and \code{qhistogram()}. +} +\examples{ +ggExpress:::.resolveColToFill("dose", c("dose", "len"), "jco") # -> "dose" +ggExpress:::.resolveColToFill(2, c("dose", "len"), "jco") # -> 2nd palette color +ggExpress:::.resolveColToFill("red", c("dose", "len"), "jco") # -> "red" +} diff --git a/man/qboxplot.Rd b/man/qboxplot.Rd index bada67a..85d6d8b 100644 --- a/man/qboxplot.Rd +++ b/man/qboxplot.Rd @@ -70,7 +70,11 @@ qboxplot( \item{save.obj}{Save the ggplot object to a file. Default: FALSE.} -\item{col}{The index or name of the column to be used for coloring the plot. Default: \code{NULL}.} +\item{col}{How to color the plot. Either (1) the \emph{name} of a column to color (group) by, +colored through \code{palette_use}; (2) a single palette index - a positive whole number, or a +digit-only string such as \code{"2"} - looked up in \code{palette_use}; or (3) a literal color such +as \code{"red"} or \code{"#FF0000"}. A number is always a \emph{palette} index, never a column index, +consistent with \code{qbarplot()} and \code{qhistogram()}. Default: \code{NULL}, which uses \code{fill}.} \item{fill}{Fill color of the plot. Default: \code{gold}.} diff --git a/man/qstripchart.Rd b/man/qstripchart.Rd index d7be69e..b38ee08 100644 --- a/man/qstripchart.Rd +++ b/man/qstripchart.Rd @@ -53,7 +53,11 @@ qstripchart( \item{y}{The index or name of the column to be plotted on the Y axis. Default: \code{2}.} -\item{col}{The index or name of the column to be used for coloring the plot. Default: \code{NULL}.} +\item{col}{How to color the plot. Either (1) the \emph{name} of a column to color (group) by, +colored through \code{palette_use}; (2) a single palette index - a positive whole number, or a +digit-only string such as \code{"2"} - looked up in \code{palette_use}; or (3) a literal color such +as \code{"red"} or \code{"#FF0000"}. A number is always a \emph{palette} index, never a column index, +consistent with \code{qbarplot()} and \code{qhistogram()}. Default: \code{NULL}, which uses \code{fill}.} \item{plotname}{Name of the plot} diff --git a/man/qviolin.Rd b/man/qviolin.Rd index 34cbfec..6573c3b 100644 --- a/man/qviolin.Rd +++ b/man/qviolin.Rd @@ -47,7 +47,11 @@ qviolin( \item{y}{The index or name of the column to be plotted on the Y axis. Default: \code{2}.} -\item{col}{The index or name of the column to be used for coloring the plot. Default: \code{NULL}.} +\item{col}{How to color the plot. Either (1) the \emph{name} of a column to color (group) by, +colored through \code{palette_use}; (2) a single palette index - a positive whole number, or a +digit-only string such as \code{"2"} - looked up in \code{palette_use}; or (3) a literal color such +as \code{"red"} or \code{"#FF0000"}. A number is always a \emph{palette} index, never a column index, +consistent with \code{qbarplot()} and \code{qhistogram()}. Default: \code{NULL}, which uses \code{fill}.} \item{plotname}{Name of the plot} From c9899cd7d3cb67e9e2f88be404723d1f3072f7ef Mon Sep 17 00:00:00 2001 From: Abel Vertesy Date: Thu, 27 Aug 2026 01:17:37 +0200 Subject: [PATCH 2/5] Document qscatter's col semantics and remove the misleading c(NULL, 3)[1] default No behaviour change - documentation and clarity only. The default was written as `col = c(NULL, 3)[1]`, which reads as "NULL or 3, take the first" but always evaluates to 3, because c() silently drops NULL. Compare qqSave's `plot_on_page = list(FALSE, "A4p", ...)[[1]]`, where the list() form does correctly preserve the alternatives. Replaced with a plain `col = 3` plus a comment recording what the old idiom actually did, so nobody re-derives NULL as a supported option. The roxygen said only "Color of the plot", which hid three things that cost me time during the color audit: - `col` is passed straight to ggpubr::ggscatter(color = ), with no interpretation by ggExpress at all; - a number here is therefore a base R colour code (the default 3 is green), NOT an index into palette_use as in qbarplot() and qhistogram(); - colouring by a *numeric* column fails outright ("Continuous value supplied to a discrete scale"), because palette_use is a discrete scale; only discrete columns work. Deliberately NOT changed: the numeric semantics. Making a number a palette index here would be consistent with the rest of the package, but 3 is also the default, so it would change the point colour of every existing qscatter call from green to palette colour 3. That needs a decision rather than a silent change. Verified unchanged: default -> colour 3, col = 2 -> colour 2, col = "red" -> red. --- R/ggExpress.R | 13 +++++++++++-- man/qscatter.Rd | 9 +++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/R/ggExpress.R b/R/ggExpress.R index 6c54ca1..edbde77 100644 --- a/R/ggExpress.R +++ b/R/ggExpress.R @@ -1293,7 +1293,12 @@ qbarplot.df <- function( #' @param label Point labels. Default: NULL. #' @param repel Repel labels from each other. Default: TRUE. #' @param legend.position Character indicating the position of the legend. Default is is 'none' to hide the legend. -#' @param col Color of the plot. +#' @param col Color of the points, passed directly to `ggpubr::ggscatter(color = )`. +#' Either a literal color (`"red"`, `"#FF0000"`), or the name of a *discrete* column to +#' color by, or a number. Note that a number here is a base R color code (the default `3` +#' is green), NOT an index into `palette_use` as in `qbarplot()` and `qhistogram()`. +#' Coloring by a numeric column fails, because `palette_use` is a discrete scale. +#' Default: 3. #' @param palette_use Color palette to use. Either a `ggpubr::get_palette` palette or a custom vector of colors. Default: 'jco'. #' #' @param xlab X-axis label. Default: NULL. @@ -1343,7 +1348,11 @@ qscatter <- function( save.obj = getOption("gg.save.obj", FALSE), label = NULL, repel = TRUE, legend.position = "none", - col = c(NULL, 3)[1], + ## NOTE: `col` is passed straight to ggpubr::ggscatter(color = ), so a number here is a + ## base R colour code (3 = green), NOT a palette_use index as in qbarplot/qhistogram. + ## Previously written as c(NULL, 3)[1], which always evaluates to 3 because c() drops + ## NULL - it only looked as though NULL were a selectable alternative. + col = 3, # fill = NULL, palette_use = getOption("gg.palette_use", "jco"), xlab = NULL, ylab = NULL, diff --git a/man/qscatter.Rd b/man/qscatter.Rd index 87a869f..602c5c5 100644 --- a/man/qscatter.Rd +++ b/man/qscatter.Rd @@ -21,7 +21,7 @@ qscatter( label = NULL, repel = TRUE, legend.position = "none", - col = c(NULL, 3)[1], + col = 3, palette_use = getOption("gg.palette_use", "jco"), xlab = NULL, ylab = NULL, @@ -81,7 +81,12 @@ qscatter( \item{legend.position}{Character indicating the position of the legend. Default is is 'none' to hide the legend.} -\item{col}{Color of the plot.} +\item{col}{Color of the points, passed directly to \code{ggpubr::ggscatter(color = )}. +Either a literal color (\code{"red"}, \code{"#FF0000"}), or the name of a \emph{discrete} column to +color by, or a number. Note that a number here is a base R color code (the default \code{3} +is green), NOT an index into \code{palette_use} as in \code{qbarplot()} and \code{qhistogram()}. +Coloring by a numeric column fails, because \code{palette_use} is a discrete scale. +Default: 3.} \item{palette_use}{Color palette to use. Either a \code{ggpubr::get_palette} palette or a custom vector of colors. Default: 'jco'.} From a08bb4462004c7bb62e506cde055203551fd5830 Mon Sep 17 00:00:00 2001 From: Abel Vertesy Date: Sun, 30 Aug 2026 18:37:20 +0200 Subject: [PATCH 3/5] Fix undefined hA4/wA4 constants in 2 grid-plot functions q32vA4_grid_plot() and qA4_grid_plot() defaulted h/w to hA4*scale and wA4*scale, but hA4/wA4 were never defined anywhere in the package, erroring on any default call. Added them as real parameters (11.69 x 8.27 inches, matching the A4 dimensions used elsewhere in this file). Also fixed the h/w roxygen descriptions, which had hA4/wA4 swapped. Co-Authored-By: Claude Sonnet 5 --- R/ggExpress.R | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/R/ggExpress.R b/R/ggExpress.R index 6c54ca1..0decc01 100644 --- a/R/ggExpress.R +++ b/R/ggExpress.R @@ -2464,8 +2464,10 @@ qmosaic <- function( #' @param nrow number of rows for panels on the page. Default: 2 #' @param ncol number of columns for panels on the page. Default: 2 #' @param scale Scaling factor of the canvas. Default: 1 -#' @param h Height of the plot. Default: wA4 * scale -#' @param w Width of the plot. Default: hA4 * scale +#' @param hA4 A4 page height, in inches. Default: 11.69 +#' @param wA4 A4 page width, in inches. Default: 8.27 +#' @param h Height of the plot. Default: hA4 * scale +#' @param w Width of the plot. Default: wA4 * scale #' @param ... Pass any other parameter to the internally called functions (most of them should work). #' @param extension file extension #' @importFrom cowplot plot_grid save_plot @@ -2479,6 +2481,7 @@ q32vA4_grid_plot <- function( plot = FALSE, nrow = 3, ncol = 2, extension = c("pdf", "png")[2], scale = 1, + hA4 = 11.69, wA4 = 8.27, h = hA4 * scale, w = wA4 * scale, ... ) { # Save 4 umaps on an A4 page. @@ -2508,8 +2511,10 @@ q32vA4_grid_plot <- function( #' @param max.list.length Max number of panels (per page). Default: 16 #' @param extension file extension #' @param scale Scaling factor of the canvas. Default: 1 -#' @param h Height of the plot. Default: wA4 * scale -#' @param w Width of the plot. Default: hA4 * scale +#' @param hA4 A4 page height, in inches. Default: 11.69 +#' @param wA4 A4 page width, in inches. Default: 8.27 +#' @param h Height of the plot. Default: hA4 * scale +#' @param w Width of the plot. Default: wA4 * scale #' @param ... Pass any other parameter to the internally called functions (most of them should work). #' #' @importFrom cowplot plot_grid save_plot @@ -2526,6 +2531,7 @@ qA4_grid_plot <- function( max.list.length = 16, extension = c("pdf", "png")[2], scale = 1, + hA4 = 11.69, wA4 = 8.27, h = hA4 * scale, w = wA4 * scale, ... ) { # Save 4 umaps on an A4 page. From 5a35d35fb097ee4f44e003961da445d59114978a Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Mon, 31 Aug 2026 17:10:18 +0000 Subject: [PATCH 4/5] Use get0() fallback for hA4/wA4 instead of new parameters Per feedback: hA4/wA4 are meant to be optional background variables a caller can predefine (e.g. in their session) to change plot defaults package-wide. Adding them as new function parameters broke positional argument order and didn't support that workflow. Use get0("hA4", ifnotfound = 11.69) / get0("wA4", ifnotfound = 8.27) directly in the h/w defaults instead, in q32vA4_grid_plot() and qA4_grid_plot(). Regenerate the corresponding .Rd files and bump the package version per AGENTS.md. Co-authored-by: Abel Vertesy <5101911+vertesy@users.noreply.github.com> --- DESCRIPTION | 2 +- Development/config.R | 2 +- R/ggExpress.R | 18 ++++++------------ man/q32vA4_grid_plot.Rd | 8 ++++---- man/qA4_grid_plot.Rd | 8 ++++---- 5 files changed, 16 insertions(+), 22 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index a1ab7c0..7713d2e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: ggExpress Title: ggExpress is the fastest way to create, annotate and export plots in R -Version: 1.0.0 +Version: 1.0.1 Authors@R: person("Abel", "Vertesy", , "av@imba.oeaw.ac.at", role = c("aut", "cre")) Description: ggExpress is a set of R functions that allows you to generate diff --git a/Development/config.R b/Development/config.R index 74671bb..3229607 100644 --- a/Development/config.R +++ b/Development/config.R @@ -3,7 +3,7 @@ DESCRIPTION <- list( package.name = "ggExpress", - version = "1.0.0", + version = "1.0.1", title = "ggExpress is the fastest way to create, annotate and export plots in R", description = "ggExpress is a set of R functions that allows you to generate precise figures easily, and create clean markdown reports about what you just discovered with your analysis script. It is ggplot-based smaller brother of vertesy/MarkdownReports. It helps you to: diff --git a/R/ggExpress.R b/R/ggExpress.R index 0decc01..54f0221 100644 --- a/R/ggExpress.R +++ b/R/ggExpress.R @@ -2464,10 +2464,8 @@ qmosaic <- function( #' @param nrow number of rows for panels on the page. Default: 2 #' @param ncol number of columns for panels on the page. Default: 2 #' @param scale Scaling factor of the canvas. Default: 1 -#' @param hA4 A4 page height, in inches. Default: 11.69 -#' @param wA4 A4 page width, in inches. Default: 8.27 -#' @param h Height of the plot. Default: hA4 * scale -#' @param w Width of the plot. Default: wA4 * scale +#' @param h Height of the plot, in inches. Default: get0("hA4", ifnotfound = 11.69) * scale +#' @param w Width of the plot, in inches. Default: get0("wA4", ifnotfound = 8.27) * scale #' @param ... Pass any other parameter to the internally called functions (most of them should work). #' @param extension file extension #' @importFrom cowplot plot_grid save_plot @@ -2481,8 +2479,7 @@ q32vA4_grid_plot <- function( plot = FALSE, nrow = 3, ncol = 2, extension = c("pdf", "png")[2], scale = 1, - hA4 = 11.69, wA4 = 8.27, - h = hA4 * scale, w = wA4 * scale, + h = get0("hA4", ifnotfound = 11.69) * scale, w = get0("wA4", ifnotfound = 8.27) * scale, ... ) { # Save 4 umaps on an A4 page. print("Plot panels on 3-by-2 vertical A4 page.") @@ -2511,10 +2508,8 @@ q32vA4_grid_plot <- function( #' @param max.list.length Max number of panels (per page). Default: 16 #' @param extension file extension #' @param scale Scaling factor of the canvas. Default: 1 -#' @param hA4 A4 page height, in inches. Default: 11.69 -#' @param wA4 A4 page width, in inches. Default: 8.27 -#' @param h Height of the plot. Default: hA4 * scale -#' @param w Width of the plot. Default: wA4 * scale +#' @param h Height of the plot, in inches. Default: get0("hA4", ifnotfound = 11.69) * scale +#' @param w Width of the plot, in inches. Default: get0("wA4", ifnotfound = 8.27) * scale #' @param ... Pass any other parameter to the internally called functions (most of them should work). #' #' @importFrom cowplot plot_grid save_plot @@ -2531,8 +2526,7 @@ qA4_grid_plot <- function( max.list.length = 16, extension = c("pdf", "png")[2], scale = 1, - hA4 = 11.69, wA4 = 8.27, - h = hA4 * scale, w = wA4 * scale, + h = get0("hA4", ifnotfound = 11.69) * scale, w = get0("wA4", ifnotfound = 8.27) * scale, ... ) { # Save 4 umaps on an A4 page. stopifnot(length(plot_list) < max.list.length) diff --git a/man/q32vA4_grid_plot.Rd b/man/q32vA4_grid_plot.Rd index cfed473..6ef90bf 100644 --- a/man/q32vA4_grid_plot.Rd +++ b/man/q32vA4_grid_plot.Rd @@ -13,8 +13,8 @@ q32vA4_grid_plot( ncol = 2, extension = c("pdf", "png")[2], scale = 1, - h = hA4 * scale, - w = wA4 * scale, + h = get0("hA4", ifnotfound = 11.69) * scale, + w = get0("wA4", ifnotfound = 8.27) * scale, ... ) } @@ -35,9 +35,9 @@ q32vA4_grid_plot( \item{scale}{Scaling factor of the canvas. Default: 1} -\item{h}{Height of the plot. Default: wA4 * scale} +\item{h}{Height of the plot, in inches. Default: get0("hA4", ifnotfound = 11.69) * scale} -\item{w}{Width of the plot. Default: hA4 * scale} +\item{w}{Width of the plot, in inches. Default: get0("wA4", ifnotfound = 8.27) * scale} \item{...}{Pass any other parameter to the internally called functions (most of them should work).} } diff --git a/man/qA4_grid_plot.Rd b/man/qA4_grid_plot.Rd index 6bfa013..2d54baa 100644 --- a/man/qA4_grid_plot.Rd +++ b/man/qA4_grid_plot.Rd @@ -15,8 +15,8 @@ qA4_grid_plot( max.list.length = 16, extension = c("pdf", "png")[2], scale = 1, - h = hA4 * scale, - w = wA4 * scale, + h = get0("hA4", ifnotfound = 11.69) * scale, + w = get0("wA4", ifnotfound = 8.27) * scale, ... ) } @@ -41,9 +41,9 @@ qA4_grid_plot( \item{scale}{Scaling factor of the canvas. Default: 1} -\item{h}{Height of the plot. Default: wA4 * scale} +\item{h}{Height of the plot, in inches. Default: get0("hA4", ifnotfound = 11.69) * scale} -\item{w}{Width of the plot. Default: hA4 * scale} +\item{w}{Width of the plot, in inches. Default: get0("wA4", ifnotfound = 8.27) * scale} \item{...}{Pass any other parameter to the internally called functions (most of them should work).} } From 3ba9d246cfa9ec611717a8e46f8234c93b611913 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Mon, 31 Aug 2026 20:40:13 +0000 Subject: [PATCH 5/5] Reject out-of-range custom-palette indexes in .resolveColToFill() Previously a col index beyond the length of a custom palette_use vector (e.g. qviolin(data, col = 3, palette_use = c("red","blue"))) silently resolved to NA. Named palettes are still auto-regenerated to cover the requested index, but custom vectors cannot be extended, so an out-of-range index now throws a clear error instead. Co-authored-by: Abel Vertesy <5101911+vertesy@users.noreply.github.com> --- R/ggExpress.R | 21 +++++++++++++++++---- man/dot-resolveColToFill.Rd | 3 +++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/R/ggExpress.R b/R/ggExpress.R index 7422638..9bd335d 100644 --- a/R/ggExpress.R +++ b/R/ggExpress.R @@ -2746,6 +2746,9 @@ qqqAnnotateTopLabels <- function(df, x, y, labels = NULL, fun = mean, digits = 2 #' (3) any other value, returned unchanged and used by ggpubr as a literal color. #' A numeric `col` is always a *palette* index, never a column index, which keeps these #' functions consistent with `qbarplot()` and `qhistogram()`. +#' A named palette (e.g. `"jco"`) is regenerated to cover any requested index; a custom +#' color vector cannot be extended, so an out-of-range index throws an informative error +#' instead of silently resolving to `NA`. #' #' @param col The `col` argument as supplied by the caller. Must not be NULL. #' @param vars Column names of the data frame being plotted. @@ -2757,6 +2760,12 @@ qqqAnnotateTopLabels <- function(df, x, y, labels = NULL, fun = mean, digits = 2 #' ggExpress:::.resolveColToFill(2, c("dose", "len"), "jco") # -> 2nd palette color #' ggExpress:::.resolveColToFill("red", c("dose", "len"), "jco") # -> "red" .resolveColToFill <- function(col, vars, palette_use) { + stopifnot( + "`col` must not be NULL." = !is.null(col), + "`vars` must be a character vector." = is.character(vars), + "`palette_use` must not be NULL." = !is.null(palette_use) + ) + # (1) A column name colors by that column; ggpubr maps it through the palette. if (is.character(col) && length(col) == 1L && col %in% vars) return(col) @@ -2770,11 +2779,15 @@ qqqAnnotateTopLabels <- function(df, x, y, labels = NULL, fun = mean, digits = 2 ) if (isTRUE(col.is.index)) { - # Size the palette to cover the requested index; pal[i] yields NA for i > length(pal). - pal <- if (length(palette_use) == 1) { - ggpubr::get_palette(palette_use, max(2L, col_i)) + if (length(palette_use) == 1) { + # Named palette (e.g. "jco"): regenerate it to cover the requested index. + pal <- ggpubr::get_palette(palette_use, max(2L, col_i)) } else { - palette_use + # Custom color vector: it cannot be extended, so the index must fall within it. + stopifnot( + "`col` index exceeds the number of colors in the custom `palette_use`." = col_i <= length(palette_use) + ) + pal <- palette_use } return(pal[col_i]) } diff --git a/man/dot-resolveColToFill.Rd b/man/dot-resolveColToFill.Rd index 6e3ea48..9514276 100644 --- a/man/dot-resolveColToFill.Rd +++ b/man/dot-resolveColToFill.Rd @@ -26,6 +26,9 @@ it through \code{palette_use}; (3) any other value, returned unchanged and used by ggpubr as a literal color. A numeric \code{col} is always a \emph{palette} index, never a column index, which keeps these functions consistent with \code{qbarplot()} and \code{qhistogram()}. +A named palette (e.g. \code{"jco"}) is regenerated to cover any requested index; a custom +color vector cannot be extended, so an out-of-range index throws an informative error +instead of silently resolving to \code{NA}. } \examples{ ggExpress:::.resolveColToFill("dose", c("dose", "len"), "jco") # -> "dose"