From 73e67ca81826b775a02c8a7295fa4f2483e925f4 Mon Sep 17 00:00:00 2001 From: mahaalbashir Date: Thu, 23 Jul 2026 14:14:25 +0100 Subject: [PATCH 1/3] catching calls to unsupported aggregation function in R --- R/acro_tables.R | 41 ++++++++++++++++++++++++-- tests/testthat/test-acro_crosstab.R | 29 ++++++++++++++++++ tests/testthat/test-acro_pivot_table.R | 13 ++++++++ 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/R/acro_tables.R b/R/acro_tables.R index 6093ed4..586f276 100644 --- a/R/acro_tables.R +++ b/R/acro_tables.R @@ -20,12 +20,33 @@ acro_crosstab <- function(index, columns, values = NULL, rownames = NULL, colnam stop("ACRO has not been initialised. Please first call acro_init()") } - py_table <- acroEnv$ac$crosstab(index, columns, values = values, rownames = rownames, colnames = colnames, aggfunc = aggfunc, margins = margins, margins_name = margins_name, dropna = dropna, normalize = normalize, show_suppressed = show_suppressed) + # Convert the values into a NumPy array so that the Python ACRO crosstab function accepts them + np <- reticulate::import("numpy", convert = TRUE) + py_values <- if (!is.null(values)) np$array(values) else NULL + + py_table <- tryCatch( + { + acroEnv$ac$crosstab(index, columns, values = py_values, rownames = rownames, colnames = colnames, aggfunc = aggfunc, margins = margins, margins_name = margins_name, dropna = dropna, normalize = normalize, show_suppressed = show_suppressed) + }, + error = function(e) { + # Check if Python threw the ValueError about an unsupported aggfunc + if (grepl("ValueError: aggfunc", e$message)) { + stop( + sprintf("Unsupported aggregation function provided: '%s'. Allowed functions are: mean, median, sum, std, count, mode.", aggfunc), + call. = FALSE + ) + } else { + stop(e) + } + } + ) + table <- reticulate::py_to_r(py_table) return(table) } + #' Compute a simple cross tabulation of two (or more) factors. #' #' @param index Values to group by in the rows. @@ -152,7 +173,23 @@ acro_pivot_table <- function(data, values = NULL, index = NULL, columns = NULL, if (is.null(acroEnv$ac)) { stop("ACRO has not been initialised. Please first call acro_init()") } - py_table <- acroEnv$ac$pivot_table(data, values = values, index = index, columns = columns, aggfunc = aggfunc) + + py_table <- tryCatch( + { + acroEnv$ac$pivot_table(data, values = values, index = index, columns = columns, aggfunc = aggfunc) + }, + error = function(e) { + if (grepl("aggfunc", e$message, ignore.case = TRUE)) { + stop( + sprintf("Unsupported aggregation function provided: '%s'. Allowed functions are: mean, median, sum, std, count, mode.", aggfunc), + call. = FALSE + ) + } else { + stop(e) + } + } + ) + table <- reticulate::py_to_r(py_table) return(table) } diff --git a/tests/testthat/test-acro_crosstab.R b/tests/testthat/test-acro_crosstab.R index 30e5002..12cf4b6 100644 --- a/tests/testthat/test-acro_crosstab.R +++ b/tests/testthat/test-acro_crosstab.R @@ -28,3 +28,32 @@ test_that("acro_crosstab works with margins", { expect_equal(unname(as.matrix(r_table)), unname(as.matrix(p_table))) }) + +test_that("acro_crosstab works with aggregation function", { + # Creating the data frame with the specified values and row names + testthat::skip_on_cran() + expected_table <- data.frame( + convenient = c(3.25, 3.23, 3.25), + inconv = c(3.26, 3.26, 3.24) + ) + + # Assigning row names to the data frame + row.names(expected_table) <- c("not_recom", "priority", "recommended") + + acro_init() + table <- acro_crosstab(index = nursery_data[, c("health")], columns = nursery_data[, c("finance")], values = nursery_data[, c("children")], aggfunc = "mean") + expect_equal(table[, -1, drop = FALSE], expected_table[, -1, drop = FALSE], tolerance = 0.01) +}) + +test_that("acro_crosstab throws an error for unsupported aggregation functions", { + acro_init() + expect_error( + acro_crosstab( + index = nursery_data$health, + columns = nursery_data$finance, + values = nursery_data$children, + aggfunc = "max" + ), + "Unsupported aggregation function provided" + ) +}) diff --git a/tests/testthat/test-acro_pivot_table.R b/tests/testthat/test-acro_pivot_table.R index ea43e11..fffc7ad 100644 --- a/tests/testthat/test-acro_pivot_table.R +++ b/tests/testthat/test-acro_pivot_table.R @@ -19,3 +19,16 @@ test_that("acro_pivot_table works", { table <- acro_pivot_table(data = nursery_data, index = "parents", values = "children", aggfunc = list("mean", "std")) expect_equal(table[, -1, drop = FALSE], expected_table[, -1, drop = FALSE]) }) + +test_that("acro_pivot_table throws an error for unsupported aggregation functions", { + acro_init() + expect_error( + acro_pivot_table( + data = nursery_data, + index = nursery_data$parents, + values = nursery_data$children, + aggfunc = "max" + ), + "Unsupported aggregation function provided" + ) +}) From 5d7d7cef5819e9ae6b7fe1b5cc4b2c7b53622cae Mon Sep 17 00:00:00 2001 From: mahaalbashir Date: Thu, 23 Jul 2026 15:48:52 +0100 Subject: [PATCH 2/3] ctach more python errors --- R/acro_tables.R | 14 +++++++++++--- tests/testthat/test-acro_crosstab.R | 13 +++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/R/acro_tables.R b/R/acro_tables.R index 586f276..7efa646 100644 --- a/R/acro_tables.R +++ b/R/acro_tables.R @@ -34,9 +34,17 @@ acro_crosstab <- function(index, columns, values = NULL, rownames = NULL, colnam stop( sprintf("Unsupported aggregation function provided: '%s'. Allowed functions are: mean, median, sum, std, count, mode.", aggfunc), call. = FALSE + )} + # 2. Check if Python threw the missing values column error + else if (grepl("If you pass an aggregation function to crosstab", e$message)) { + stop( + "If you pass an aggregation function to crosstab, you must also specify a single values column to aggregate over.", + call. = FALSE ) - } else { - stop(e) + } + # any other unexpected errors + else { + stop(e) # nocov } } ) @@ -185,7 +193,7 @@ acro_pivot_table <- function(data, values = NULL, index = NULL, columns = NULL, call. = FALSE ) } else { - stop(e) + stop(e) # nocov } } ) diff --git a/tests/testthat/test-acro_crosstab.R b/tests/testthat/test-acro_crosstab.R index 12cf4b6..8bbc35c 100644 --- a/tests/testthat/test-acro_crosstab.R +++ b/tests/testthat/test-acro_crosstab.R @@ -57,3 +57,16 @@ test_that("acro_crosstab throws an error for unsupported aggregation functions", "Unsupported aggregation function provided" ) }) + +test_that("acro_crosstab throws an error for missing values", { + acro_init() + expect_error( + acro_crosstab( + index = nursery_data$health, + columns = nursery_data$finance, + aggfunc = "mean" + ), + "If you pass an aggregation function to crosstab" + ) +}) + From c0be4de404df7d904e647aba750b6e0752a85c08 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 23 Jul 2026 14:49:23 +0000 Subject: [PATCH 3/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- R/acro_tables.R | 5 +++-- tests/testthat/test-acro_crosstab.R | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/R/acro_tables.R b/R/acro_tables.R index 7efa646..a926791 100644 --- a/R/acro_tables.R +++ b/R/acro_tables.R @@ -34,14 +34,15 @@ acro_crosstab <- function(index, columns, values = NULL, rownames = NULL, colnam stop( sprintf("Unsupported aggregation function provided: '%s'. Allowed functions are: mean, median, sum, std, count, mode.", aggfunc), call. = FALSE - )} + ) + } # 2. Check if Python threw the missing values column error else if (grepl("If you pass an aggregation function to crosstab", e$message)) { stop( "If you pass an aggregation function to crosstab, you must also specify a single values column to aggregate over.", call. = FALSE ) - } + } # any other unexpected errors else { stop(e) # nocov diff --git a/tests/testthat/test-acro_crosstab.R b/tests/testthat/test-acro_crosstab.R index 8bbc35c..3bd1ee7 100644 --- a/tests/testthat/test-acro_crosstab.R +++ b/tests/testthat/test-acro_crosstab.R @@ -69,4 +69,3 @@ test_that("acro_crosstab throws an error for missing values", { "If you pass an aggregation function to crosstab" ) }) -