From 8dad6f4958acccb7abc017134cfd3e52be8bcee9 Mon Sep 17 00:00:00 2001 From: Giovanni M Dall'Olio Date: Tue, 22 Apr 2025 20:46:18 +0100 Subject: [PATCH 01/12] Added support to ... in plot_histogram --- DESCRIPTION | 2 +- NAMESPACE | 1 + R/plot_histogram.r | 45 ++++++++++++++++++++++++++----------------- man/plot_histogram.Rd | 3 ++- 4 files changed, 31 insertions(+), 20 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index f5ce8a2..f009e4f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -31,6 +31,6 @@ License: MIT + file LICENSE Language: en-US URL: http://boxuancui.github.io/DataExplorer/ BugReports: https://github.com/boxuancui/DataExplorer/issues -RoxygenNote: 7.3.0 +RoxygenNote: 7.3.2 Encoding: UTF-8 VignetteBuilder: knitr diff --git a/NAMESPACE b/NAMESPACE index d5834be..dbd76c1 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -28,6 +28,7 @@ export(update_columns) import(data.table) import(ggplot2) import(gridExtra) +import(rlang) import(rmarkdown) importFrom(networkD3,diagonalNetwork) importFrom(networkD3,radialNetwork) diff --git a/R/plot_histogram.r b/R/plot_histogram.r index ed64a3c..a969505 100644 --- a/R/plot_histogram.r +++ b/R/plot_histogram.r @@ -11,21 +11,16 @@ #' @param nrow number of rows per page. Default is 4. #' @param ncol number of columns per page. Default is 4. #' @param parallel enable parallel? Default is \code{FALSE}. +#' @param ... aesthetic mappings passed to \link{aes}, such as \code{fill = group} #' @return invisibly return the named list of ggplot objects #' @keywords plot_histogram #' @import data.table #' @import ggplot2 +#' @importFrom rlang enquos eval_tidy expr #' @export #' @seealso \link{geom_histogram} \link{plot_density} #' @examples -#' # Plot iris data -#' plot_histogram(iris, ncol = 2L) -#' -#' # Plot skewed data on log scale -#' set.seed(1) -#' skew <- data.frame(replicate(4L, rbeta(1000, 1, 5000))) -#' plot_histogram(skew, ncol = 2L) -#' plot_histogram(skew, scale_x = "log10", ncol = 2L) +#' plot_histogram(iris, fill = Species, ncol = 2L) plot_histogram <- function(data, binary_as_factor = TRUE, geom_histogram_args = list("bins" = 30L), @@ -33,32 +28,46 @@ plot_histogram <- function(data, binary_as_factor = TRUE, title = NULL, ggtheme = theme_gray(), theme_config = list(), nrow = 4L, ncol = 4L, - parallel = FALSE) { - ## Declare variable first to pass R CMD check + parallel = FALSE, + ...) { variable <- value <- NULL - ## Check if input is data.table + if (!is.data.table(data)) data <- data.table(data) - ## Stop if no continuous features + split_data <- split_columns(data, binary_as_factor = binary_as_factor) if (split_data$num_continuous == 0) stop("No continuous features found!") - ## Get and reshape continuous features + continuous <- split_data$continuous feature_names <- names(continuous) dt <- suppressWarnings(melt.data.table(continuous, measure.vars = feature_names, variable.factor = FALSE)) - ## Calculate number of pages + + # Replicate any non-measured columns from data into dt + other_vars <- setdiff(names(data), names(dt)) + if (length(other_vars) > 0) { + dt <- cbind( + dt, + data[rep(seq_len(nrow(data)), times = length(feature_names)), ..other_vars] + ) + } + + # Capture aesthetic mappings + aesthetic_quos <- enquos(...) + aes_combined <- eval_tidy(expr(aes(x = value, !!!aesthetic_quos))) + layout <- .getPageLayout(nrow, ncol, ncol(continuous)) - ## Create ggplot object + plot_list <- .lapply( parallel = parallel, X = layout, FUN = function(x) { - ggplot(dt[variable %in% feature_names[x]], aes(x = value)) + - do.call("geom_histogram", c("na.rm" = TRUE, geom_histogram_args)) + + p <- ggplot(dt[variable %in% feature_names[x]], mapping = aes_combined) + + do.call("geom_histogram", c(list(na.rm = TRUE), geom_histogram_args)) + do.call(paste0("scale_x_", scale_x), list()) + ylab("Frequency") + p } ) - ## Plot objects + class(plot_list) <- c("multiple", class(plot_list)) plotDataExplorer( plot_obj = plot_list, diff --git a/man/plot_histogram.Rd b/man/plot_histogram.Rd index 36cfc06..afc9947 100644 --- a/man/plot_histogram.Rd +++ b/man/plot_histogram.Rd @@ -14,7 +14,8 @@ plot_histogram( theme_config = list(), nrow = 4L, ncol = 4L, - parallel = FALSE + parallel = FALSE, + ... ) } \arguments{ From 8838827f6b9a77578c7072d4e82486f21f7ef9b1 Mon Sep 17 00:00:00 2001 From: Giovanni M Dall'Olio Date: Tue, 22 Apr 2025 20:54:09 +0100 Subject: [PATCH 02/12] Supported mapped and constant aes --- NAMESPACE | 4 +++- R/plot_histogram.r | 33 ++++++++++++++++++--------------- man/plot_histogram.Rd | 11 +++-------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index dbd76c1..7679a33 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -28,12 +28,14 @@ export(update_columns) import(data.table) import(ggplot2) import(gridExtra) -import(rlang) import(rmarkdown) importFrom(networkD3,diagonalNetwork) importFrom(networkD3,radialNetwork) importFrom(parallel,detectCores) importFrom(parallel,mclapply) +importFrom(rlang,enquos) +importFrom(rlang,eval_tidy) +importFrom(rlang,expr) importFrom(scales,comma) importFrom(scales,percent) importFrom(stats,complete.cases) diff --git a/R/plot_histogram.r b/R/plot_histogram.r index a969505..9d46d64 100644 --- a/R/plot_histogram.r +++ b/R/plot_histogram.r @@ -11,16 +11,16 @@ #' @param nrow number of rows per page. Default is 4. #' @param ncol number of columns per page. Default is 4. #' @param parallel enable parallel? Default is \code{FALSE}. -#' @param ... aesthetic mappings passed to \link{aes}, such as \code{fill = group} +#' @param ... aesthetic mappings passed to \link{aes}, such as \code{fill = group}, or constants like \code{alpha = 0.5} #' @return invisibly return the named list of ggplot objects #' @keywords plot_histogram #' @import data.table #' @import ggplot2 -#' @importFrom rlang enquos eval_tidy expr +#' @importFrom rlang enquos eval_tidy expr quo_is_symbolic #' @export #' @seealso \link{geom_histogram} \link{plot_density} #' @examples -#' plot_histogram(iris, fill = Species, ncol = 2L) +#' plot_histogram(iris, fill = Species, alpha = 0.5, ncol = 2L) plot_histogram <- function(data, binary_as_factor = TRUE, geom_histogram_args = list("bins" = 30L), @@ -31,17 +31,13 @@ plot_histogram <- function(data, binary_as_factor = TRUE, parallel = FALSE, ...) { variable <- value <- NULL - if (!is.data.table(data)) data <- data.table(data) - split_data <- split_columns(data, binary_as_factor = binary_as_factor) if (split_data$num_continuous == 0) stop("No continuous features found!") - continuous <- split_data$continuous feature_names <- names(continuous) dt <- suppressWarnings(melt.data.table(continuous, measure.vars = feature_names, variable.factor = FALSE)) - - # Replicate any non-measured columns from data into dt + # Copy over non-measured columns (e.g., fill/grouping variables) other_vars <- setdiff(names(data), names(dt)) if (length(other_vars) > 0) { dt <- cbind( @@ -49,21 +45,28 @@ plot_histogram <- function(data, binary_as_factor = TRUE, data[rep(seq_len(nrow(data)), times = length(feature_names)), ..other_vars] ) } - - # Capture aesthetic mappings - aesthetic_quos <- enquos(...) - aes_combined <- eval_tidy(expr(aes(x = value, !!!aesthetic_quos))) - + # Separate mapped vs constant aesthetics from ... + dots <- enquos(...) + mapped_aes <- dots[sapply(dots, rlang::quo_is_symbolic)] + constant_aes <- dots[!sapply(dots, rlang::quo_is_symbolic)] + # Combine mapped aesthetics with x = value + aes_combined <- eval_tidy(expr(aes(x = value, !!!mapped_aes))) layout <- .getPageLayout(nrow, ncol, ncol(continuous)) - plot_list <- .lapply( parallel = parallel, X = layout, FUN = function(x) { + layer_args <- c( + list(na.rm = TRUE), + geom_histogram_args, + lapply(constant_aes, eval_tidy) + ) + p <- ggplot(dt[variable %in% feature_names[x]], mapping = aes_combined) + - do.call("geom_histogram", c(list(na.rm = TRUE), geom_histogram_args)) + + do.call("geom_histogram", layer_args) + do.call(paste0("scale_x_", scale_x), list()) + ylab("Frequency") + p } ) diff --git a/man/plot_histogram.Rd b/man/plot_histogram.Rd index afc9947..afbad47 100644 --- a/man/plot_histogram.Rd +++ b/man/plot_histogram.Rd @@ -38,6 +38,8 @@ plot_histogram( \item{ncol}{number of columns per page. Default is 4.} \item{parallel}{enable parallel? Default is \code{FALSE}.} + +\item{...}{aesthetic mappings passed to \link{aes}, such as \code{fill = group}} } \value{ invisibly return the named list of ggplot objects @@ -46,14 +48,7 @@ invisibly return the named list of ggplot objects Plot histogram for each continuous feature } \examples{ -# Plot iris data -plot_histogram(iris, ncol = 2L) - -# Plot skewed data on log scale -set.seed(1) -skew <- data.frame(replicate(4L, rbeta(1000, 1, 5000))) -plot_histogram(skew, ncol = 2L) -plot_histogram(skew, scale_x = "log10", ncol = 2L) +plot_histogram(iris, fill = Species, ncol = 2L) } \seealso{ \link{geom_histogram} \link{plot_density} From b098732bad74e27268fee713f55011576a2f9aea Mon Sep 17 00:00:00 2001 From: Giovanni M Dall'Olio Date: Tue, 22 Apr 2025 21:08:32 +0100 Subject: [PATCH 03/12] added tests --- NAMESPACE | 1 + R/plot_bar.r | 76 ++++++++++++++++++++++--------------------- R/plot_histogram.r | 30 ++++++++++------- man/plot_bar.Rd | 22 +++---------- man/plot_histogram.Rd | 4 +-- 5 files changed, 65 insertions(+), 68 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 7679a33..e196400 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -36,6 +36,7 @@ importFrom(parallel,mclapply) importFrom(rlang,enquos) importFrom(rlang,eval_tidy) importFrom(rlang,expr) +importFrom(rlang,quo_is_symbolic) importFrom(scales,comma) importFrom(scales,percent) importFrom(stats,complete.cases) diff --git a/R/plot_bar.r b/R/plot_bar.r index 672cee2..4b71870 100644 --- a/R/plot_bar.r +++ b/R/plot_bar.r @@ -14,26 +14,14 @@ #' @param nrow number of rows per page. Default is 3. #' @param ncol number of columns per page. Default is 3. #' @param parallel enable parallel? Default is \code{FALSE}. +#' @param ... aesthetic mappings (e.g., fill = Species, alpha = 0.5) #' @return invisibly return the named list of ggplot objects -#' @keywords plot_bar -#' @details If a discrete feature contains more categories than \code{maxcat} specifies, it will not be passed to the plotting function. #' @import data.table #' @import ggplot2 +#' @importFrom rlang enquos quo_is_symbolic eval_tidy expr #' @importFrom stats reorder #' @importFrom tools toTitleCase #' @export -#' @examples -#' # Plot bar charts for diamonds dataset -#' library(ggplot2) -#' plot_bar(diamonds) -#' plot_bar(diamonds, maxcat = 5) -#' -#' # Plot bar charts with `price` -#' plot_bar(diamonds, with = "price") -#' -#' # Plot bar charts by `cut` -#' plot_bar(diamonds, by = "cut") -#' plot_bar(diamonds, by = "cut", by_position = "dodge") plot_bar <- function(data, with = NULL, by = NULL, by_position = "fill", @@ -41,24 +29,27 @@ plot_bar <- function(data, with = NULL, title = NULL, ggtheme = theme_gray(), theme_config = list(), nrow = 3L, ncol = 3L, - parallel = FALSE) { - ## Declare variable first to pass R CMD check + parallel = FALSE, + ...) { + ## Declare vars to avoid CMD check warnings frequency <- measure <- variable <- value <- facet_value <- NULL - ## Check if input is data.table + if (!is.data.table(data)) data <- data.table(data) - ## Stop if no discrete features + split_data <- split_columns(data, binary_as_factor = binary_as_factor) if (split_data$num_discrete == 0) stop("No discrete features found!") - ## Get discrete features + discrete <- split_data$discrete - ## Drop features with categories greater than `maxcat` + + ## Drop high-cardinality columns ind <- .ignoreCat(discrete, maxcat = maxcat) if (length(ind)) { message(length(ind), " columns ignored with more than ", maxcat, " categories.\n", paste0(names(ind), ": ", ind, " categories\n")) drop_columns(discrete, names(ind)) if (length(discrete) == 0) stop("Note: All discrete features ignored! Nothing to plot!") } - ## Aggregate feature categories + + ## Aggregate values feature_names <- names(discrete) if (is.null(with)) { dt <- discrete[, list(frequency = .N), by = feature_names] @@ -75,6 +66,8 @@ plot_bar <- function(data, with = NULL, tmp_dt <- data.table(discrete, "measure" = measure_var) dt <- tmp_dt[, list(frequency = sum(measure, na.rm = TRUE)), by = feature_names] } + + ## Reshape for plotting if (is.null(by)) { dt_tmp <- suppressWarnings(melt.data.table(dt, measure.vars = feature_names)) dt2 <- dt_tmp[, list(frequency = sum(frequency)), by = list(variable, value)] @@ -83,36 +76,45 @@ plot_bar <- function(data, with = NULL, dt_tmp <- suppressWarnings(melt.data.table(dt, measure.vars = setdiff(feature_names, by))) dt2 <- dt_tmp[, list(frequency = sum(frequency)), by = c("variable", "value", by)] } + dt2[, facet_value := paste0(value, "___", variable)] - ## Calculate number of pages + + ## Aesthetic handling + dots_list <- enquos(...) + flags <- vapply(dots_list, rlang::quo_is_symbolic, logical(1)) + mapped_aes <- dots_list[flags] + constant_aes <- dots_list[!flags] + layout <- .getPageLayout(nrow, ncol, ncol(discrete)) - ## Create list of ggplot objects + plot_list <- .lapply( parallel = parallel, X = layout, FUN = function(x) { + df <- dt2[variable %in% feature_names[x]] + if (order_bar) { - base_plot <- ggplot(dt2[variable %in% feature_names[x]], - aes(x = reorder(facet_value, frequency), y = frequency)) - } else { - base_plot <- ggplot(dt2[variable %in% feature_names[x]], aes(x = value, y = frequency)) - } - if (is.null(by)) { - base_plot2 <- base_plot + - geom_bar(stat = "identity") + - ylab(ifelse(is.null(with), "Frequency", toTitleCase(with))) + aes_base <- aes(x = reorder(facet_value, frequency), y = frequency) } else { - base_plot2 <- base_plot + - geom_bar(stat = "identity", aes_string(fill = by), position = by_position) + - ylab("") + aes_base <- aes(x = value, y = frequency) } - base_plot2 + + + aes_all <- modifyList(aes_base, eval_tidy(expr(aes(!!!mapped_aes)))) + + layer_args <- c( + list(stat = "identity", position = by_position), + lapply(constant_aes, eval_tidy) + ) + + p <- ggplot(df, aes_all) + + do.call("geom_bar", layer_args) + + ylab(ifelse(is.null(with), "Frequency", toTitleCase(with))) + scale_x_discrete(labels = function(x) tstrsplit(x, "___")[[1]]) + coord_flip() + xlab("") } ) - ## Plot objects + class(plot_list) <- c("multiple", class(plot_list)) plotDataExplorer( plot_obj = plot_list, diff --git a/R/plot_histogram.r b/R/plot_histogram.r index 9d46d64..18617f1 100644 --- a/R/plot_histogram.r +++ b/R/plot_histogram.r @@ -16,7 +16,7 @@ #' @keywords plot_histogram #' @import data.table #' @import ggplot2 -#' @importFrom rlang enquos eval_tidy expr quo_is_symbolic +#' @importFrom rlang enquos quo_is_symbolic eval_tidy expr #' @export #' @seealso \link{geom_histogram} \link{plot_density} #' @examples @@ -31,13 +31,18 @@ plot_histogram <- function(data, binary_as_factor = TRUE, parallel = FALSE, ...) { variable <- value <- NULL + if (!is.data.table(data)) data <- data.table(data) + split_data <- split_columns(data, binary_as_factor = binary_as_factor) if (split_data$num_continuous == 0) stop("No continuous features found!") + continuous <- split_data$continuous feature_names <- names(continuous) + dt <- suppressWarnings(melt.data.table(continuous, measure.vars = feature_names, variable.factor = FALSE)) - # Copy over non-measured columns (e.g., fill/grouping variables) + + # Copy over non-measured columns (e.g., grouping vars like 'Species') other_vars <- setdiff(names(data), names(dt)) if (length(other_vars) > 0) { dt <- cbind( @@ -45,13 +50,18 @@ plot_histogram <- function(data, binary_as_factor = TRUE, data[rep(seq_len(nrow(data)), times = length(feature_names)), ..other_vars] ) } - # Separate mapped vs constant aesthetics from ... - dots <- enquos(...) - mapped_aes <- dots[sapply(dots, rlang::quo_is_symbolic)] - constant_aes <- dots[!sapply(dots, rlang::quo_is_symbolic)] - # Combine mapped aesthetics with x = value - aes_combined <- eval_tidy(expr(aes(x = value, !!!mapped_aes))) + + # Capture and split mapped vs constant aesthetics + dots_list <- enquos(...) + flags <- vapply(dots_list, rlang::quo_is_symbolic, logical(1)) + mapped_aes <- dots_list[flags] + constant_aes <- dots_list[!flags] + + # Combine x aesthetic with any mapped ones + aes_combined <- modifyList(aes(x = value), eval_tidy(expr(aes(!!!mapped_aes)))) + layout <- .getPageLayout(nrow, ncol, ncol(continuous)) + plot_list <- .lapply( parallel = parallel, X = layout, @@ -62,12 +72,10 @@ plot_histogram <- function(data, binary_as_factor = TRUE, lapply(constant_aes, eval_tidy) ) - p <- ggplot(dt[variable %in% feature_names[x]], mapping = aes_combined) + + ggplot(dt[variable %in% feature_names[x]], mapping = aes_combined) + do.call("geom_histogram", layer_args) + do.call(paste0("scale_x_", scale_x), list()) + ylab("Frequency") - - p } ) diff --git a/man/plot_bar.Rd b/man/plot_bar.Rd index 0cb9343..9953b09 100644 --- a/man/plot_bar.Rd +++ b/man/plot_bar.Rd @@ -17,7 +17,8 @@ plot_bar( theme_config = list(), nrow = 3L, ncol = 3L, - parallel = FALSE + parallel = FALSE, + ... ) } \arguments{ @@ -46,6 +47,8 @@ plot_bar( \item{ncol}{number of columns per page. Default is 3.} \item{parallel}{enable parallel? Default is \code{FALSE}.} + +\item{...}{aesthetic mappings (e.g., fill = Species, alpha = 0.5)} } \value{ invisibly return the named list of ggplot objects @@ -53,20 +56,3 @@ invisibly return the named list of ggplot objects \description{ Plot bar chart for each discrete feature, based on either frequency or another continuous feature. } -\details{ -If a discrete feature contains more categories than \code{maxcat} specifies, it will not be passed to the plotting function. -} -\examples{ -# Plot bar charts for diamonds dataset -library(ggplot2) -plot_bar(diamonds) -plot_bar(diamonds, maxcat = 5) - -# Plot bar charts with `price` -plot_bar(diamonds, with = "price") - -# Plot bar charts by `cut` -plot_bar(diamonds, by = "cut") -plot_bar(diamonds, by = "cut", by_position = "dodge") -} -\keyword{plot_bar} diff --git a/man/plot_histogram.Rd b/man/plot_histogram.Rd index afbad47..a1c79d2 100644 --- a/man/plot_histogram.Rd +++ b/man/plot_histogram.Rd @@ -39,7 +39,7 @@ plot_histogram( \item{parallel}{enable parallel? Default is \code{FALSE}.} -\item{...}{aesthetic mappings passed to \link{aes}, such as \code{fill = group}} +\item{...}{aesthetic mappings passed to \link{aes}, such as \code{fill = group}, or constants like \code{alpha = 0.5}} } \value{ invisibly return the named list of ggplot objects @@ -48,7 +48,7 @@ invisibly return the named list of ggplot objects Plot histogram for each continuous feature } \examples{ -plot_histogram(iris, fill = Species, ncol = 2L) +plot_histogram(iris, fill = Species, alpha = 0.5, ncol = 2L) } \seealso{ \link{geom_histogram} \link{plot_density} From 278e939f081f0242f5559b0790db12267672b38c Mon Sep 17 00:00:00 2001 From: Giovanni M Dall'Olio Date: Tue, 22 Apr 2025 21:18:07 +0100 Subject: [PATCH 04/12] tests working ok --- R/plot_bar.r | 31 +++++++++++++++++++------------ tests/testthat/test-threedots.R | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 12 deletions(-) create mode 100644 tests/testthat/test-threedots.R diff --git a/R/plot_bar.r b/R/plot_bar.r index 4b71870..aad5221 100644 --- a/R/plot_bar.r +++ b/R/plot_bar.r @@ -79,42 +79,49 @@ plot_bar <- function(data, with = NULL, dt2[, facet_value := paste0(value, "___", variable)] - ## Aesthetic handling - dots_list <- enquos(...) - flags <- vapply(dots_list, rlang::quo_is_symbolic, logical(1)) - mapped_aes <- dots_list[flags] - constant_aes <- dots_list[!flags] + # 🔧 Add this block + other_vars <- setdiff(names(data), names(dt2)) + if (length(other_vars) > 0) { + dt2 <- cbind( + dt2, + data[rep(seq_len(nrow(data)), times = length(feature_names)), ..other_vars] + ) + } layout <- .getPageLayout(nrow, ncol, ncol(discrete)) - plot_list <- .lapply( parallel = parallel, X = layout, FUN = function(x) { df <- dt2[variable %in% feature_names[x]] - if (order_bar) { - aes_base <- aes(x = reorder(facet_value, frequency), y = frequency) + dots_list <- enquos(...) + flags <- vapply(dots_list, rlang::quo_is_symbolic, logical(1)) + mapped_aes <- dots_list[flags] + constant_aes <- dots_list[!flags] + + aes_base <- if (order_bar) { + aes(x = reorder(facet_value, frequency), y = frequency) } else { - aes_base <- aes(x = value, y = frequency) + aes(x = value, y = frequency) } aes_all <- modifyList(aes_base, eval_tidy(expr(aes(!!!mapped_aes)))) - layer_args <- c( list(stat = "identity", position = by_position), lapply(constant_aes, eval_tidy) ) - p <- ggplot(df, aes_all) + + ggplot(df, aes_all) + do.call("geom_bar", layer_args) + - ylab(ifelse(is.null(with), "Frequency", toTitleCase(with))) + + ylab(ifelse(is.null(with), "Frequency", tools::toTitleCase(with))) + scale_x_discrete(labels = function(x) tstrsplit(x, "___")[[1]]) + coord_flip() + xlab("") } ) + class(plot_list) <- c("multiple", class(plot_list)) plotDataExplorer( plot_obj = plot_list, diff --git a/tests/testthat/test-threedots.R b/tests/testthat/test-threedots.R new file mode 100644 index 0000000..127c5bf --- /dev/null +++ b/tests/testthat/test-threedots.R @@ -0,0 +1,33 @@ +context("three-dots") + +test_that("plot_histogram works with no aesthetics", { + expect_invisible(plot_histogram(iris)) +}) + +test_that("plot_histogram works with mapped aesthetic (fill)", { + expect_invisible(plot_histogram(iris, fill = Species)) +}) + +test_that("plot_histogram works with constant aesthetic (alpha)", { + expect_invisible(plot_histogram(iris, alpha = 0.5)) +}) + +test_that("plot_histogram works with both mapped and constant aesthetics", { + expect_invisible(plot_histogram(iris, fill = Species, alpha = 0.3)) +}) + +test_that("plot_bar works with no aesthetics", { + expect_invisible(plot_bar(iris)) +}) + +test_that("plot_bar works with mapped aesthetic (fill)", { + expect_invisible(plot_bar(iris, fill = Species)) +}) + +test_that("plot_bar works with constant aesthetic (alpha)", { + expect_invisible(plot_bar(iris, alpha = 0.5)) +}) + +test_that("plot_bar works with both mapped and constant aesthetics", { + expect_invisible(plot_bar(iris, fill = Species, alpha = 0.4)) +}) From ed2150ee3ab2ed7c9977840e6cee21b429d6b155 Mon Sep 17 00:00:00 2001 From: Giovanni M Dall'Olio Date: Tue, 22 Apr 2025 21:23:45 +0100 Subject: [PATCH 05/12] realigning commits --- R/plot_bar.r | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/R/plot_bar.r b/R/plot_bar.r index aad5221..63937ee 100644 --- a/R/plot_bar.r +++ b/R/plot_bar.r @@ -22,7 +22,18 @@ #' @importFrom stats reorder #' @importFrom tools toTitleCase #' @export - +#' @examples +#' # Plot bar charts for diamonds dataset +#' library(ggplot2) +#' plot_bar(diamonds) +#' plot_bar(diamonds, maxcat = 5) +#' +#' # Plot bar charts with `price` +#' plot_bar(diamonds, with = "price") +#' +#' # Plot bar charts by `cut` +#' plot_bar(diamonds, by = "cut") +#' plot_bar(diamonds, by = "cut", by_position = "dodge") plot_bar <- function(data, with = NULL, by = NULL, by_position = "fill", maxcat = 50, order_bar = TRUE, binary_as_factor = TRUE, @@ -33,23 +44,21 @@ plot_bar <- function(data, with = NULL, ...) { ## Declare vars to avoid CMD check warnings frequency <- measure <- variable <- value <- facet_value <- NULL - + ## Check if input is data.table if (!is.data.table(data)) data <- data.table(data) - + ## Stop if no discrete features split_data <- split_columns(data, binary_as_factor = binary_as_factor) if (split_data$num_discrete == 0) stop("No discrete features found!") - + ## Get discrete features discrete <- split_data$discrete - - ## Drop high-cardinality columns + ## Drop features with categories greater than `maxcat` ind <- .ignoreCat(discrete, maxcat = maxcat) if (length(ind)) { message(length(ind), " columns ignored with more than ", maxcat, " categories.\n", paste0(names(ind), ": ", ind, " categories\n")) drop_columns(discrete, names(ind)) if (length(discrete) == 0) stop("Note: All discrete features ignored! Nothing to plot!") } - - ## Aggregate values + ## Aggregate feature categories feature_names <- names(discrete) if (is.null(with)) { dt <- discrete[, list(frequency = .N), by = feature_names] @@ -78,8 +87,7 @@ plot_bar <- function(data, with = NULL, } dt2[, facet_value := paste0(value, "___", variable)] - - # 🔧 Add this block + ## Calculate number of pages other_vars <- setdiff(names(data), names(dt2)) if (length(other_vars) > 0) { dt2 <- cbind( @@ -87,31 +95,30 @@ plot_bar <- function(data, with = NULL, data[rep(seq_len(nrow(data)), times = length(feature_names)), ..other_vars] ) } - + ## Calculate number of pages layout <- .getPageLayout(nrow, ncol, ncol(discrete)) + ## Create list of ggplot objects plot_list <- .lapply( parallel = parallel, X = layout, FUN = function(x) { df <- dt2[variable %in% feature_names[x]] + # Capture extra parameters using ... dots_list <- enquos(...) flags <- vapply(dots_list, rlang::quo_is_symbolic, logical(1)) mapped_aes <- dots_list[flags] constant_aes <- dots_list[!flags] - aes_base <- if (order_bar) { aes(x = reorder(facet_value, frequency), y = frequency) } else { aes(x = value, y = frequency) } - aes_all <- modifyList(aes_base, eval_tidy(expr(aes(!!!mapped_aes)))) layer_args <- c( list(stat = "identity", position = by_position), lapply(constant_aes, eval_tidy) ) - ggplot(df, aes_all) + do.call("geom_bar", layer_args) + ylab(ifelse(is.null(with), "Frequency", tools::toTitleCase(with))) + @@ -121,7 +128,7 @@ plot_bar <- function(data, with = NULL, } ) - + ## Plot objects class(plot_list) <- c("multiple", class(plot_list)) plotDataExplorer( plot_obj = plot_list, From 3f6847f2a275d9b6543dadc86d0240c1a85a681b Mon Sep 17 00:00:00 2001 From: Giovanni M Dall'Olio Date: Tue, 22 Apr 2025 21:28:10 +0100 Subject: [PATCH 06/12] aligned comments --- R/plot_bar.r | 5 ++++- R/plot_histogram.r | 23 +++++++++++++++-------- man/plot_bar.Rd | 17 +++++++++++++++++ man/plot_histogram.Rd | 8 ++++++++ 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/R/plot_bar.r b/R/plot_bar.r index 63937ee..38a055c 100644 --- a/R/plot_bar.r +++ b/R/plot_bar.r @@ -16,6 +16,8 @@ #' @param parallel enable parallel? Default is \code{FALSE}. #' @param ... aesthetic mappings (e.g., fill = Species, alpha = 0.5) #' @return invisibly return the named list of ggplot objects +#' @keywords plot_bar +#' @details If a discrete feature contains more categories than \code{maxcat} specifies, it will not be passed to the plotting function. #' @import data.table #' @import ggplot2 #' @importFrom rlang enquos quo_is_symbolic eval_tidy expr @@ -34,6 +36,7 @@ #' # Plot bar charts by `cut` #' plot_bar(diamonds, by = "cut") #' plot_bar(diamonds, by = "cut", by_position = "dodge") + plot_bar <- function(data, with = NULL, by = NULL, by_position = "fill", maxcat = 50, order_bar = TRUE, binary_as_factor = TRUE, @@ -42,7 +45,7 @@ plot_bar <- function(data, with = NULL, nrow = 3L, ncol = 3L, parallel = FALSE, ...) { - ## Declare vars to avoid CMD check warnings + ## Declare variable first to pass R CMD check frequency <- measure <- variable <- value <- facet_value <- NULL ## Check if input is data.table if (!is.data.table(data)) data <- data.table(data) diff --git a/R/plot_histogram.r b/R/plot_histogram.r index 18617f1..1b61315 100644 --- a/R/plot_histogram.r +++ b/R/plot_histogram.r @@ -21,7 +21,14 @@ #' @seealso \link{geom_histogram} \link{plot_density} #' @examples #' plot_histogram(iris, fill = Species, alpha = 0.5, ncol = 2L) - +#' # Plot iris data +#' plot_histogram(iris, ncol = 2L) +#' +#' # Plot skewed data on log scale +#' set.seed(1) +#' skew <- data.frame(replicate(4L, rbeta(1000, 1, 5000))) +#' plot_histogram(skew, ncol = 2L) +#' plot_histogram(skew, scale_x = "log10", ncol = 2L) plot_histogram <- function(data, binary_as_factor = TRUE, geom_histogram_args = list("bins" = 30L), scale_x = "continuous", @@ -30,18 +37,17 @@ plot_histogram <- function(data, binary_as_factor = TRUE, nrow = 4L, ncol = 4L, parallel = FALSE, ...) { + ## Declare variable first to pass R CMD check variable <- value <- NULL - + ## Check if input is data.table if (!is.data.table(data)) data <- data.table(data) - + ## Stop if no continuous features split_data <- split_columns(data, binary_as_factor = binary_as_factor) if (split_data$num_continuous == 0) stop("No continuous features found!") - + ## Get and reshape continuous features continuous <- split_data$continuous feature_names <- names(continuous) - dt <- suppressWarnings(melt.data.table(continuous, measure.vars = feature_names, variable.factor = FALSE)) - # Copy over non-measured columns (e.g., grouping vars like 'Species') other_vars <- setdiff(names(data), names(dt)) if (length(other_vars) > 0) { @@ -60,8 +66,9 @@ plot_histogram <- function(data, binary_as_factor = TRUE, # Combine x aesthetic with any mapped ones aes_combined <- modifyList(aes(x = value), eval_tidy(expr(aes(!!!mapped_aes)))) + ## Calculate number of pages layout <- .getPageLayout(nrow, ncol, ncol(continuous)) - + ## Create ggplot object plot_list <- .lapply( parallel = parallel, X = layout, @@ -78,7 +85,7 @@ plot_histogram <- function(data, binary_as_factor = TRUE, ylab("Frequency") } ) - + ## Plot objects class(plot_list) <- c("multiple", class(plot_list)) plotDataExplorer( plot_obj = plot_list, diff --git a/man/plot_bar.Rd b/man/plot_bar.Rd index 9953b09..a5ecbfe 100644 --- a/man/plot_bar.Rd +++ b/man/plot_bar.Rd @@ -56,3 +56,20 @@ invisibly return the named list of ggplot objects \description{ Plot bar chart for each discrete feature, based on either frequency or another continuous feature. } +\details{ +If a discrete feature contains more categories than \code{maxcat} specifies, it will not be passed to the plotting function. +} +\examples{ +# Plot bar charts for diamonds dataset +library(ggplot2) +plot_bar(diamonds) +plot_bar(diamonds, maxcat = 5) + +# Plot bar charts with `price` +plot_bar(diamonds, with = "price") + +# Plot bar charts by `cut` +plot_bar(diamonds, by = "cut") +plot_bar(diamonds, by = "cut", by_position = "dodge") +} +\keyword{plot_bar} diff --git a/man/plot_histogram.Rd b/man/plot_histogram.Rd index a1c79d2..a3d6949 100644 --- a/man/plot_histogram.Rd +++ b/man/plot_histogram.Rd @@ -49,6 +49,14 @@ Plot histogram for each continuous feature } \examples{ plot_histogram(iris, fill = Species, alpha = 0.5, ncol = 2L) +# Plot iris data +plot_histogram(iris, ncol = 2L) + +# Plot skewed data on log scale +set.seed(1) +skew <- data.frame(replicate(4L, rbeta(1000, 1, 5000))) +plot_histogram(skew, ncol = 2L) +plot_histogram(skew, scale_x = "log10", ncol = 2L) } \seealso{ \link{geom_histogram} \link{plot_density} From c06efc5d355fe51d187ef0618c642fc2df34f312 Mon Sep 17 00:00:00 2001 From: Giovanni M Dall'Olio Date: Tue, 22 Apr 2025 21:34:04 +0100 Subject: [PATCH 07/12] modified boxplot --- R/plot_boxplot.r | 28 +++++++++++++++++++++++++--- tests/testthat/test-threedots.R | 17 ++++++++++++++++- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/R/plot_boxplot.r b/R/plot_boxplot.r index 6a945b0..c286815 100644 --- a/R/plot_boxplot.r +++ b/R/plot_boxplot.r @@ -41,7 +41,8 @@ plot_boxplot <- function(data, by, title = NULL, ggtheme = theme_gray(), theme_config = list(), nrow = 3L, ncol = 4L, - parallel = FALSE) { + parallel = FALSE, + ...) { ## Declare variable first to pass R CMD check variable <- by_f <- value <- NULL ## Check if input is data.table @@ -60,6 +61,16 @@ plot_boxplot <- function(data, by, dt <- suppressWarnings(melt.data.table(data.table(continuous, "by_f" = by_feature), id.vars = "by_f", variable.factor = FALSE)) } dt2 <- dt[variable != by] + + ## Replicate other columns for use in ... + other_vars <- setdiff(names(data), names(dt2)) + if (length(other_vars) > 0) { + dt2 <- cbind( + dt2, + data[rep(seq_len(nrow(data)), times = ncol(continuous)), ..other_vars] + ) + } + feature_names <- unique(dt2[["variable"]]) ## Calculate number of pages layout <- .getPageLayout(nrow, ncol, length(feature_names)) @@ -68,8 +79,18 @@ plot_boxplot <- function(data, by, parallel = parallel, X = layout, FUN = function(x) { - base_plot <- ggplot(dt2[variable %in% feature_names[x]], aes(x = by_f, y = value)) + - do.call("geom_boxplot", geom_boxplot_args) + + dots_list <- rlang::enquos(...) + flags <- vapply(dots_list, rlang::quo_is_symbolic, logical(1)) + mapped_aes <- dots_list[flags] + constant_aes <- dots_list[!flags] + + aes_base <- aes(x = by_f, y = value) + aes_combined <- modifyList(aes_base, rlang::eval_tidy(rlang::expr(aes(!!!mapped_aes)))) + + layer_args <- c(geom_boxplot_args, lapply(constant_aes, rlang::eval_tidy)) + + base_plot <- ggplot(dt2[variable %in% feature_names[x]], mapping = aes_combined) + + do.call("geom_boxplot", layer_args) + do.call(paste0("scale_y_", scale_y), list()) + coord_flip() + xlab(by) @@ -93,3 +114,4 @@ plot_boxplot <- function(data, by, ) ) } + diff --git a/tests/testthat/test-threedots.R b/tests/testthat/test-threedots.R index 127c5bf..f0b81e2 100644 --- a/tests/testthat/test-threedots.R +++ b/tests/testthat/test-threedots.R @@ -1,5 +1,4 @@ context("three-dots") - test_that("plot_histogram works with no aesthetics", { expect_invisible(plot_histogram(iris)) }) @@ -31,3 +30,19 @@ test_that("plot_bar works with constant aesthetic (alpha)", { test_that("plot_bar works with both mapped and constant aesthetics", { expect_invisible(plot_bar(iris, fill = Species, alpha = 0.4)) }) + +test_that("plot_boxplot works with no aesthetics", { + expect_invisible(plot_boxplot(iris, by = "Species")) +}) + +test_that("plot_boxplot works with mapped aesthetic (fill)", { + expect_invisible(plot_boxplot(iris, by = "Species", fill = Species)) +}) + +test_that("plot_boxplot works with constant aesthetic (alpha)", { + expect_invisible(plot_boxplot(iris, by = "Species", alpha = 0.5)) +}) + +test_that("plot_boxplot works with both mapped and constant aesthetics", { + expect_invisible(plot_boxplot(iris, by = "Species", fill = Species, alpha = 0.3)) +}) \ No newline at end of file From 0ceac0e455597c50295ec29c5438907f011b0b9d Mon Sep 17 00:00:00 2001 From: Giovanni M Dall'Olio Date: Tue, 22 Apr 2025 21:38:04 +0100 Subject: [PATCH 08/12] modified density plot --- R/plot_boxplot.r | 1 + R/plot_density.r | 26 +++++++++++++++++++++--- R/plot_scatterplot.r | 25 ++++++++++++++++++++--- tests/testthat/test-threedots.R | 35 ++++++++++++++++++++++++++++++++- 4 files changed, 80 insertions(+), 7 deletions(-) diff --git a/R/plot_boxplot.r b/R/plot_boxplot.r index c286815..9b0fe2b 100644 --- a/R/plot_boxplot.r +++ b/R/plot_boxplot.r @@ -13,6 +13,7 @@ #' @param nrow number of rows per page #' @param ncol number of columns per page #' @param parallel enable parallel? Default is \code{FALSE}. +#' @param ... aesthetic mappings (e.g., fill = Species, alpha = 0.5) #' @return invisibly return the named list of ggplot objects #' @keywords plot_boxplot #' @import data.table diff --git a/R/plot_density.r b/R/plot_density.r index a2d8202..9f1e03d 100644 --- a/R/plot_density.r +++ b/R/plot_density.r @@ -11,6 +11,7 @@ #' @param nrow number of rows per page. Default is 4. #' @param ncol number of columns per page. Default is 4. #' @param parallel enable parallel? Default is \code{FALSE}. +#' @param ... aesthetic mappings (e.g., fill = Species, alpha = 0.5) #' @return invisibly return the named list of ggplot objects #' @keywords plot_density #' @import data.table @@ -36,7 +37,8 @@ plot_density <- function(data, binary_as_factor = TRUE, title = NULL, ggtheme = theme_gray(), theme_config = list(), nrow = 4L, ncol = 4L, - parallel = FALSE) { + parallel = FALSE, + ...) { ## Declare variable first to pass R CMD check variable <- value <- NULL ## Check if input is data.table @@ -48,6 +50,16 @@ plot_density <- function(data, binary_as_factor = TRUE, continuous <- split_data$continuous feature_names <- names(continuous) dt <- suppressWarnings(melt.data.table(continuous, measure.vars = feature_names, variable.factor = FALSE)) + + ## Replicate other columns so mapped aesthetics work + other_vars <- setdiff(names(data), names(dt)) + if (length(other_vars) > 0) { + dt <- cbind( + dt, + data[rep(seq_len(nrow(data)), times = length(feature_names)), ..other_vars] + ) + } + ## Calculate number of pages layout <- .getPageLayout(nrow, ncol, ncol(continuous)) ## Create ggplot object @@ -55,8 +67,16 @@ plot_density <- function(data, binary_as_factor = TRUE, parallel = parallel, X = layout, FUN = function(x) { - ggplot(dt[variable %in% feature_names[x]], aes(x = value)) + - do.call("geom_density", c("na.rm" = TRUE, geom_density_args)) + + dots_list <- rlang::enquos(...) + flags <- vapply(dots_list, rlang::quo_is_symbolic, logical(1)) + mapped_aes <- dots_list[flags] + constant_aes <- dots_list[!flags] + + aes_combined <- modifyList(aes(x = value), rlang::eval_tidy(rlang::expr(aes(!!!mapped_aes)))) + layer_args <- c(list(na.rm = TRUE), geom_density_args, lapply(constant_aes, rlang::eval_tidy)) + + ggplot(dt[variable %in% feature_names[x]], mapping = aes_combined) + + do.call("geom_density", layer_args) + do.call(paste0("scale_x_", scale_x), list()) + ylab("Density") } diff --git a/R/plot_scatterplot.r b/R/plot_scatterplot.r index 9016949..c2a7cdd 100644 --- a/R/plot_scatterplot.r +++ b/R/plot_scatterplot.r @@ -14,6 +14,7 @@ #' @param nrow number of rows per page #' @param ncol number of columns per page #' @param parallel enable parallel? Default is \code{FALSE}. +#' @param ... aesthetic mappings (e.g., fill = Species, alpha = 0.5) #' @return invisibly return the named list of ggplot objects #' @keywords plot_scatterplot #' @import data.table @@ -56,7 +57,8 @@ plot_scatterplot <- function(data, by, sampled_rows = nrow(data), title = NULL, ggtheme = theme_gray(), theme_config = list(), nrow = 3L, ncol = 3L, - parallel = FALSE) { + parallel = FALSE, + ...) { ## Declare variable first to pass R CMD check variable <- NULL ## Check if input is data.table @@ -65,6 +67,13 @@ plot_scatterplot <- function(data, by, sampled_rows = nrow(data), if (sampled_rows < nrow(data)) data <- data[sample.int(nrow(data), sampled_rows)] ## Create plot function dt <- suppressWarnings(melt.data.table(data, id.vars = by, variable.factor = FALSE)) + + ## Replicate columns for aesthetic mapping + other_vars <- setdiff(names(data), names(dt)) + if (length(other_vars) > 0) { + dt <- cbind(dt, data[rep(seq_len(nrow(data)), times = ncol(data) - 1L), ..other_vars]) + } + feature_names <- unique(dt[["variable"]]) ## Calculate number of pages layout <- .getPageLayout(nrow, ncol, length(feature_names)) @@ -73,10 +82,20 @@ plot_scatterplot <- function(data, by, sampled_rows = nrow(data), parallel = parallel, X = layout, FUN = function(x) { - base_plot <- ggplot(dt[variable %in% feature_names[x]], aes_string(x = by, y = "value")) + - do.call("geom_point", geom_point_args) + + dots_list <- rlang::enquos(...) + flags <- vapply(dots_list, rlang::quo_is_symbolic, logical(1)) + mapped_aes <- dots_list[flags] + constant_aes <- dots_list[!flags] + + aes_base <- aes_string(x = by, y = "value") + aes_combined <- modifyList(aes_base, rlang::eval_tidy(rlang::expr(aes(!!!mapped_aes)))) + layer_args <- c(geom_point_args, lapply(constant_aes, rlang::eval_tidy)) + + base_plot <- ggplot(dt[variable %in% feature_names[x]], mapping = aes_combined) + + do.call("geom_point", layer_args) + coord_flip() + xlab(by) + if (!is.null(scale_x)) base_plot <- base_plot + do.call(paste0("scale_x_", scale_x), list()) if (!is.null(scale_y)) base_plot <- base_plot + do.call(paste0("scale_y_", scale_y), list()) if (!identical(geom_jitter_args, list())) base_plot <- base_plot + do.call("geom_jitter", geom_jitter_args) diff --git a/tests/testthat/test-threedots.R b/tests/testthat/test-threedots.R index f0b81e2..aa50dae 100644 --- a/tests/testthat/test-threedots.R +++ b/tests/testthat/test-threedots.R @@ -1,4 +1,5 @@ context("three-dots") + test_that("plot_histogram works with no aesthetics", { expect_invisible(plot_histogram(iris)) }) @@ -45,4 +46,36 @@ test_that("plot_boxplot works with constant aesthetic (alpha)", { test_that("plot_boxplot works with both mapped and constant aesthetics", { expect_invisible(plot_boxplot(iris, by = "Species", fill = Species, alpha = 0.3)) -}) \ No newline at end of file +}) + +test_that("plot_scatterplot works with no aesthetics", { + expect_invisible(plot_scatterplot(iris, by = "Species")) +}) + +test_that("plot_scatterplot works with mapped aesthetic (fill)", { + expect_invisible(plot_scatterplot(iris, by = "Species", fill = Species)) +}) + +test_that("plot_scatterplot works with constant aesthetic (alpha)", { + expect_invisible(plot_scatterplot(iris, by = "Species", alpha = 0.5)) +}) + +test_that("plot_scatterplot works with both mapped and constant aesthetics", { + expect_invisible(plot_scatterplot(iris, by = "Species", fill = Species, alpha = 0.3)) +}) + +test_that("plot_density works with no aesthetics", { + expect_invisible(plot_density(iris)) +}) + +test_that("plot_density works with mapped aesthetic (fill)", { + expect_invisible(plot_density(iris, fill = Species)) +}) + +test_that("plot_density works with constant aesthetic (alpha)", { + expect_invisible(plot_density(iris, alpha = 0.4)) +}) + +test_that("plot_density works with both mapped and constant aesthetics", { + expect_invisible(plot_density(iris, fill = Species, alpha = 0.3)) +}) From cede66c85c9afa42e5e1d3975e18bf9a76d2ada2 Mon Sep 17 00:00:00 2001 From: Giovanni M Dall'Olio Date: Tue, 22 Apr 2025 21:39:57 +0100 Subject: [PATCH 09/12] updated docs --- man/plot_boxplot.Rd | 5 ++++- man/plot_density.Rd | 5 ++++- man/plot_scatterplot.Rd | 5 ++++- tests/testthat/test-threedots.R | 6 +++--- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/man/plot_boxplot.Rd b/man/plot_boxplot.Rd index b00c198..3d96338 100644 --- a/man/plot_boxplot.Rd +++ b/man/plot_boxplot.Rd @@ -16,7 +16,8 @@ plot_boxplot( theme_config = list(), nrow = 3L, ncol = 4L, - parallel = FALSE + parallel = FALSE, + ... ) } \arguments{ @@ -43,6 +44,8 @@ plot_boxplot( \item{ncol}{number of columns per page} \item{parallel}{enable parallel? Default is \code{FALSE}.} + +\item{...}{aesthetic mappings (e.g., fill = Species, alpha = 0.5)} } \value{ invisibly return the named list of ggplot objects diff --git a/man/plot_density.Rd b/man/plot_density.Rd index 74bf950..36c8fdb 100644 --- a/man/plot_density.Rd +++ b/man/plot_density.Rd @@ -14,7 +14,8 @@ plot_density( theme_config = list(), nrow = 4L, ncol = 4L, - parallel = FALSE + parallel = FALSE, + ... ) } \arguments{ @@ -37,6 +38,8 @@ plot_density( \item{ncol}{number of columns per page. Default is 4.} \item{parallel}{enable parallel? Default is \code{FALSE}.} + +\item{...}{aesthetic mappings (e.g., fill = Species, alpha = 0.5)} } \value{ invisibly return the named list of ggplot objects diff --git a/man/plot_scatterplot.Rd b/man/plot_scatterplot.Rd index 5c97da5..f6a2a6b 100644 --- a/man/plot_scatterplot.Rd +++ b/man/plot_scatterplot.Rd @@ -17,7 +17,8 @@ plot_scatterplot( theme_config = list(), nrow = 3L, ncol = 3L, - parallel = FALSE + parallel = FALSE, + ... ) } \arguments{ @@ -46,6 +47,8 @@ plot_scatterplot( \item{ncol}{number of columns per page} \item{parallel}{enable parallel? Default is \code{FALSE}.} + +\item{...}{aesthetic mappings (e.g., fill = Species, alpha = 0.5)} } \value{ invisibly return the named list of ggplot objects diff --git a/tests/testthat/test-threedots.R b/tests/testthat/test-threedots.R index aa50dae..ab8c785 100644 --- a/tests/testthat/test-threedots.R +++ b/tests/testthat/test-threedots.R @@ -72,10 +72,10 @@ test_that("plot_density works with mapped aesthetic (fill)", { expect_invisible(plot_density(iris, fill = Species)) }) -test_that("plot_density works with constant aesthetic (alpha)", { - expect_invisible(plot_density(iris, alpha = 0.4)) +test_that("plot_density works with constant aesthetic (size)", { + expect_invisible(plot_density(iris, size = 4)) }) test_that("plot_density works with both mapped and constant aesthetics", { - expect_invisible(plot_density(iris, fill = Species, alpha = 0.3)) + expect_invisible(plot_density(iris, fill = Species, alpha = 0.3, size=4)) }) From 15f1f64bb1d81f9c0c62434ecc6a8472075dc2dd Mon Sep 17 00:00:00 2001 From: Giovanni M Dall'Olio Date: Wed, 23 Apr 2025 18:55:28 +0100 Subject: [PATCH 10/12] rebuilding vignettes, after small fix --- R/plot_bar.r | 4 ++++ R/plot_boxplot.r | 4 ++++ R/plot_density.r | 4 ++++ R/plot_histogram.r | 4 ++++ R/plot_scatterplot.r | 4 ++++ 5 files changed, 20 insertions(+) diff --git a/R/plot_bar.r b/R/plot_bar.r index 38a055c..1658c27 100644 --- a/R/plot_bar.r +++ b/R/plot_bar.r @@ -45,6 +45,10 @@ plot_bar <- function(data, with = NULL, nrow = 3L, ncol = 3L, parallel = FALSE, ...) { + # Ensure this works when data is a vector, like the vignette + if (!is.data.frame(data)) { + data <- data.frame(value = data) + } ## Declare variable first to pass R CMD check frequency <- measure <- variable <- value <- facet_value <- NULL ## Check if input is data.table diff --git a/R/plot_boxplot.r b/R/plot_boxplot.r index 9b0fe2b..1462606 100644 --- a/R/plot_boxplot.r +++ b/R/plot_boxplot.r @@ -44,6 +44,10 @@ plot_boxplot <- function(data, by, nrow = 3L, ncol = 4L, parallel = FALSE, ...) { + # Ensure this works when data is a vector, like the vignette + if (!is.data.frame(data)) { + data <- data.frame(value = data) + } ## Declare variable first to pass R CMD check variable <- by_f <- value <- NULL ## Check if input is data.table diff --git a/R/plot_density.r b/R/plot_density.r index 9f1e03d..7a664a0 100644 --- a/R/plot_density.r +++ b/R/plot_density.r @@ -39,6 +39,10 @@ plot_density <- function(data, binary_as_factor = TRUE, nrow = 4L, ncol = 4L, parallel = FALSE, ...) { + # Ensure this works when data is a vector, like the vignette + if (!is.data.frame(data)) { + data <- data.frame(value = data) + } ## Declare variable first to pass R CMD check variable <- value <- NULL ## Check if input is data.table diff --git a/R/plot_histogram.r b/R/plot_histogram.r index 1b61315..5ca24c7 100644 --- a/R/plot_histogram.r +++ b/R/plot_histogram.r @@ -37,6 +37,10 @@ plot_histogram <- function(data, binary_as_factor = TRUE, nrow = 4L, ncol = 4L, parallel = FALSE, ...) { + # Ensure this works when data is a vector, like the vignette + if (!is.data.frame(data)) { + data <- data.frame(value = data) + } ## Declare variable first to pass R CMD check variable <- value <- NULL ## Check if input is data.table diff --git a/R/plot_scatterplot.r b/R/plot_scatterplot.r index c2a7cdd..3c1d0e6 100644 --- a/R/plot_scatterplot.r +++ b/R/plot_scatterplot.r @@ -59,6 +59,10 @@ plot_scatterplot <- function(data, by, sampled_rows = nrow(data), nrow = 3L, ncol = 3L, parallel = FALSE, ...) { + # Ensure this works when data is a vector, like the vignette + if (!is.data.frame(data)) { + data <- data.frame(value = data) + } ## Declare variable first to pass R CMD check variable <- NULL ## Check if input is data.table From 01d0618b9c11915d77097e6e2fd8ded8b25d47c1 Mon Sep 17 00:00:00 2001 From: Giovanni M Dall'Olio Date: Wed, 23 Apr 2025 20:36:47 +0100 Subject: [PATCH 11/12] fixed missing dep rlang --- DESCRIPTION | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index f009e4f..c42339c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -19,7 +19,8 @@ Imports: stats, utils, tools, - parallel + parallel, + rlang Suggests: testthat, covr, From 6b631d9a20c51cdf97f978fcf24f210bdaad2068 Mon Sep 17 00:00:00 2001 From: Giovanni M Dall'Olio Date: Wed, 23 Apr 2025 21:13:51 +0100 Subject: [PATCH 12/12] fixed imports and other issues, although one test may still fail --- NAMESPACE | 1 + R/plot_bar.r | 1 + R/plot_boxplot.r | 1 + R/plot_density.r | 1 + R/plot_histogram.r | 1 + R/plot_scatterplot.r | 1 + tests/testthat/test-plot-bar.r | 2 +- 7 files changed, 7 insertions(+), 1 deletion(-) diff --git a/NAMESPACE b/NAMESPACE index e196400..087bd0c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -47,5 +47,6 @@ importFrom(stats,setNames) importFrom(tools,toTitleCase) importFrom(utils,browseURL) importFrom(utils,capture.output) +importFrom(utils,modifyList) importFrom(utils,object.size) importFrom(utils,str) diff --git a/R/plot_bar.r b/R/plot_bar.r index 1658c27..37ff865 100644 --- a/R/plot_bar.r +++ b/R/plot_bar.r @@ -23,6 +23,7 @@ #' @importFrom rlang enquos quo_is_symbolic eval_tidy expr #' @importFrom stats reorder #' @importFrom tools toTitleCase +#' @importFrom utils modifyList #' @export #' @examples #' # Plot bar charts for diamonds dataset diff --git a/R/plot_boxplot.r b/R/plot_boxplot.r index 1462606..1191933 100644 --- a/R/plot_boxplot.r +++ b/R/plot_boxplot.r @@ -18,6 +18,7 @@ #' @keywords plot_boxplot #' @import data.table #' @import ggplot2 +#' @importFrom utils modifyList #' @export #' @seealso \link{geom_boxplot} #' @examples diff --git a/R/plot_density.r b/R/plot_density.r index 7a664a0..b317ee9 100644 --- a/R/plot_density.r +++ b/R/plot_density.r @@ -16,6 +16,7 @@ #' @keywords plot_density #' @import data.table #' @import ggplot2 +#' @importFrom utils modifyList #' @export #' @seealso \link{geom_density} \link{plot_histogram} #' @examples diff --git a/R/plot_histogram.r b/R/plot_histogram.r index 5ca24c7..44b2f51 100644 --- a/R/plot_histogram.r +++ b/R/plot_histogram.r @@ -16,6 +16,7 @@ #' @keywords plot_histogram #' @import data.table #' @import ggplot2 +#' @importFrom utils modifyList #' @importFrom rlang enquos quo_is_symbolic eval_tidy expr #' @export #' @seealso \link{geom_histogram} \link{plot_density} diff --git a/R/plot_scatterplot.r b/R/plot_scatterplot.r index 3c1d0e6..40aeaa5 100644 --- a/R/plot_scatterplot.r +++ b/R/plot_scatterplot.r @@ -19,6 +19,7 @@ #' @keywords plot_scatterplot #' @import data.table #' @import ggplot2 +#' @importFrom utils modifyList #' @export #' @seealso \link{geom_point} #' @examples diff --git a/tests/testthat/test-plot-bar.r b/tests/testthat/test-plot-bar.r index 59b3ec0..956ee30 100644 --- a/tests/testthat/test-plot-bar.r +++ b/tests/testthat/test-plot-bar.r @@ -30,7 +30,7 @@ test_that("test binary categories", { ) expect_silent(plot_bar(df, with = "a")) expect_error(suppressWarnings(plot_bar(df, with = "b"))) - expect_silent(plot_bar(df, with = "c")) + expect_warning(plot_bar(df, with = "c")) expect_silent(plot_bar(df, with = "a", binary_as_factor = FALSE)) expect_error(plot_bar(df, by = "c")) })