From f77f84a01acf45b8ae0f40ff5202c6ed67e702c2 Mon Sep 17 00:00:00 2001 From: Davis Vaughan Date: Fri, 23 Oct 2026 20:55:52 -0400 Subject: [PATCH] Accept titles for `axis` in the remaining one axis functions --- R/locate.R | 7 +++- R/names.R | 14 ++++++- R/rep.R | 7 +++- R/roll.R | 7 +++- R/slice.R | 7 +++- R/split.R | 7 +++- R/titles.R | 7 +++- R/unstack.R | 7 +++- man/rray-locate.Rd | 7 +++- man/rray-rep.Rd | 7 +++- man/rray-roll.Rd | 7 +++- man/rray-titles.Rd | 7 +++- man/rray_axis_names.Rd | 7 +++- man/rray_set_axis_names.Rd | 7 +++- man/rray_slice.Rd | 7 +++- man/rray_split.Rd | 7 +++- man/rray_unstack.Rd | 7 +++- plans/title-as-axis.md | 16 ++++---- src/decl/locate-decl.h | 2 +- src/globals.c | 8 ++++ src/globals.h | 13 +++++++ src/init.c | 2 + src/locate.c | 29 +++++++------- src/locate.h | 4 +- src/names.c | 57 +++++++++++++++++++-------- src/names.h | 4 +- src/rep-each.c | 23 +++++++---- src/rep-each.h | 2 +- src/roll-each.c | 29 +++++++++----- src/roll-each.h | 2 +- src/slice-axis.c | 66 ++++++++++++++++++++++++-------- src/slice-axis.h | 4 +- src/split.c | 32 +++++++++------- src/split.h | 2 +- src/stack.c | 4 +- src/titles.c | 31 ++++++++++----- src/titles.h | 2 +- src/unstack.c | 20 ++++++---- src/unstack.h | 2 +- tests/testthat/_snaps/locate.md | 18 ++++++++- tests/testthat/_snaps/names.md | 36 ++++++++++++++++- tests/testthat/_snaps/rep.md | 8 ++++ tests/testthat/_snaps/roll.md | 10 ++++- tests/testthat/_snaps/slice.md | 12 +++++- tests/testthat/_snaps/split.md | 10 ++++- tests/testthat/_snaps/titles.md | 16 ++++++++ tests/testthat/_snaps/unstack.md | 10 ++++- tests/testthat/test-locate.R | 18 +++++++++ tests/testthat/test-names.R | 30 +++++++++++++++ tests/testthat/test-rep.R | 9 +++++ tests/testthat/test-roll.R | 9 +++++ tests/testthat/test-slice.R | 36 +++++++++++++++++ tests/testthat/test-split.R | 9 +++++ tests/testthat/test-titles.R | 17 ++++++++ tests/testthat/test-unstack.R | 6 +++ 55 files changed, 595 insertions(+), 139 deletions(-) create mode 100644 src/globals.c create mode 100644 src/globals.h diff --git a/R/locate.R b/R/locate.R index 57abc0dd..49dd551f 100644 --- a/R/locate.R +++ b/R/locate.R @@ -34,7 +34,8 @@ #' #' @param x An array. #' -#' @param axis A single integer giving the axis to locate along. +#' @param axis The axis to locate along, given +#' [by position or by title][faq-axes]. #' #' @param na_rm If `TRUE`, missing values are skipped. #' @@ -51,6 +52,10 @@ #' #' # Position of the minimum going across the columns #' rray_locate_min(x, axis = 2L) +#' +#' # Position of the minimum going across the columns, by title +#' y <- rray_set_titles(x, c("row", "col")) +#' rray_locate_min(y, axis = "col") NULL #' @rdname rray-locate diff --git a/R/names.R b/R/names.R index d5771bbe..652272fb 100644 --- a/R/names.R +++ b/R/names.R @@ -37,7 +37,8 @@ rray_names <- function(x) { #' #' @param x An array. #' -#' @param axis A single integer. The axis to get names for. +#' @param axis The axis to get names for, given +#' [by position or by title][faq-axes]. #' #' @returns #' A character vector of names, or `NULL` if that axis has no names. @@ -49,6 +50,10 @@ rray_names <- function(x) { #' rray_axis_names(x, 1) #' rray_axis_names(x, 2) #' +#' # By title +#' y <- rray_set_titles(x, c("row", "col")) +#' rray_axis_names(y, "col") +#' #' rray_row_names(x) #' rray_column_names(x) rray_axis_names <- function(x, axis) { @@ -105,7 +110,8 @@ rray_set_names <- function(x, names) { #' #' @param x An array. #' -#' @param axis A single integer. The axis to set names for. +#' @param axis The axis to set names for, given +#' [by position or by title][faq-axes]. #' #' @param names A character vector of names for `axis`, the same length as #' the dimension of `axis`. Can also be `NULL` to remove names from `axis`. @@ -120,6 +126,10 @@ rray_set_names <- function(x, names) { #' rray_set_axis_names(x, 1, c("r1", "r2")) #' rray_set_axis_names(x, 2, c("c1", "c2", "c3")) #' +#' # By title +#' y <- rray_set_titles(x, c("row", "col")) +#' rray_set_axis_names(y, "col", c("c1", "c2", "c3")) +#' #' rray_set_row_names(x, c("r1", "r2")) #' rray_set_column_names(x, c("c1", "c2", "c3")) rray_set_axis_names <- function(x, axis, names) { diff --git a/R/rep.R b/R/rep.R index 15213037..57a142c4 100644 --- a/R/rep.R +++ b/R/rep.R @@ -31,7 +31,8 @@ #' #' @param axes An integer vector of axes to repeat along. #' -#' @param axis A single integer representing the axis to repeat along. +#' @param axis The axis to repeat along, given +#' [by position or by title][faq-axes]. #' #' @returns #' An array with the same dimensions as `x`, except along the repeated axes. @@ -58,6 +59,10 @@ #' # A different number of repeats per row #' rray_rep_each(x, times = c(1, 2, 3), axis = 1) #' +#' # A different number of repeats per row, by title +#' z <- rray_set_titles(x, c("row", "col")) +#' rray_rep_each(z, times = c(1, 2, 3), axis = "row") +#' #' # Zeroes drop slices entirely #' rray_rep_each(x, times = c(1, 0, 2), axis = 1) #' diff --git a/R/roll.R b/R/roll.R index 9809231a..be729a58 100644 --- a/R/roll.R +++ b/R/roll.R @@ -226,7 +226,8 @@ #' #' @param axes An integer vector of axes to roll along. #' -#' @param axis A single integer representing the axis to roll along. +#' @param axis The axis to roll along, given +#' [by position or by title][faq-axes]. #' #' @returns #' An array with the same type and dimensions as `x`. @@ -261,6 +262,10 @@ #' # Roll each row by its own `n`, given as a one column matrix #' rray_roll_each(y, n = matrix(c(1, 0, -1), ncol = 1), axis = 2) #' +#' # Roll each row by its own `n`, by title +#' z <- rray_set_titles(y, c("row", "col")) +#' rray_roll_each(z, n = matrix(c(1, 0, -1), ncol = 1), axis = "col") +#' #' # Roll each column by its own `n`, given as a one row matrix #' rray_roll_each(y, n = matrix(c(0, 1, 2, 3), nrow = 1), axis = 1) #' diff --git a/R/slice.R b/R/slice.R index becba2a4..91aa71a0 100644 --- a/R/slice.R +++ b/R/slice.R @@ -58,7 +58,8 @@ #' @param i A subscript for the selected axis. It takes any of the forms #' allowed in `...`. #' -#' @param axis A single integer. The axis to slice along. +#' @param axis The axis to slice along, given +#' [by position or by title][faq-axes]. #' #' @param value An array to assign to the selected positions. It is cast to #' the type of `x`, then broadcast to the dimensions of the selection. @@ -100,6 +101,10 @@ #' rray_slice_rows(y, c("b", "a")) #' rray_slice_columns(x, -2) #' +#' # Slice a single axis, by title +#' z <- rray_set_titles(y, c("row", "col")) +#' rray_slice_axis(z, c("e", "c"), axis = "col") +#' #' # Assign one value to every first row #' rray_slice_assign(x, 1, TRUE, TRUE, value = 0L) #' rray_slice_assign_rows(x, 1, 0L) diff --git a/R/split.R b/R/split.R index 97a87998..31cec7a3 100644 --- a/R/split.R +++ b/R/split.R @@ -9,7 +9,8 @@ #' #' @param x An array. #' -#' @param axis A single integer representing the axis to split on. +#' @param axis The axis to split along, given +#' [by position or by title][faq-axes]. #' #' @param dimensions One of: #' @@ -35,6 +36,10 @@ #' # One array per column #' rray_split(x, axis = 2, dimensions = 1) #' +#' # One array per column, by title +#' y <- rray_set_titles(x, c("row", "col")) +#' rray_split(y, axis = "col", dimensions = 1) +#' #' # Arrays of dimension zero are allowed in the explicit form #' rray_split(x, axis = 2, dimensions = c(0, 2)) #' diff --git a/R/titles.R b/R/titles.R index ffce5dfe..0909197a 100644 --- a/R/titles.R +++ b/R/titles.R @@ -14,7 +14,11 @@ #' #' @param x An array. #' -#' @param axis A single integer. The axis to get or set the title for. +#' @param axis The axis to get or set the title for. +#' +#' - For `rray_set_axis_title()`, given [by position or by title][faq-axes]. +#' +#' - For `rray_axis_title()`, given [by position][faq-axes]. #' #' @param titles A character vector with one title per axis, or `NULL` to #' remove all titles. @@ -38,6 +42,7 @@ #' rray_titles(x) #' rray_axis_title(x, 2) #' rray_set_axis_title(x, 1, NULL) +#' rray_set_axis_title(x, "Row", "R") NULL #' @rdname rray-titles diff --git a/R/unstack.R b/R/unstack.R index 761a4e89..44c6c322 100644 --- a/R/unstack.R +++ b/R/unstack.R @@ -18,7 +18,8 @@ #' #' @param x An array. #' -#' @param axis A single integer representing the axis to unstack along. +#' @param axis The axis to unstack along, given +#' [by position or by title][faq-axes]. #' #' @returns #' A list of arrays with the same dimensions as `x`, except that `axis` has @@ -36,6 +37,10 @@ #' # One array per column #' rray_unstack(x, axis = 2) #' +#' # One array per column, by title +#' z <- rray_set_titles(x, c("row", "col")) +#' rray_unstack(z, axis = "col") +#' #' # Names along the axis become list names #' y <- array(1:4, c(2, 2), dimnames = list(c("a", "b"), c("x", "y"))) #' rray_unstack(y, axis = 2) diff --git a/man/rray-locate.Rd b/man/rray-locate.Rd index e134c095..4161ab4c 100644 --- a/man/rray-locate.Rd +++ b/man/rray-locate.Rd @@ -15,7 +15,8 @@ rray_locate_min(x, ..., axis, na_rm = FALSE) \item{...}{These dots are for future extensions and must be empty.} -\item{axis}{A single integer giving the axis to locate along.} +\item{axis}{The axis to locate along, given +\link[=faq-axes]{by position or by title}.} \item{na_rm}{If \code{TRUE}, missing values are skipped.} } @@ -59,4 +60,8 @@ rray_locate_max(x, axis = 1L) # Position of the minimum going across the columns rray_locate_min(x, axis = 2L) + +# Position of the minimum going across the columns, by title +y <- rray_set_titles(x, c("row", "col")) +rray_locate_min(y, axis = "col") } diff --git a/man/rray-rep.Rd b/man/rray-rep.Rd index f06d728e..3167aff0 100644 --- a/man/rray-rep.Rd +++ b/man/rray-rep.Rd @@ -23,7 +23,8 @@ For \code{rray_rep_each()}, a vector of integers greater than or equal to \item{axes}{An integer vector of axes to repeat along.} -\item{axis}{A single integer representing the axis to repeat along.} +\item{axis}{The axis to repeat along, given +\link[=faq-axes]{by position or by title}.} } \value{ An array with the same dimensions as \code{x}, except along the repeated axes. @@ -69,6 +70,10 @@ rray_rep(x, times = c(2, 3), axes = c(1, 2)) # A different number of repeats per row rray_rep_each(x, times = c(1, 2, 3), axis = 1) +# A different number of repeats per row, by title +z <- rray_set_titles(x, c("row", "col")) +rray_rep_each(z, times = c(1, 2, 3), axis = "row") + # Zeroes drop slices entirely rray_rep_each(x, times = c(1, 0, 2), axis = 1) diff --git a/man/rray-roll.Rd b/man/rray-roll.Rd index d2a650bd..7fe5a939 100644 --- a/man/rray-roll.Rd +++ b/man/rray-roll.Rd @@ -24,7 +24,8 @@ dimensions of \code{x} with \code{axis} set to 1.} \item{axes}{An integer vector of axes to roll along.} -\item{axis}{A single integer representing the axis to roll along.} +\item{axis}{The axis to roll along, given +\link[=faq-axes]{by position or by title}.} } \value{ An array with the same type and dimensions as \code{x}. @@ -269,6 +270,10 @@ y <- matrix(1:12, nrow = 3, byrow = TRUE) # Roll each row by its own `n`, given as a one column matrix rray_roll_each(y, n = matrix(c(1, 0, -1), ncol = 1), axis = 2) +# Roll each row by its own `n`, by title +z <- rray_set_titles(y, c("row", "col")) +rray_roll_each(z, n = matrix(c(1, 0, -1), ncol = 1), axis = "col") + # Roll each column by its own `n`, given as a one row matrix rray_roll_each(y, n = matrix(c(0, 1, 2, 3), nrow = 1), axis = 1) diff --git a/man/rray-titles.Rd b/man/rray-titles.Rd index 64d6f93d..12258b71 100644 --- a/man/rray-titles.Rd +++ b/man/rray-titles.Rd @@ -19,7 +19,11 @@ rray_set_axis_title(x, axis, title) \arguments{ \item{x}{An array.} -\item{axis}{A single integer. The axis to get or set the title for.} +\item{axis}{The axis to get or set the title for. +\itemize{ +\item For \code{rray_set_axis_title()}, given \link[=faq-axes]{by position or by title}. +\item For \code{rray_axis_title()}, given \link[=faq-axes]{by position}. +}} \item{titles}{A character vector with one title per axis, or \code{NULL} to remove all titles.} @@ -54,4 +58,5 @@ x <- rray_set_titles(x, c("Row", "Column")) rray_titles(x) rray_axis_title(x, 2) rray_set_axis_title(x, 1, NULL) +rray_set_axis_title(x, "Row", "R") } diff --git a/man/rray_axis_names.Rd b/man/rray_axis_names.Rd index 3121bf75..e91a7e67 100644 --- a/man/rray_axis_names.Rd +++ b/man/rray_axis_names.Rd @@ -15,7 +15,8 @@ rray_column_names(x) \arguments{ \item{x}{An array.} -\item{axis}{A single integer. The axis to get names for.} +\item{axis}{The axis to get names for, given +\link[=faq-axes]{by position or by title}.} } \value{ A character vector of names, or \code{NULL} if that axis has no names. @@ -34,6 +35,10 @@ x <- array(1:6, c(2, 3), dimnames = list(c("r1", "r2"), c("c1", "c2", "c3"))) rray_axis_names(x, 1) rray_axis_names(x, 2) +# By title +y <- rray_set_titles(x, c("row", "col")) +rray_axis_names(y, "col") + rray_row_names(x) rray_column_names(x) } diff --git a/man/rray_set_axis_names.Rd b/man/rray_set_axis_names.Rd index 507b4861..6c82908b 100644 --- a/man/rray_set_axis_names.Rd +++ b/man/rray_set_axis_names.Rd @@ -15,7 +15,8 @@ rray_set_column_names(x, names) \arguments{ \item{x}{An array.} -\item{axis}{A single integer. The axis to set names for.} +\item{axis}{The axis to set names for, given +\link[=faq-axes]{by position or by title}.} \item{names}{A character vector of names for \code{axis}, the same length as the dimension of \code{axis}. Can also be \code{NULL} to remove names from \code{axis}.} @@ -37,6 +38,10 @@ x <- array(1:6, c(2, 3)) rray_set_axis_names(x, 1, c("r1", "r2")) rray_set_axis_names(x, 2, c("c1", "c2", "c3")) +# By title +y <- rray_set_titles(x, c("row", "col")) +rray_set_axis_names(y, "col", c("c1", "c2", "c3")) + rray_set_row_names(x, c("r1", "r2")) rray_set_column_names(x, c("c1", "c2", "c3")) } diff --git a/man/rray_slice.Rd b/man/rray_slice.Rd index b1fd1549..03f6b720 100644 --- a/man/rray_slice.Rd +++ b/man/rray_slice.Rd @@ -48,7 +48,8 @@ empty.} \item{i}{A subscript for the selected axis. It takes any of the forms allowed in \code{...}.} -\item{axis}{A single integer. The axis to slice along.} +\item{axis}{The axis to slice along, given +\link[=faq-axes]{by position or by title}.} \item{value}{An array to assign to the selected positions. It is cast to the type of \code{x}, then broadcast to the dimensions of the selection.} @@ -124,6 +125,10 @@ rray_slice_axis(x, c(4, 1), axis = 3) rray_slice_rows(y, c("b", "a")) rray_slice_columns(x, -2) +# Slice a single axis, by title +z <- rray_set_titles(y, c("row", "col")) +rray_slice_axis(z, c("e", "c"), axis = "col") + # Assign one value to every first row rray_slice_assign(x, 1, TRUE, TRUE, value = 0L) rray_slice_assign_rows(x, 1, 0L) diff --git a/man/rray_split.Rd b/man/rray_split.Rd index edcaa6af..dac0d6bc 100644 --- a/man/rray_split.Rd +++ b/man/rray_split.Rd @@ -11,7 +11,8 @@ rray_split(x, ..., axis, dimensions) \item{...}{These dots are for future extensions and must be empty.} -\item{axis}{A single integer representing the axis to split on.} +\item{axis}{The axis to split along, given +\link[=faq-axes]{by position or by title}.} \item{dimensions}{One of: \itemize{ @@ -41,6 +42,10 @@ rray_split(x, axis = 1, dimensions = c(1, 5)) # One array per column rray_split(x, axis = 2, dimensions = 1) +# One array per column, by title +y <- rray_set_titles(x, c("row", "col")) +rray_split(y, axis = "col", dimensions = 1) + # Arrays of dimension zero are allowed in the explicit form rray_split(x, axis = 2, dimensions = c(0, 2)) diff --git a/man/rray_unstack.Rd b/man/rray_unstack.Rd index 773a35f6..c55030a2 100644 --- a/man/rray_unstack.Rd +++ b/man/rray_unstack.Rd @@ -11,7 +11,8 @@ rray_unstack(x, ..., axis) \item{...}{These dots are for future extensions and must be empty.} -\item{axis}{A single integer representing the axis to unstack along.} +\item{axis}{The axis to unstack along, given +\link[=faq-axes]{by position or by title}.} } \value{ A list of arrays with the same dimensions as \code{x}, except that \code{axis} has @@ -40,6 +41,10 @@ rray_unstack(x, axis = 1) # One array per column rray_unstack(x, axis = 2) +# One array per column, by title +z <- rray_set_titles(x, c("row", "col")) +rray_unstack(z, axis = "col") + # Names along the axis become list names y <- array(1:4, c(2, 2), dimnames = list(c("a", "b"), c("x", "y"))) rray_unstack(y, axis = 2) diff --git a/plans/title-as-axis.md b/plans/title-as-axis.md index 9b6324c8..ea85320f 100644 --- a/plans/title-as-axis.md +++ b/plans/title-as-axis.md @@ -574,21 +574,21 @@ Each top level item is one PR. Each PR runs `clang-format`, `air format .`, - [x] Add `faq-axes` and update the reduce and cumulative docs. -- [ ] PR 3: Titles for one axis functions. +- [x] PR 3: Titles for one axis functions. - - [ ] Locate. + - [x] Locate. - - [ ] Rep each and roll each. + - [x] Rep each and roll each. - - [ ] Slice axis and slice assign axis. + - [x] Slice axis and slice assign axis. - - [ ] Split and unstack. + - [x] Split and unstack. - - [ ] Names: `rray_axis_names()`, `rray_set_axis_names()`. + - [x] Names: `rray_axis_names()`, `rray_set_axis_names()`. - - [ ] Titles: `rray_set_axis_title()`. + - [x] Titles: `rray_set_axis_title()`. - - [ ] Docs for each. + - [x] Docs for each. - [ ] PR 4: Titles for multi axis functions. diff --git a/src/decl/locate-decl.h b/src/decl/locate-decl.h index 6f4b943f..1c745661 100644 --- a/src/decl/locate-decl.h +++ b/src/decl/locate-decl.h @@ -1,6 +1,6 @@ static r_obj* rray_locate( r_obj* x, - int axis, + r_obj* axis, bool na_rm, enum rray_locate_op op, struct rray_arg* arg, diff --git a/src/globals.c b/src/globals.c new file mode 100644 index 00000000..0cafaf4b --- /dev/null +++ b/src/globals.c @@ -0,0 +1,8 @@ +#include "globals.h" + +struct rray_globals rray_globals; + +void rray_init_globals(r_obj* ns) { + r_preserve_global(rray_globals.one_int = r_int(1)); + r_preserve_global(rray_globals.two_int = r_int(2)); +} diff --git a/src/globals.h b/src/globals.h new file mode 100644 index 00000000..88bb15f2 --- /dev/null +++ b/src/globals.h @@ -0,0 +1,13 @@ +#ifndef RRAY_GLOBALS_H +#define RRAY_GLOBALS_H + +#include "rlang.h" + +struct rray_globals { + r_obj* one_int; + r_obj* two_int; +}; + +extern struct rray_globals rray_globals; + +#endif diff --git a/src/init.c b/src/init.c index ccfab598..ccf6b7d0 100644 --- a/src/init.c +++ b/src/init.c @@ -510,10 +510,12 @@ void R_init_rray(DllInfo* dll) { } extern void rray_init_args(r_obj* ns); +extern void rray_init_globals(r_obj* ns); extern void rray_init_syms(r_obj* ns); r_obj* ffi_rray_init_library(r_obj* ffi_ns) { r_init_library(ffi_ns); + rray_init_globals(ffi_ns); rray_init_syms(ffi_ns); rray_init_args(ffi_ns); return r_null; diff --git a/src/locate.c b/src/locate.c index af634a6e..5c4141d8 100644 --- a/src/locate.c +++ b/src/locate.c @@ -11,6 +11,7 @@ #include "size.h" #include "strided-iterator.h" #include "strides.h" +#include "titles.h" #include "type.h" #include "utils.h" @@ -37,14 +38,13 @@ r_obj* ffi_rray_locate_max( r_obj* ffi_frame ) { struct r_lazy error_call = {.x = ffi_frame, .env = r_null}; - const int axis = arg_as_int(ffi_axis, rray_args.axis, error_call); const bool na_rm = r_arg_as_bool(ffi_na_rm, "na_rm"); - return rray_locate_max(ffi_x, axis, na_rm, rray_args.x, error_call); + return rray_locate_max(ffi_x, ffi_axis, na_rm, rray_args.x, error_call); } r_obj* rray_locate_max( r_obj* x, - int axis, + r_obj* axis, bool na_rm, struct rray_arg* arg, struct r_lazy error_call @@ -59,14 +59,13 @@ r_obj* ffi_rray_locate_min( r_obj* ffi_frame ) { struct r_lazy error_call = {.x = ffi_frame, .env = r_null}; - const int axis = arg_as_int(ffi_axis, rray_args.axis, error_call); const bool na_rm = r_arg_as_bool(ffi_na_rm, "na_rm"); - return rray_locate_min(ffi_x, axis, na_rm, rray_args.x, error_call); + return rray_locate_min(ffi_x, ffi_axis, na_rm, rray_args.x, error_call); } r_obj* rray_locate_min( r_obj* x, - int axis, + r_obj* axis, bool na_rm, struct rray_arg* arg, struct r_lazy error_call @@ -76,7 +75,7 @@ r_obj* rray_locate_min( static r_obj* rray_locate( r_obj* x, - int axis, + r_obj* axis, bool na_rm, enum rray_locate_op op, struct rray_arg* arg, @@ -91,28 +90,32 @@ static r_obj* rray_locate( const int dimensionality = rray_dimensionality_from_dimensions(x_dimensions); check_dimensionality(dimensionality); - check_axis(axis, dimensionality, rray_args.axis, error_call); + r_obj* titles = KEEP(rray_titles(x, arg, error_call)); + + const int c_axis = + arg_as_axis(axis, titles, dimensionality, rray_args.axis, arg, error_call); const rray_locate_fn fn = rray_locate_switch(x, op, arg, error_call); r_obj* out_dimensions = KEEP(r_alloc_integer(dimensionality)); int* v_out_dimensions = r_int_begin(out_dimensions); r_memcpy(v_out_dimensions, v_x_dimensions, sizeof(int) * dimensionality); - v_out_dimensions[axis - 1] = 1; + v_out_dimensions[c_axis - 1] = 1; - r_obj* out = - KEEP(fn(x, na_rm, v_x_dimensions, v_out_dimensions, dimensionality, axis)); + r_obj* out = KEEP( + fn(x, na_rm, v_x_dimensions, v_out_dimensions, dimensionality, c_axis) + ); r_attrib_poke_dim(out, out_dimensions); - r_obj* axes = KEEP(r_int(axis)); + r_obj* axes = KEEP(r_int(c_axis)); r_obj* out_names = KEEP(rray_reduce_names(x, axes)); if (out_names != r_null) { r_attrib_poke_dim_names(out, out_names); } - FREE(6); + FREE(7); return out; } diff --git a/src/locate.h b/src/locate.h index c6cbbccf..ea47abe4 100644 --- a/src/locate.h +++ b/src/locate.h @@ -7,7 +7,7 @@ r_obj* rray_locate_max( r_obj* x, - int axis, + r_obj* axis, bool na_rm, struct rray_arg* arg, struct r_lazy error_call @@ -15,7 +15,7 @@ r_obj* rray_locate_max( r_obj* rray_locate_min( r_obj* x, - int axis, + r_obj* axis, bool na_rm, struct rray_arg* arg, struct r_lazy error_call diff --git a/src/names.c b/src/names.c index 452deda4..3e1f0865 100644 --- a/src/names.c +++ b/src/names.c @@ -3,6 +3,7 @@ #include "axes.h" #include "dimensionality.h" #include "dimensions.h" +#include "globals.h" #include "utils.h" #include "view.h" @@ -23,23 +24,22 @@ r_obj* rray_names(r_obj* x, struct rray_arg* arg, struct r_lazy error_call) { r_obj* ffi_rray_axis_names(r_obj* ffi_x, r_obj* ffi_axis, r_obj* ffi_frame) { struct r_lazy error_call = {.x = ffi_frame, .env = r_null}; - const int axis = arg_as_int(ffi_axis, rray_args.axis, error_call); - return rray_axis_names(ffi_x, axis, rray_args.x, error_call); + return rray_axis_names(ffi_x, ffi_axis, rray_args.x, error_call); } r_obj* ffi_rray_row_names(r_obj* ffi_x, r_obj* ffi_frame) { struct r_lazy error_call = {.x = ffi_frame, .env = r_null}; - return rray_axis_names(ffi_x, 1, rray_args.x, error_call); + return rray_axis_names(ffi_x, rray_globals.one_int, rray_args.x, error_call); } r_obj* ffi_rray_column_names(r_obj* ffi_x, r_obj* ffi_frame) { struct r_lazy error_call = {.x = ffi_frame, .env = r_null}; - return rray_axis_names(ffi_x, 2, rray_args.x, error_call); + return rray_axis_names(ffi_x, rray_globals.two_int, rray_args.x, error_call); } r_obj* rray_axis_names( r_obj* x, - int axis, + r_obj* axis, struct rray_arg* arg, struct r_lazy error_call ) { @@ -47,11 +47,14 @@ r_obj* rray_axis_names( x = KEEP(arg_as_array(x, arg, error_call)); const int dimensionality = rray_dimensionality(x, arg, error_call); - check_axis(axis, dimensionality, rray_args.axis, error_call); r_obj* names = rray_names(x, arg, error_call); + r_obj* titles = (names == r_null) ? r_null : r_names(names); + + const int c_axis = + arg_as_axis(axis, titles, dimensionality, rray_args.axis, arg, error_call); - r_obj* out = (names == r_null) ? r_null : r_list_get(names, axis - 1); + r_obj* out = (names == r_null) ? r_null : r_list_get(names, c_axis - 1); FREE(1); return out; @@ -120,8 +123,13 @@ r_obj* ffi_rray_set_axis_names( r_obj* ffi_frame ) { struct r_lazy error_call = {.x = ffi_frame, .env = r_null}; - const int axis = arg_as_int(ffi_axis, rray_args.axis, error_call); - return rray_set_axis_names(ffi_x, axis, ffi_names, rray_args.x, error_call); + return rray_set_axis_names( + ffi_x, + ffi_axis, + ffi_names, + rray_args.x, + error_call + ); } r_obj* ffi_rray_set_row_names( @@ -130,7 +138,13 @@ r_obj* ffi_rray_set_row_names( r_obj* ffi_frame ) { struct r_lazy error_call = {.x = ffi_frame, .env = r_null}; - return rray_set_axis_names(ffi_x, 1, ffi_names, rray_args.x, error_call); + return rray_set_axis_names( + ffi_x, + rray_globals.one_int, + ffi_names, + rray_args.x, + error_call + ); } r_obj* ffi_rray_set_column_names( @@ -139,12 +153,18 @@ r_obj* ffi_rray_set_column_names( r_obj* ffi_frame ) { struct r_lazy error_call = {.x = ffi_frame, .env = r_null}; - return rray_set_axis_names(ffi_x, 2, ffi_names, rray_args.x, error_call); + return rray_set_axis_names( + ffi_x, + rray_globals.two_int, + ffi_names, + rray_args.x, + error_call + ); } r_obj* rray_set_axis_names( r_obj* x, - int axis, + r_obj* axis, r_obj* names, struct rray_arg* arg, struct r_lazy error_call @@ -153,17 +173,20 @@ r_obj* rray_set_axis_names( x = KEEP(arg_as_array(x, arg, error_call)); const int dimensionality = rray_dimensionality(x, arg, error_call); - check_axis(axis, dimensionality, rray_args.axis, error_call); - - const int dimension = rray_dimension(x, axis, arg, error_call); - check_axis_names(names, axis, dimension, error_call); r_obj* old_names = r_dim_names(x); + r_obj* titles = (old_names == r_null) ? r_null : r_names(old_names); + + const int c_axis = + arg_as_axis(axis, titles, dimensionality, rray_args.axis, arg, error_call); + + const int dimension = rray_dimension(x, c_axis, arg, error_call); + check_axis_names(names, c_axis, dimension, error_call); r_obj* new_names = KEEP( old_names == r_null ? r_alloc_list(dimensionality) : r_clone(old_names) ); - r_list_poke(new_names, axis - 1, names); + r_list_poke(new_names, c_axis - 1, names); r_obj* out = KEEP(r_view(x)); r_attrib_poke_dim(out, r_dim(x)); diff --git a/src/names.h b/src/names.h index bf80a112..d34b3e89 100644 --- a/src/names.h +++ b/src/names.h @@ -9,7 +9,7 @@ r_obj* rray_names(r_obj* x, struct rray_arg* arg, struct r_lazy error_call); r_obj* rray_axis_names( r_obj* x, - int axis, + r_obj* axis, struct rray_arg* arg, struct r_lazy error_call ); @@ -23,7 +23,7 @@ r_obj* rray_set_names( r_obj* rray_set_axis_names( r_obj* x, - int axis, + r_obj* axis, r_obj* names, struct rray_arg* arg, struct r_lazy error_call diff --git a/src/rep-each.c b/src/rep-each.c index dcc1619a..9c655469 100644 --- a/src/rep-each.c +++ b/src/rep-each.c @@ -6,6 +6,7 @@ #include "dimensionality.h" #include "rep.h" #include "slice.h" +#include "titles.h" #include "utils.h" #include "decl/rep-each-decl.h" @@ -17,14 +18,13 @@ r_obj* ffi_rray_rep_each( r_obj* ffi_frame ) { struct r_lazy error_call = {.x = ffi_frame, .env = r_null}; - const int axis = arg_as_int(ffi_axis, rray_args.axis, error_call); - return rray_rep_each(ffi_x, ffi_times, axis, rray_args.x, error_call); + return rray_rep_each(ffi_x, ffi_times, ffi_axis, rray_args.x, error_call); } r_obj* rray_rep_each( r_obj* x, r_obj* times, - int axis, + r_obj* axis, struct rray_arg* x_arg, struct r_lazy error_call ) { @@ -36,8 +36,17 @@ r_obj* rray_rep_each( const int dimensionality = rray_dimensionality_from_dimensions(x_dimensions); check_dimensionality(dimensionality); - check_axis(axis, dimensionality, rray_args.axis, error_call); - const int axis_dimension = v_x_dimensions[axis - 1]; + r_obj* titles = KEEP(rray_titles(x, x_arg, error_call)); + + const int c_axis = arg_as_axis( + axis, + titles, + dimensionality, + rray_args.axis, + x_arg, + error_call + ); + const int axis_dimension = v_x_dimensions[c_axis - 1]; times = KEEP( arg_as_rep_each_times(times, axis_dimension, rray_args.times, error_call) @@ -56,13 +65,13 @@ r_obj* rray_rep_each( r_list_poke( indices, - axis - 1, + c_axis - 1, rray_rep_each_locations(axis_dimension, out_dimension, v_times, times_size) ); r_obj* out = rray_slice(x, indices, x_arg, rray_args.empty, error_call); - FREE(3); + FREE(4); return out; } diff --git a/src/rep-each.h b/src/rep-each.h index 5fd3e2f2..a96e322a 100644 --- a/src/rep-each.h +++ b/src/rep-each.h @@ -8,7 +8,7 @@ r_obj* rray_rep_each( r_obj* x, r_obj* times, - int axis, + r_obj* axis, struct rray_arg* x_arg, struct r_lazy error_call ); diff --git a/src/roll-each.c b/src/roll-each.c index c460b3a0..dd86b6f6 100644 --- a/src/roll-each.c +++ b/src/roll-each.c @@ -7,6 +7,7 @@ #include "reduce-names.h" #include "roll.h" #include "size.h" +#include "titles.h" #include "utils.h" #include "decl/roll-each-decl.h" @@ -18,14 +19,13 @@ r_obj* ffi_rray_roll_each( r_obj* ffi_frame ) { struct r_lazy error_call = {.x = ffi_frame, .env = r_null}; - const int axis = arg_as_int(ffi_axis, rray_args.axis, error_call); - return rray_roll_each(ffi_x, ffi_n, axis, rray_args.x, error_call); + return rray_roll_each(ffi_x, ffi_n, ffi_axis, rray_args.x, error_call); } r_obj* rray_roll_each( r_obj* x, r_obj* n, - int axis, + r_obj* axis, struct rray_arg* x_arg, struct r_lazy error_call ) { @@ -37,15 +37,24 @@ r_obj* rray_roll_each( const int dimensionality = rray_dimensionality_from_dimensions(x_dimensions); check_dimensionality(dimensionality); - check_axis(axis, dimensionality, rray_args.axis, error_call); - const int axis_dimension = v_x_dimensions[axis - 1]; + r_obj* titles = KEEP(rray_titles(x, x_arg, error_call)); + + const int c_axis = arg_as_axis( + axis, + titles, + dimensionality, + rray_args.axis, + x_arg, + error_call + ); + const int axis_dimension = v_x_dimensions[c_axis - 1]; n = KEEP(arg_as_roll_each_n(n, rray_args.n, error_call)); n = KEEP(rray_roll_each_normalize(n, axis_dimension)); r_obj* n_broadcast_dimensions = KEEP(r_clone(x_dimensions)); int* v_n_broadcast_dimensions = r_int_begin(n_broadcast_dimensions); - v_n_broadcast_dimensions[axis - 1] = 1; + v_n_broadcast_dimensions[c_axis - 1] = 1; // Nicely simplifies the loop to explicitly broadcast `n`. Cost is fairly // cheap, since one dimension of `x` is set to 1. @@ -59,12 +68,12 @@ r_obj* rray_roll_each( r_attrib_poke_dim(out, x_dimensions); r_ssize block_size = 1; - for (int i = 0; i < axis - 1; ++i) { + for (int i = 0; i < c_axis - 1; ++i) { block_size *= v_x_dimensions[i]; } r_ssize n_groups = 1; - for (int i = axis; i < dimensionality; ++i) { + for (int i = c_axis; i < dimensionality; ++i) { n_groups *= v_x_dimensions[i]; } @@ -72,14 +81,14 @@ r_obj* rray_roll_each( // Using `rray_reduce_names()` is an easy way to clear the `axis` names, which // likely no longer make sense - r_obj* axes = KEEP(r_int(axis)); + r_obj* axes = KEEP(r_int(c_axis)); r_obj* out_names = KEEP(rray_reduce_names(x, axes)); if (out_names != r_null) { r_attrib_poke_dim_names(out, out_names); } - FREE(8); + FREE(9); return out; } diff --git a/src/roll-each.h b/src/roll-each.h index c214d8ae..7c0484f0 100644 --- a/src/roll-each.h +++ b/src/roll-each.h @@ -8,7 +8,7 @@ r_obj* rray_roll_each( r_obj* x, r_obj* n, - int axis, + r_obj* axis, struct rray_arg* x_arg, struct r_lazy error_call ); diff --git a/src/slice-axis.c b/src/slice-axis.c index c1c3a606..29bacb8f 100644 --- a/src/slice-axis.c +++ b/src/slice-axis.c @@ -2,8 +2,10 @@ #include "axes.h" #include "dimensionality.h" +#include "globals.h" #include "slice-assign.h" #include "slice.h" +#include "titles.h" #include "utils.h" #include "decl/slice-axis-decl.h" @@ -15,11 +17,10 @@ r_obj* ffi_rray_slice_axis( r_obj* ffi_frame ) { struct r_lazy error_call = {.x = ffi_frame, .env = r_null}; - const int axis = arg_as_int(ffi_axis, rray_args.axis, error_call); return rray_slice_axis( ffi_x, ffi_i, - axis, + ffi_axis, rray_args.x, rray_args.i, error_call @@ -28,18 +29,32 @@ r_obj* ffi_rray_slice_axis( r_obj* ffi_rray_slice_rows(r_obj* ffi_x, r_obj* ffi_i, r_obj* ffi_frame) { struct r_lazy error_call = {.x = ffi_frame, .env = r_null}; - return rray_slice_axis(ffi_x, ffi_i, 1, rray_args.x, rray_args.i, error_call); + return rray_slice_axis( + ffi_x, + ffi_i, + rray_globals.one_int, + rray_args.x, + rray_args.i, + error_call + ); } r_obj* ffi_rray_slice_columns(r_obj* ffi_x, r_obj* ffi_i, r_obj* ffi_frame) { struct r_lazy error_call = {.x = ffi_frame, .env = r_null}; - return rray_slice_axis(ffi_x, ffi_i, 2, rray_args.x, rray_args.i, error_call); + return rray_slice_axis( + ffi_x, + ffi_i, + rray_globals.two_int, + rray_args.x, + rray_args.i, + error_call + ); } r_obj* rray_slice_axis( r_obj* x, r_obj* i, - int axis, + r_obj* axis, struct rray_arg* x_arg, struct rray_arg* i_arg, struct r_lazy error_call @@ -48,14 +63,24 @@ r_obj* rray_slice_axis( x = KEEP(arg_as_array(x, x_arg, error_call)); const int dimensionality = rray_dimensionality(x, x_arg, error_call); - check_axis(axis, dimensionality, rray_args.axis, error_call); + + r_obj* titles = KEEP(rray_titles(x, x_arg, error_call)); + + const int c_axis = arg_as_axis( + axis, + titles, + dimensionality, + rray_args.axis, + x_arg, + error_call + ); r_obj* indices = - KEEP(rray_slice_axis_indices(i, axis, dimensionality, i_arg)); + KEEP(rray_slice_axis_indices(i, c_axis, dimensionality, i_arg)); r_obj* out = rray_slice(x, indices, x_arg, rray_args.empty, error_call); - FREE(2); + FREE(3); return out; } @@ -67,11 +92,10 @@ r_obj* ffi_rray_slice_assign_axis( r_obj* ffi_frame ) { struct r_lazy error_call = {.x = ffi_frame, .env = r_null}; - const int axis = arg_as_int(ffi_axis, rray_args.axis, error_call); return rray_slice_assign_axis( ffi_x, ffi_i, - axis, + ffi_axis, ffi_value, rray_args.x, rray_args.i, @@ -90,7 +114,7 @@ r_obj* ffi_rray_slice_assign_rows( return rray_slice_assign_axis( ffi_x, ffi_i, - 1, + rray_globals.one_int, ffi_value, rray_args.x, rray_args.i, @@ -109,7 +133,7 @@ r_obj* ffi_rray_slice_assign_columns( return rray_slice_assign_axis( ffi_x, ffi_i, - 2, + rray_globals.two_int, ffi_value, rray_args.x, rray_args.i, @@ -121,7 +145,7 @@ r_obj* ffi_rray_slice_assign_columns( r_obj* rray_slice_assign_axis( r_obj* x, r_obj* i, - int axis, + r_obj* axis, r_obj* value, struct rray_arg* x_arg, struct rray_arg* i_arg, @@ -132,10 +156,20 @@ r_obj* rray_slice_assign_axis( x = KEEP(arg_as_array(x, x_arg, error_call)); const int dimensionality = rray_dimensionality(x, x_arg, error_call); - check_axis(axis, dimensionality, rray_args.axis, error_call); + + r_obj* titles = KEEP(rray_titles(x, x_arg, error_call)); + + const int c_axis = arg_as_axis( + axis, + titles, + dimensionality, + rray_args.axis, + x_arg, + error_call + ); r_obj* indices = - KEEP(rray_slice_axis_indices(i, axis, dimensionality, i_arg)); + KEEP(rray_slice_axis_indices(i, c_axis, dimensionality, i_arg)); r_obj* out = rray_slice_assign( x, @@ -147,7 +181,7 @@ r_obj* rray_slice_assign_axis( error_call ); - FREE(2); + FREE(3); return out; } diff --git a/src/slice-axis.h b/src/slice-axis.h index 5f9413f6..3b6c47c3 100644 --- a/src/slice-axis.h +++ b/src/slice-axis.h @@ -8,7 +8,7 @@ r_obj* rray_slice_axis( r_obj* x, r_obj* i, - int axis, + r_obj* axis, struct rray_arg* x_arg, struct rray_arg* i_arg, struct r_lazy error_call @@ -17,7 +17,7 @@ r_obj* rray_slice_axis( r_obj* rray_slice_assign_axis( r_obj* x, r_obj* i, - int axis, + r_obj* axis, r_obj* value, struct rray_arg* x_arg, struct rray_arg* i_arg, diff --git a/src/split.c b/src/split.c index 2d096c84..8431001f 100644 --- a/src/split.c +++ b/src/split.c @@ -16,13 +16,12 @@ r_obj* ffi_rray_split( r_obj* ffi_frame ) { struct r_lazy error_call = {.x = ffi_frame, .env = r_null}; - const int axis = arg_as_int(ffi_axis, rray_args.axis, error_call); - return rray_split(ffi_x, axis, ffi_dimensions, rray_args.x, error_call); + return rray_split(ffi_x, ffi_axis, ffi_dimensions, rray_args.x, error_call); } r_obj* rray_split( r_obj* x, - int axis, + r_obj* axis, r_obj* dimensions, struct rray_arg* arg, struct r_lazy error_call @@ -35,8 +34,19 @@ r_obj* rray_split( const int dimensionality = rray_dimensionality_from_dimensions(x_dimensions); check_dimensionality(dimensionality); - check_axis(axis, dimensionality, rray_args.axis, error_call); - const int axis_dimension = v_x_dimensions[axis - 1]; + r_obj* x_names = r_dim_names(x); + r_obj* const* v_x_names = (x_names == r_null) ? NULL : r_list_cbegin(x_names); + r_obj* x_titles = (x_names == r_null) ? r_null : r_names(x_names); + + const int c_axis = arg_as_axis( + axis, + x_titles, + dimensionality, + rray_args.axis, + arg, + error_call + ); + const int axis_dimension = v_x_dimensions[c_axis - 1]; dimensions = KEEP(arg_as_non_negative_bare_integer( dimensions, @@ -59,11 +69,7 @@ r_obj* rray_split( r_obj* out = KEEP(r_alloc_list(out_size)); - r_obj* x_names = r_dim_names(x); - r_obj* const* v_x_names = (x_names == r_null) ? NULL : r_list_cbegin(x_names); - r_obj* x_titles = (x_names == r_null) ? r_null : r_names(x_names); - - r_obj* x_axis_names = (v_x_names == NULL) ? r_null : v_x_names[axis - 1]; + r_obj* x_axis_names = (v_x_names == NULL) ? r_null : v_x_names[c_axis - 1]; r_obj* const* v_x_axis_names = (x_axis_names == r_null) ? NULL : r_chr_cbegin(x_axis_names); @@ -73,7 +79,7 @@ r_obj* rray_split( dimensionality, v_x_strides ); - const r_ssize x_axis_stride = v_x_strides[axis - 1]; + const r_ssize x_axis_stride = v_x_strides[c_axis - 1]; const enum r_type type = r_typeof(x); @@ -109,7 +115,7 @@ r_obj* rray_split( v_x_dimensions, sizeof(int) * dimensionality ); - v_out_elt_dimensions[axis - 1] = dimension; + v_out_elt_dimensions[c_axis - 1] = dimension; rray_run_iterator_init1( &it, @@ -131,7 +137,7 @@ r_obj* rray_split( v_x_names, x_titles, dimensionality, - axis, + c_axis, v_x_axis_names, x_axis_start, dimension diff --git a/src/split.h b/src/split.h index 9fef3120..567d9f48 100644 --- a/src/split.h +++ b/src/split.h @@ -7,7 +7,7 @@ r_obj* rray_split( r_obj* x, - int axis, + r_obj* axis, r_obj* dimensions, struct rray_arg* arg, struct r_lazy error_call diff --git a/src/stack.c b/src/stack.c index c2946a88..d9ec6ecd 100644 --- a/src/stack.c +++ b/src/stack.c @@ -57,7 +57,9 @@ r_obj* rray_stack( r_obj* names = KEEP(r_names(xs)); if (names != r_null) { - out = rray_set_axis_names(out, axis, names, arg, error_call); + r_obj* axis_obj = KEEP(r_int(axis)); + out = rray_set_axis_names(out, axis_obj, names, arg, error_call); + FREE(1); } FREE(4); diff --git a/src/titles.c b/src/titles.c index dfffc0b9..80a5bd1d 100644 --- a/src/titles.c +++ b/src/titles.c @@ -113,13 +113,18 @@ r_obj* ffi_rray_set_axis_title( r_obj* ffi_frame ) { struct r_lazy error_call = {.x = ffi_frame, .env = r_null}; - const int axis = arg_as_int(ffi_axis, rray_args.axis, error_call); - return rray_set_axis_title(ffi_x, axis, ffi_title, rray_args.x, error_call); + return rray_set_axis_title( + ffi_x, + ffi_axis, + ffi_title, + rray_args.x, + error_call + ); } r_obj* rray_set_axis_title( r_obj* x, - int axis, + r_obj* axis, r_obj* title, struct rray_arg* arg, struct r_lazy error_call @@ -128,10 +133,21 @@ r_obj* rray_set_axis_title( x = KEEP(arg_as_array(x, arg, error_call)); const int dimensionality = rray_dimensionality(x, arg, error_call); - check_axis(axis, dimensionality, rray_args.axis, error_call); + + r_obj* names = r_dim_names(x); + r_obj* old_titles = names == r_null ? r_null : r_names(names); + + const int c_axis = arg_as_axis( + axis, + old_titles, + dimensionality, + rray_args.axis, + arg, + error_call + ); if (title == r_null) { - r_obj* out = rray_clear_axis_title(x, axis, arg, error_call); + r_obj* out = rray_clear_axis_title(x, c_axis, arg, error_call); FREE(1); return out; } @@ -154,9 +170,6 @@ r_obj* rray_set_axis_title( ); } - r_obj* names = r_dim_names(x); - r_obj* old_titles = names == r_null ? r_null : r_names(names); - r_obj* new_titles = KEEP(r_alloc_character(dimensionality)); if (old_titles != r_null) { @@ -166,7 +179,7 @@ r_obj* rray_set_axis_title( } } - r_chr_poke(new_titles, axis - 1, r_chr_get(title, 0)); + r_chr_poke(new_titles, c_axis - 1, r_chr_get(title, 0)); r_obj* out = KEEP(rray_set_titles(x, new_titles, arg, error_call)); diff --git a/src/titles.h b/src/titles.h index 372f5430..50fd76f9 100644 --- a/src/titles.h +++ b/src/titles.h @@ -23,7 +23,7 @@ r_obj* rray_set_titles( r_obj* rray_set_axis_title( r_obj* x, - int axis, + r_obj* axis, r_obj* title, struct rray_arg* arg, struct r_lazy error_call diff --git a/src/unstack.c b/src/unstack.c index 66b5eca9..0bdc4278 100644 --- a/src/unstack.c +++ b/src/unstack.c @@ -2,20 +2,21 @@ #include "axes.h" #include "dimensionality.h" +#include "globals.h" #include "names.h" #include "remove-axes.h" #include "split.h" +#include "titles.h" #include "utils.h" r_obj* ffi_rray_unstack(r_obj* ffi_x, r_obj* ffi_axis, r_obj* ffi_frame) { struct r_lazy error_call = {.x = ffi_frame, .env = r_null}; - const int axis = arg_as_int(ffi_axis, rray_args.axis, error_call); - return rray_unstack(ffi_x, axis, rray_args.x, error_call); + return rray_unstack(ffi_x, ffi_axis, rray_args.x, error_call); } r_obj* rray_unstack( r_obj* x, - int axis, + r_obj* axis, struct rray_arg* arg, struct r_lazy error_call ) { @@ -34,12 +35,15 @@ r_obj* rray_unstack( ); } - check_axis(axis, dimensionality, rray_args.axis, error_call); + r_obj* titles = KEEP(rray_titles(x, arg, error_call)); - r_obj* dimensions = KEEP(r_int(1)); - r_obj* out = KEEP(rray_split(x, axis, dimensions, arg, error_call)); + const int c_axis = + arg_as_axis(axis, titles, dimensionality, rray_args.axis, arg, error_call); + + r_obj* axes = KEEP(r_int(c_axis)); + + r_obj* out = KEEP(rray_split(x, axes, rray_globals.one_int, arg, error_call)); - r_obj* axes = KEEP(r_int(axis)); const r_ssize out_size = r_length(out); for (r_ssize i = 0; i < out_size; ++i) { @@ -47,7 +51,7 @@ r_obj* rray_unstack( r_list_poke(out, i, rray_remove_axes(elt, axes, arg, error_call)); } - r_obj* names = KEEP(rray_axis_names(x, axis, arg, error_call)); + r_obj* names = KEEP(rray_axis_names(x, axes, arg, error_call)); if (names != r_null) { r_attrib_poke_names(out, names); } diff --git a/src/unstack.h b/src/unstack.h index 21f35d95..f6df26be 100644 --- a/src/unstack.h +++ b/src/unstack.h @@ -7,7 +7,7 @@ r_obj* rray_unstack( r_obj* x, - int axis, + r_obj* axis, struct rray_arg* arg, struct r_lazy error_call ); diff --git a/tests/testthat/_snaps/locate.md b/tests/testthat/_snaps/locate.md index 7f24ea28..d375c653 100644 --- a/tests/testthat/_snaps/locate.md +++ b/tests/testthat/_snaps/locate.md @@ -20,7 +20,7 @@ rray_locate_max(x, axis = c(1L, 2L)) Condition Error in `rray_locate_max()`: - ! `axis` must be a single integer, not length 2. + ! `axis` must be length 1, not length 2. --- @@ -38,6 +38,22 @@ Error in `rray_locate_min()`: ! `axis` must contain whole numbers that fit in an integer. Problem at location 1. +--- + + Code + rray_locate_min(x, axis = "row") + Condition + Error in `rray_locate_min()`: + ! Can't use a title in `axis` because `x` has no titles. + +# errors on an unknown `axis` title + + Code + rray_locate_max(x, axis = "nope") + Condition + Error in `rray_locate_max()`: + ! Can't find an axis of `x` titled "nope". + # `axis` must be named Code diff --git a/tests/testthat/_snaps/names.md b/tests/testthat/_snaps/names.md index 8e126cbc..f9958bf6 100644 --- a/tests/testthat/_snaps/names.md +++ b/tests/testthat/_snaps/names.md @@ -52,7 +52,7 @@ rray_axis_names(x, c(1, 2)) Condition Error in `rray_axis_names()`: - ! `axis` must be a single integer, not length 2. + ! `axis` must be length 1, not length 2. --- @@ -60,7 +60,23 @@ rray_axis_names(x, integer()) Condition Error in `rray_axis_names()`: - ! `axis` must be a single integer, not length 0. + ! `axis` must be length 1, not length 0. + +--- + + Code + rray_axis_names(x, "row") + Condition + Error in `rray_axis_names()`: + ! Can't use a title in `axis` because `x` has no titles. + +# rray_axis_names() errors on an unknown title + + Code + rray_axis_names(x, "nope") + Condition + Error in `rray_axis_names()`: + ! Can't find an axis of `x` titled "nope". # rray_axis_names() errors on classed input @@ -158,6 +174,22 @@ Error in `rray_set_axis_names()`: ! `axis` must be less than or equal to the dimensionality of 2, not 3. +--- + + Code + rray_set_axis_names(x, "row", "a") + Condition + Error in `rray_set_axis_names()`: + ! Can't use a title in `axis` because `x` has no titles. + +# rray_set_axis_names() errors on an unknown title + + Code + rray_set_axis_names(x, "nope", "a") + Condition + Error in `rray_set_axis_names()`: + ! Can't find an axis of `x` titled "nope". + # rray_set_axis_names() errors if names aren't character or NULL Code diff --git a/tests/testthat/_snaps/rep.md b/tests/testthat/_snaps/rep.md index 665e82ce..7444892d 100644 --- a/tests/testthat/_snaps/rep.md +++ b/tests/testthat/_snaps/rep.md @@ -301,3 +301,11 @@ Error in `rray_rep_each()`: ! `axis` must be less than or equal to the dimensionality of 2, not 3. +--- + + Code + rray_rep_each(x, times = 2, axis = "row") + Condition + Error in `rray_rep_each()`: + ! Can't use a title in `axis` because `x` has no titles. + diff --git a/tests/testthat/_snaps/roll.md b/tests/testthat/_snaps/roll.md index 15bdca30..999599b2 100644 --- a/tests/testthat/_snaps/roll.md +++ b/tests/testthat/_snaps/roll.md @@ -180,7 +180,7 @@ rray_roll_each(x, n = 1, axis = c(1, 2)) Condition Error in `rray_roll_each()`: - ! `axis` must be a single integer, not length 2. + ! `axis` must be length 1, not length 2. --- @@ -206,6 +206,14 @@ Error in `rray_roll_each()`: ! `axis` must not be missing. +--- + + Code + rray_roll_each(x, n = 1, axis = "row") + Condition + Error in `rray_roll_each()`: + ! Can't use a title in `axis` because `x` has no titles. + # `n` must broadcast to `x` with `axis` set to 1 Code diff --git a/tests/testthat/_snaps/slice.md b/tests/testthat/_snaps/slice.md index c2d2dbf8..da2ac1b8 100644 --- a/tests/testthat/_snaps/slice.md +++ b/tests/testthat/_snaps/slice.md @@ -124,12 +124,17 @@ rray_slice_axis(x, 1L, axis = c(1, 2)) Condition Error in `rray_slice_axis()`: - ! `axis` must be a single integer, not length 2. + ! `axis` must be length 1, not length 2. Code rray_slice_axis(x, 1L, axis = NA_integer_) Condition Error in `rray_slice_axis()`: ! `axis` must not be missing. + Code + rray_slice_axis(x, 1L, axis = "row") + Condition + Error in `rray_slice_axis()`: + ! Can't use a title in `axis` because `x` has no titles. # errors on unsupported `x` inputs to `rray_slice_axis()` @@ -275,6 +280,11 @@ Condition Error in `rray_slice_assign_axis()`: ! `axis` must be less than or equal to the dimensionality of 2, not 3. + Code + rray_slice_assign_axis(x, 1L, axis = "row", value = 0L) + Condition + Error in `rray_slice_assign_axis()`: + ! Can't use a title in `axis` because `x` has no titles. Code rray_slice_assign_axis(x, 1L, axis = 1, value = "a") Condition diff --git a/tests/testthat/_snaps/split.md b/tests/testthat/_snaps/split.md index 6406c54e..383271a7 100644 --- a/tests/testthat/_snaps/split.md +++ b/tests/testthat/_snaps/split.md @@ -40,7 +40,7 @@ rray_split(x, axis = c(1, 2), dimensions = 1) Condition Error in `rray_split()`: - ! `axis` must be a single integer, not length 2. + ! `axis` must be length 1, not length 2. --- @@ -58,6 +58,14 @@ Error in `rray_split()`: ! `axis` must contain whole numbers that fit in an integer. Problem at location 1. +--- + + Code + rray_split(x, axis = "row", dimensions = 1) + Condition + Error in `rray_split()`: + ! Can't use a title in `axis` because `x` has no titles. + --- Code diff --git a/tests/testthat/_snaps/titles.md b/tests/testthat/_snaps/titles.md index 9453e083..839d19a3 100644 --- a/tests/testthat/_snaps/titles.md +++ b/tests/testthat/_snaps/titles.md @@ -46,6 +46,14 @@ Error in `rray_axis_title()`: ! `axis` must be a single integer, not length 2. +--- + + Code + rray_axis_title(x, "Row") + Condition + Error in `rray_axis_title()`: + ! `axis` must be an integer or double vector, not the string "Row". + # title setters reject invalid input and values Code @@ -78,6 +86,14 @@ Error in `rray_set_axis_title()`: ! `axis` must be greater than or equal to 1, not 0. +--- + + Code + rray_set_axis_title(x, "Row", "R") + Condition + Error in `rray_set_axis_title()`: + ! Can't use a title in `axis` because `x` has no titles. + --- Code diff --git a/tests/testthat/_snaps/unstack.md b/tests/testthat/_snaps/unstack.md index 12d8502e..86efaec4 100644 --- a/tests/testthat/_snaps/unstack.md +++ b/tests/testthat/_snaps/unstack.md @@ -20,7 +20,7 @@ rray_unstack(x, axis = c(1, 2)) Condition Error in `rray_unstack()`: - ! `axis` must be a single integer, not length 2. + ! `axis` must be length 1, not length 2. --- @@ -38,6 +38,14 @@ Error in `rray_unstack()`: ! `axis` must contain whole numbers that fit in an integer. Problem at location 1. +--- + + Code + rray_unstack(x, axis = "row") + Condition + Error in `rray_unstack()`: + ! Can't use a title in `axis` because `x` has no titles. + --- Code diff --git a/tests/testthat/test-locate.R b/tests/testthat/test-locate.R index bbf9896c..d21a8fb5 100644 --- a/tests/testthat/test-locate.R +++ b/tests/testthat/test-locate.R @@ -243,6 +243,18 @@ test_that("`axis` is coerced to integer", { expect_identical(rray_locate_min(x, axis = 1), rray_locate_min(x, axis = 1L)) }) +test_that("`axis` can be a title", { + x <- array(1:6, c(2L, 3L), dimnames = list(row = NULL, col = NULL)) + expect_identical( + rray_locate_max(x, axis = "col"), + rray_locate_max(x, axis = 2L) + ) + expect_identical( + rray_locate_min(x, axis = "col"), + rray_locate_min(x, axis = 2L) + ) +}) + test_that("`axis` is validated", { x <- array(1:4, c(2L, 2L)) expect_snapshot(rray_locate_max(x, axis = 0L), error = TRUE) @@ -250,6 +262,12 @@ test_that("`axis` is validated", { expect_snapshot(rray_locate_max(x, axis = c(1L, 2L)), error = TRUE) expect_snapshot(rray_locate_max(x, axis = NA_integer_), error = TRUE) expect_snapshot(rray_locate_min(x, axis = 1.5), error = TRUE) + expect_snapshot(rray_locate_min(x, axis = "row"), error = TRUE) +}) + +test_that("errors on an unknown `axis` title", { + x <- array(1:6, c(2L, 3L), dimnames = list(row = NULL, col = NULL)) + expect_snapshot(rray_locate_max(x, axis = "nope"), error = TRUE) }) test_that("`axis` must be named", { diff --git a/tests/testthat/test-names.R b/tests/testthat/test-names.R index ca054db4..a7d9c557 100644 --- a/tests/testthat/test-names.R +++ b/tests/testthat/test-names.R @@ -70,6 +70,16 @@ test_that("rray_axis_names() coerces axis to integer", { expect_identical(rray_axis_names(x, 1), rray_axis_names(x, 1L)) }) +test_that("rray_axis_names() takes a title for axis", { + x <- array( + 1:6, + c(2, 3), + dimnames = list(row = NULL, col = c("c1", "c2", "c3")) + ) + expect_identical(rray_axis_names(x, "col"), c("c1", "c2", "c3")) + expect_null(rray_axis_names(x, "row")) +}) + test_that("rray_axis_names() errors on an invalid axis", { x <- array(1:6, c(2, 3)) expect_snapshot(rray_axis_names(x, 0), error = TRUE) @@ -77,6 +87,12 @@ test_that("rray_axis_names() errors on an invalid axis", { expect_snapshot(rray_axis_names(x, NA_integer_), error = TRUE) expect_snapshot(rray_axis_names(x, c(1, 2)), error = TRUE) expect_snapshot(rray_axis_names(x, integer()), error = TRUE) + expect_snapshot(rray_axis_names(x, "row"), error = TRUE) +}) + +test_that("rray_axis_names() errors on an unknown title", { + x <- array(1:6, c(2, 3), dimnames = list(row = NULL, col = NULL)) + expect_snapshot(rray_axis_names(x, "nope"), error = TRUE) }) test_that("rray_axis_names() errors on classed input", { @@ -280,10 +296,24 @@ test_that("rray_set_axis_names() coerces axis to integer", { ) }) +test_that("rray_set_axis_names() takes a title for axis", { + x <- array(1:6, c(2, 3), dimnames = list(row = NULL, col = NULL)) + expect_identical( + rray_set_axis_names(x, "col", c("c1", "c2", "c3")), + rray_set_axis_names(x, 2, c("c1", "c2", "c3")) + ) +}) + test_that("rray_set_axis_names() errors on an invalid axis", { x <- array(1:6, c(2, 3)) expect_snapshot(rray_set_axis_names(x, 0, "a"), error = TRUE) expect_snapshot(rray_set_axis_names(x, 3, "a"), error = TRUE) + expect_snapshot(rray_set_axis_names(x, "row", "a"), error = TRUE) +}) + +test_that("rray_set_axis_names() errors on an unknown title", { + x <- array(1:6, c(2, 3), dimnames = list(row = NULL, col = NULL)) + expect_snapshot(rray_set_axis_names(x, "nope", "a"), error = TRUE) }) test_that("rray_set_axis_names() errors if names aren't character or NULL", { diff --git a/tests/testthat/test-rep.R b/tests/testthat/test-rep.R index 493edfe6..53ab0c2c 100644 --- a/tests/testthat/test-rep.R +++ b/tests/testthat/test-rep.R @@ -512,6 +512,14 @@ test_that("`times` can be a double", { ) }) +test_that("`axis` can be a title", { + x <- array(1:6, c(3L, 2L), dimnames = list(row = NULL, col = NULL)) + expect_identical( + rray_rep_each(x, times = c(1, 2, 3), axis = "row"), + rray_rep_each(x, times = c(1, 2, 3), axis = 1) + ) +}) + test_that("names on the repeated axis repeat, and can be duplicated", { x <- array(1:6, c(3, 2), dimnames = list(c("a", "b", "c"), c("x", "y"))) @@ -603,6 +611,7 @@ test_that("`axis` is validated", { x <- array(1:6, c(3, 2)) expect_snapshot(rray_rep_each(x, times = 2, axis = 0), error = TRUE) expect_snapshot(rray_rep_each(x, times = 2, axis = 3), error = TRUE) + expect_snapshot(rray_rep_each(x, times = 2, axis = "row"), error = TRUE) }) test_that("`times` is validated", { diff --git a/tests/testthat/test-roll.R b/tests/testthat/test-roll.R index 19858810..47010fc8 100644 --- a/tests/testthat/test-roll.R +++ b/tests/testthat/test-roll.R @@ -525,6 +525,14 @@ test_that("`n` can be integerish doubles or logicals", { ) }) +test_that("`axis` can be a title", { + x <- matrix(1:12, nrow = 3, dimnames = list(row = NULL, col = NULL)) + expect_identical( + rray_roll_each(x, n = c(0, 1, 2), axis = "col"), + rray_roll_each(x, n = c(0, 1, 2), axis = 2) + ) +}) + test_that("works with a zero dimension", { x <- array(integer(), c(2, 0)) expect_identical(rray_roll_each(x, n = c(1, 2), axis = 2), x) @@ -588,6 +596,7 @@ test_that("`axis` is validated", { expect_snapshot(rray_roll_each(x, n = 1, axis = 3), error = TRUE) expect_snapshot(rray_roll_each(x, n = 1, axis = 0), error = TRUE) expect_snapshot(rray_roll_each(x, n = 1, axis = NA_integer_), error = TRUE) + expect_snapshot(rray_roll_each(x, n = 1, axis = "row"), error = TRUE) }) test_that("`n` must broadcast to `x` with `axis` set to 1", { diff --git a/tests/testthat/test-slice.R b/tests/testthat/test-slice.R index f532323a..ad1d4083 100644 --- a/tests/testthat/test-slice.R +++ b/tests/testthat/test-slice.R @@ -420,6 +420,23 @@ test_that("`rray_slice_axis()` keeps titles", { ) }) +test_that("`axis` can be a title", { + x <- array( + 1:6, + c(2L, 3L), + dimnames = list(row = c("a", "b"), col = c("c", "d", "e")) + ) + + expect_identical( + rray_slice_axis(x, 1L, axis = "col"), + rray_slice_axis(x, 1L, axis = 2) + ) + expect_identical( + rray_slice_axis(x, c("e", "c"), axis = "col"), + rray_slice_axis(x, c(3L, 1L), axis = 2) + ) +}) + test_that("`rray_slice_axis()` works with a bare vector", { expect_identical( rray_slice_axis(c(a = 1L, b = 2L), c(2L, 1L), axis = 1), @@ -457,6 +474,7 @@ test_that("`axis` is validated", { rray_slice_axis(x, 1L, axis = 3) rray_slice_axis(x, 1L, axis = c(1, 2)) rray_slice_axis(x, 1L, axis = NA_integer_) + rray_slice_axis(x, 1L, axis = "row") }) }) @@ -986,6 +1004,23 @@ test_that("`rray_slice_assign_axis()` keeps titles", { ) }) +test_that("`axis` can be a title in `rray_slice_assign_axis()`", { + x <- array( + 1:6, + c(2L, 3L), + dimnames = list(row = c("a", "b"), col = c("c", "d", "e")) + ) + + expect_identical( + rray_slice_assign_axis(x, 1L, axis = "col", value = 0L), + rray_slice_assign_axis(x, 1L, axis = 2, value = 0L) + ) + expect_identical( + rray_slice_assign_axis(x, c("e", "c"), axis = "col", value = 0L), + rray_slice_assign_axis(x, c(3L, 1L), axis = 2, value = 0L) + ) +}) + test_that("reports subscript errors from `rray_slice_assign_axis()` as `i`", { x <- array(1:6, c(2L, 3L), dimnames = list(c("a", "b"), NULL)) @@ -1000,6 +1035,7 @@ test_that("`axis` and `value` are validated by `rray_slice_assign_axis()`", { expect_snapshot(error = TRUE, { rray_slice_assign_axis(x, 1L, axis = 3, value = 0L) + rray_slice_assign_axis(x, 1L, axis = "row", value = 0L) rray_slice_assign_axis(x, 1L, axis = 1, value = "a") rray_slice_assign_axis(x, 1L, axis = 1, value = 1:2) }) diff --git a/tests/testthat/test-split.R b/tests/testthat/test-split.R index 89fcca5c..112c2982 100644 --- a/tests/testthat/test-split.R +++ b/tests/testthat/test-split.R @@ -185,6 +185,14 @@ test_that("names on non-split axes are kept", { expect_identical(dimnames(out[[3]]), list(c("a", "b"), "z")) }) +test_that("`axis` can be a title", { + x <- array(1:6, c(2, 3), dimnames = list(row = NULL, col = c("x", "y", "z"))) + expect_identical( + rray_split(x, axis = "col", dimensions = c(2, 1)), + rray_split(x, axis = 2, dimensions = c(2, 1)) + ) +}) + test_that("names on the split axis are sliced", { x <- array(1:6, c(2, 3), dimnames = list(c("a", "b"), c("x", "y", "z"))) @@ -307,6 +315,7 @@ test_that("`axis` is validated", { error = TRUE ) expect_snapshot(rray_split(x, axis = 1.5, dimensions = 1), error = TRUE) + expect_snapshot(rray_split(x, axis = "row", dimensions = 1), error = TRUE) expect_snapshot( rray_split(x, axis = structure(1L, class = "foo"), dimensions = 1), error = TRUE diff --git a/tests/testthat/test-titles.R b/tests/testthat/test-titles.R index 52ee466c..aed11cc0 100644 --- a/tests/testthat/test-titles.R +++ b/tests/testthat/test-titles.R @@ -127,6 +127,21 @@ test_that("title setters remove one or all titles", { expect_identical(dimnames(out), unname(dimnames(x))) }) +test_that("`rray_set_axis_title()` takes a title for `axis`", { + x <- matrix( + 1:6, + 2, + 3, + dimnames = list(Row = c("a", "b"), Column = c("x", "y", "z")) + ) + + out <- rray_set_axis_title(x, "Row", "R") + expect_identical(rray_titles(out), c("R", "Column")) + + out <- rray_set_axis_title(x, "Column", NULL) + expect_identical(rray_titles(out), c("Row", "")) +}) + test_that("title setters keep empty titles", { x <- matrix( 1:6, @@ -166,6 +181,7 @@ test_that("title getters reject invalid input and axes", { expect_snapshot(rray_axis_title(x, 3), error = TRUE) expect_snapshot(rray_axis_title(x, NA_integer_), error = TRUE) expect_snapshot(rray_axis_title(x, c(1, 2)), error = TRUE) + expect_snapshot(rray_axis_title(x, "Row"), error = TRUE) }) test_that("title setters reject invalid input and values", { @@ -176,6 +192,7 @@ test_that("title setters reject invalid input and values", { expect_snapshot(rray_set_titles(x, 1:2), error = TRUE) expect_snapshot(rray_set_titles(x, "Row"), error = TRUE) expect_snapshot(rray_set_axis_title(x, 0, "Row"), error = TRUE) + expect_snapshot(rray_set_axis_title(x, "Row", "R"), error = TRUE) expect_snapshot(rray_set_axis_title(x, 1, 1L), error = TRUE) expect_snapshot(rray_set_axis_title(x, 1, character()), error = TRUE) expect_snapshot(rray_set_axis_title(x, 1, c("Row", "Column")), error = TRUE) diff --git a/tests/testthat/test-unstack.R b/tests/testthat/test-unstack.R index b657d279..b604d0e5 100644 --- a/tests/testthat/test-unstack.R +++ b/tests/testthat/test-unstack.R @@ -74,6 +74,11 @@ test_that("names on the axis become the list names", { expect_identical(out$x, array(1:2, 2, dimnames = list(c("a", "b")))) }) +test_that("`axis` can be a title", { + x <- array(1:6, c(2, 3), dimnames = list(row = NULL, col = c("x", "y", "z"))) + expect_identical(rray_unstack(x, axis = "col"), rray_unstack(x, axis = 2)) +}) + test_that("names on the surviving axes move to their new locations", { x <- array( 1:24, @@ -184,6 +189,7 @@ test_that("`axis` is validated", { expect_snapshot(rray_unstack(x, axis = c(1, 2)), error = TRUE) expect_snapshot(rray_unstack(x, axis = NA_integer_), error = TRUE) expect_snapshot(rray_unstack(x, axis = 1.5), error = TRUE) + expect_snapshot(rray_unstack(x, axis = "row"), error = TRUE) expect_snapshot( rray_unstack(x, axis = structure(1L, class = "foo")), error = TRUE