├── .Rbuildignore ├── .gitignore ├── DESCRIPTION ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R ├── bind.R ├── dim.R ├── dim2.R ├── dimnames.R ├── extract.R ├── map.R ├── modify.R ├── onehot.R ├── reshape.R ├── seq_along.R ├── shuffle.R ├── split.R ├── standardize.R ├── utils.R └── zzz.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── cran-comments.md ├── docs ├── 404.html ├── LICENSE.html ├── authors.html ├── bootstrap-toc.css ├── bootstrap-toc.js ├── docsearch.css ├── docsearch.js ├── extra.css ├── index.html ├── link.svg ├── pkgdown.css ├── pkgdown.js ├── pkgdown.yml └── reference │ ├── DIM.html │ ├── array2.html │ ├── bind-arrays.html │ ├── drop_dimnames.html │ ├── expand_dims.html │ ├── extract_dim.html │ ├── index.html │ ├── map_along_dim.html │ ├── modify_along_dim.html │ ├── ndim.html │ ├── onehot.html │ ├── seq_along_dim.html │ ├── set_as_rows.html │ ├── set_dim.html │ ├── set_dimnames.html │ ├── shuffle_rows.html │ ├── split-array.html │ └── t.array.html ├── listarrays.Rproj ├── man ├── DIM.Rd ├── array2.Rd ├── bind-arrays.Rd ├── drop_dimnames.Rd ├── expand_dims.Rd ├── extract_dim.Rd ├── map_along_dim.Rd ├── modify_along_dim.Rd ├── ndim.Rd ├── onehot.Rd ├── seq_along_dim.Rd ├── set_as_rows.Rd ├── set_dim.Rd ├── set_dimnames.Rd ├── shuffle_rows.Rd └── split-array.Rd ├── pkgdown └── extra.css └── tests ├── testthat.R └── testthat ├── test-bind.R ├── test-dimnames.R ├── test-map.R ├── test-modify.R ├── test-onehot.R ├── test-shuffle.R └── test-split.R /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^README\.Rmd$ 4 | ^LICENSE\.md$ 5 | ^docs$ 6 | ^pkgdown$ 7 | ^_ 8 | ^_pkgdown\.yml$ 9 | ^CRAN-SUBMISSION$ 10 | ^cran-comments\.md$ 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | R/_*.R 6 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: listarrays 2 | Type: Package 3 | Title: A Toolbox for Working with R Arrays in a Functional Programming Style 4 | Version: 0.4.0 5 | Authors@R: person("Tomasz", "Kalinowski", email = "kalinowskit@gmail.com", 6 | role = c("aut", "cre")) 7 | Description: A toolbox for R arrays. Flexibly split, bind, reshape, modify, 8 | subset and name arrays. 9 | URL: https://github.com/t-kalinowski/listarrays, https://t-kalinowski.github.io/listarrays/ 10 | BugReports: https://github.com/t-kalinowski/listarrays/issues 11 | License: GPL-3 12 | Encoding: UTF-8 13 | LazyData: true 14 | ByteCompile: true 15 | RoxygenNote: 7.3.1 16 | Roxygen: list(markdown = TRUE) 17 | Suggests: 18 | testthat, magrittr, zeallot, rlang, tibble, purrr 19 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export("dim2<-") 4 | export(DIM) 5 | export(DROP) 6 | export(array2) 7 | export(bind_as_cols) 8 | export(bind_as_dim) 9 | export(bind_as_rows) 10 | export(bind_on_cols) 11 | export(bind_on_dim) 12 | export(bind_on_rows) 13 | export(decode_onehot) 14 | export(drop_dim) 15 | export(drop_dim2) 16 | export(drop_dimnames) 17 | export(expand_dims) 18 | export(extract_cols) 19 | export(extract_dim) 20 | export(extract_rows) 21 | export(map_along_cols) 22 | export(map_along_dim) 23 | export(map_along_rows) 24 | export(matrix2) 25 | export(modify_along_cols) 26 | export(modify_along_dim) 27 | export(modify_along_rows) 28 | export(ndim) 29 | export(onehot) 30 | export(onehot_decoder) 31 | export(onehot_with_decoder) 32 | export(seq_along_cols) 33 | export(seq_along_dim) 34 | export(seq_along_rows) 35 | export(set_as_cols) 36 | export(set_as_rows) 37 | export(set_dim) 38 | export(set_dim2) 39 | export(set_dimnames) 40 | export(shuffle_rows) 41 | export(split_along_cols) 42 | export(split_along_dim) 43 | export(split_along_rows) 44 | export(split_on_cols) 45 | export(split_on_dim) 46 | export(split_on_rows) 47 | importFrom(compiler,cmpfun) 48 | importFrom(utils,modifyList) 49 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # listarrays 0.4.0 2 | 3 | * Removed `t.arrray()` S3 method 4 | * Added a `NEWS.md` file to track changes to the package. 5 | -------------------------------------------------------------------------------- /R/bind.R: -------------------------------------------------------------------------------- 1 | #' Bind arrays along a specified dimension 2 | #' 3 | #' `bind_as_*` introduces a new dimension, such that each element in 4 | #' `list_of_arrays` corresponds to one index position along the new dimension in 5 | #' the returned array. `bind_on_*` binds all elements along an existing 6 | #' dimension, (meaning, the returned array has the same number of dimensions as 7 | #' each of the arrays in the list). 8 | #' 9 | #' `bind_*_rows()` is a wrapper for the common case of `bind_*_dim(X, 1)`. 10 | #' `bind_*_cols()` is a wrapper for the common case of `bind_*_dim(X, -1)`. 11 | #' 12 | #' @param list_of_arrays a list of arrays. All arrays must be of the same 13 | #' dimension. NULL's in place of arrays are automatically dropped. 14 | #' @param ... Arrays to be bound, specified individually or supplied as a single 15 | #' list 16 | #' @param which_dim Scalar integer specifying the index position of where to 17 | #' introduce the new dimension to introduce. Negative numbers count from the 18 | #' back. For example, given a 3 dimensional array, `-1`, is equivalent to `3`, 19 | #' `-2` to `2` and `-3` to `1`. 20 | #' 21 | #' @return An array, with one additional dimension. 22 | #' @rdname bind-arrays 23 | #' @export 24 | #' 25 | #' @examples 26 | #' list_of_arrays <- replicate(10, array(1:8, dim = c(2,3,4)), FALSE) 27 | #' 28 | #' dim(list_of_arrays[[1]]) 29 | #' 30 | #' # bind on a new dimension 31 | #' combined_as <- bind_as_rows(list_of_arrays) 32 | #' dim(combined_as) 33 | #' dim(combined_as)[1] == length(list_of_arrays) 34 | #' 35 | #' # each element in `list_of_arrays` corresponds to one "row" 36 | #' # (i.e., one entry in along the first dimension) 37 | #' for(i in seq_along(list_of_arrays)) 38 | #' stopifnot(identical(combined_as[i,,,], list_of_arrays[[i]])) 39 | #' 40 | #' # bind on an existing dimension 41 | #' combined_on <- bind_on_rows(list_of_arrays) 42 | #' dim(combined_on) 43 | #' dim(combined_on)[1] == sum(sapply(list_of_arrays, function(x) dim(x)[1])) 44 | #' identical(list_of_arrays[[1]], combined_on[1:2,,]) 45 | #' for (i in seq_along(list_of_arrays)) 46 | #' stopifnot(identical( 47 | #' list_of_arrays[[i]], combined_on[ (1:2) + (i-1)*2,,] 48 | #' )) 49 | #' 50 | #' # bind on any dimension 51 | #' combined <- bind_as_dim(list_of_arrays, 3) 52 | #' dim(combined) 53 | #' for(i in seq_along(list_of_arrays)) 54 | #' stopifnot(identical(combined[,,i,], list_of_arrays[[i]])) 55 | bind_as_dim <- function(list_of_arrays, which_dim) { 56 | 57 | # standardize_which_dim(which_dim, n_dim = ) 58 | check.is.integerish(which_dim, 1L) 59 | new_axis_nm <- names(which_dim) 60 | which_dim <- as.integer(which_dim) 61 | 62 | stopifnot(is.list(list_of_arrays)) 63 | list_of_arrays <- dropNULLs(list_of_arrays) 64 | 65 | base_dim <- unique(lapply(list_of_arrays, function(x) dim(x) %||% length(x))) 66 | stopifnot(is.scalar(base_dim)) 67 | base_dim <- base_dim[[1]] 68 | 69 | if(is.negative(which_dim)) 70 | which_dim <- which_dim + length(base_dim) + 2L 71 | 72 | X <- simplify2array(list_of_arrays) 73 | rank <- length(base_dim) 74 | if (which_dim != rank + 1L) { 75 | perm <- append(seq_len(rank), rank + 1L, after = which_dim - 1L) 76 | X <- aperm(X, perm) 77 | } 78 | 79 | if(!is.null(new_axis_nm)) 80 | names(dimnames(X))[which_dim] <- new_axis_nm 81 | 82 | X 83 | } 84 | 85 | #' @rdname bind-arrays 86 | #' @export 87 | bind_as_rows <- function(...) { 88 | list_of_arrays <- list(...) 89 | if (identical(nargs(), 1L)) 90 | list_of_arrays <- list_of_arrays[[1]] 91 | bind_as_dim(list_of_arrays, which_dim = 1L) 92 | } 93 | 94 | #' @rdname bind-arrays 95 | #' @export 96 | bind_as_cols <- function(...) { 97 | list_of_arrays <- list(...) 98 | if (identical(nargs(), 1L)) 99 | list_of_arrays <- list_of_arrays[[1]] 100 | bind_as_dim(list_of_arrays, which_dim = -1L) 101 | } 102 | 103 | 104 | 105 | 106 | 107 | 108 | BIND_ON_FN_TEMPLATE <- 109 | alist(list_of_arrays = , n_entries_per_array = , new_dim = , { 110 | X <- array(vector(typeof(list_of_arrays[[1L]])), dim = new_dim) 111 | start <- 1L 112 | for (i in seq_along(list_of_arrays)) { 113 | end <- start + n_entries_per_array[[i]] - 1L 114 | EXTRACT_CALL <- list_of_arrays[[i]] 115 | start <- end + 1L 116 | } 117 | X 118 | }) 119 | 120 | minimal_bind_on_env <- 121 | list2env(mget( 122 | c( 123 | "array", 124 | "vector", 125 | "typeof", 126 | "seq_along", 127 | "<-", 128 | "+", 129 | "-", 130 | "[[", 131 | "[<-", 132 | ":", "{", "for" 133 | ), 134 | envir = baseenv() 135 | )) 136 | 137 | 138 | new_bind_on_fn <- function(extract_call) { 139 | BIND_ON_FN_TEMPLATE[[c(4L, 4L, 4L, 3L, 2L)]] <- extract_call 140 | as.function.default(BIND_ON_FN_TEMPLATE, envir = minimal_bind_on_env) 141 | } 142 | 143 | #' @rdname bind-arrays 144 | #' @export 145 | bind_on_dim <- function(list_of_arrays, which_dim) { 146 | 147 | stopifnot(is.list(list_of_arrays)) 148 | list_of_arrays <- dropNULLs(list_of_arrays) 149 | 150 | all_dims <- lapply(list_of_arrays, function(x) dim(x) %||% length(x)) 151 | all_n_dims <- lengths(all_dims) #lapply(all_dims, length) 152 | stopifnot(is.scalar(unique(all_n_dims))) 153 | n_dim <- all_n_dims[[1]] 154 | 155 | which_dim <- standardize_which_dim(which_dim, 156 | names_dimnames_X = { 157 | all_axis_names <- lapply(list_of_arrays, function(x) names(dimnames(x))) 158 | stopifnot(length(unique(all_axis_names)) == 1L) 159 | all_axis_names[[1]] 160 | }, n_dim = n_dim) 161 | 162 | base_dim <- unique(lapply(all_dims, function(d) 163 | d[-which_dim])) 164 | stopifnot(identical(length(base_dim), 1L)) 165 | base_dim <- base_dim[[1]] 166 | 167 | n_entries_per_array <- quick_cbind(all_dims)[which_dim,] 168 | 169 | new_dim <- all_dims[[1]] 170 | new_dim[which_dim] <- sum(n_entries_per_array) 171 | 172 | X_start_to_end <- extract_dim_expr( 173 | var_to_subset = quote(X), idx_var_sym = quote(start:end), 174 | which_dim = which_dim, ndims = length(new_dim)) 175 | 176 | bind_it <- new_bind_on_fn(X_start_to_end) 177 | 178 | if(length(list_of_arrays) > 100) 179 | bind_it <- cmpfun(bind_it) 180 | 181 | X <- bind_it(list_of_arrays, n_entries_per_array, new_dim) 182 | 183 | if (!is.null(names(list_of_arrays))) 184 | dimnames(X)[[which_dim]] <- 185 | rep(names(list_of_arrays), times = n_entries_per_array) 186 | 187 | X 188 | } 189 | 190 | #' @rdname bind-arrays 191 | #' @export 192 | bind_on_rows <- function(...) { 193 | list_of_arrays <- list(...) 194 | if (identical(length(list_of_arrays), 1L)) 195 | list_of_arrays <- list_of_arrays[[1]] 196 | 197 | bind_on_dim(list_of_arrays, which_dim = 1L) 198 | } 199 | 200 | #' @rdname bind-arrays 201 | #' @export 202 | bind_on_cols <- function(...) { 203 | list_of_arrays <- list(...) 204 | if (identical(length(list_of_arrays), 1L)) 205 | list_of_arrays <- list_of_arrays[[1]] 206 | 207 | bind_on_dim(list_of_arrays, which_dim = -1L) 208 | } 209 | -------------------------------------------------------------------------------- /R/dim.R: -------------------------------------------------------------------------------- 1 | #' Reshape an array 2 | #' 3 | #' Pipe friendly `dim<-()`, with option to pad to necessary length. Also allows 4 | #' for filling the array using C style row-major semantics. 5 | #' 6 | #' @param x A vector or array to set dimensions on 7 | #' @param new_dim The desired dimensions (an integer(ish) vector) 8 | #' @param pad The value to pad the vector with. `NULL` (the default) performs no 9 | #' padding. 10 | #' @param order whether to use row-major (C) or column major (F) style 11 | #' semantics. The default, "F", corresponds to the default behavior of R's 12 | #' `dim<-()`, while "C" corresponds to the default behavior of 13 | #' `reticulate::array_reshape()`, numpy, reshaping semantics commonly 14 | #' encountered in the python world. 15 | #' @param verbose Whether to emit a message if padding. By default, `FALSE`. 16 | #' 17 | #' 18 | #' @return Object with dimensions set 19 | #' @export 20 | #' @rdname set_dim 21 | #' 22 | #' @seealso `set_dim2()`, \code{`dim<-`()}, `reticulate::array_reshape()` 23 | #' 24 | #' @examples 25 | #' set_dim(1:10, c(2, 5)) 26 | #' try( set_dim(1:7, c(2, 5)) ) # error by default, just like `dim<-`() 27 | #' set_dim(1:7, c(2, 5), pad = 99) 28 | #' set_dim(1:7, c(2, 5), pad = 99, order = "C") # fills row-wise 29 | #' 30 | #' y <- x <- 1:4 31 | #' # base::dim<- fills the array column wise 32 | #' dim(x) <- c(2, 2) 33 | #' x 34 | #' 35 | #' # dim2 will fill the array row-wise 36 | #' dim2(y) <- c(2, 2) 37 | #' y 38 | #' 39 | #' identical(x, set_dim(1:4, c(2,2))) 40 | #' identical(y, set_dim(1:4, c(2,2), order = "C")) 41 | #' 42 | #' \dontrun{ 43 | #' py_reshaped <- reticulate::array_reshape(1:4, c(2,2)) 44 | #' storage.mode(py_reshaped) <- "integer" # reticulate coerces to double 45 | #' identical(y, py_reshaped) 46 | #' # if needed, see listarrays:::array_reshape() for 47 | #' # a drop-in pure R replacement for reticulate::array_reshape() 48 | #' } 49 | set_dim <- function(x, new_dim, 50 | pad = getOption("listarrays.autopad_arrays_with", NULL), 51 | order = c("F", "C"), 52 | verbose = getOption("verbose")) { 53 | 54 | if (!is.null(pad) && !identical(length(x), needed_len <- prod(new_dim))) { 55 | stopifnot(identical(length(pad), 1L)) 56 | if (verbose) 57 | message("Padding vector with ", pad, "s", 58 | " from length ", length(x), " to length ", needed_len) 59 | x <- c(x, rep_len(pad, needed_len - length(x))) 60 | } 61 | 62 | order <- match.arg(order) 63 | if (identical(order, "C")) 64 | dim2(x) <- new_dim 65 | else 66 | dim(x) <- new_dim 67 | 68 | x 69 | } 70 | 71 | 72 | 73 | #' Expand the shape of an array 74 | #' 75 | #' This is the inverse operation of `base::drop()`. 76 | #' It is analogous to python's `numpy.expand_dims()`, but vectorized on 77 | #' `which_dim`. 78 | #' 79 | #' @param x an array. Bare vectors are treated as 1-d arrays. 80 | #' @param which_dim numeric. Desired index position of the new axis or axes in 81 | #' the returned array. Negative numbers count from the back. Can be any 82 | #' length.Throws a warning if any duplicates are provided. 83 | #' 84 | #' 85 | #' @return the array `x` with new dim 86 | #' @export 87 | #' 88 | #' @examples 89 | #' x <- array(1:24, 2:4) 90 | #' dim(x) 91 | #' dim(expand_dims(x)) 92 | #' dim(expand_dims(x, 2)) 93 | #' dim(expand_dims(x, c(1,2))) 94 | #' dim(expand_dims(x, c(1,-1))) 95 | #' dim(expand_dims(x, 6)) # implicitly also expands dims 4,5 96 | #' dim(expand_dims(x, 4:6)) 97 | #' 98 | #' # error, implicit expansion with negative indexes not supported 99 | #' try(expand_dims(x, -6)) 100 | #' 101 | #' # supply them explicitly instead 102 | #' dim(expand_dims(x, -(4:6))) 103 | expand_dims <- function(x, which_dim = -1L) { 104 | d <- DIM(x) 105 | nd <- length(d) 106 | nwd <- length(which_dim) 107 | 108 | stopifnot(is.integerish(which_dim)) 109 | wd <- which_dim 110 | storage.mode(wd) <- "integer" 111 | 112 | 113 | neg <- wd < 0L 114 | if(any(neg)) 115 | wd[neg] <- wd[neg] + nd + nwd + 1L 116 | 117 | if (min(wd) < 1L) 118 | stop("Implicit additional dims for expansion with negative indexes not supported") 119 | 120 | if ((max_wd <- max(wd)) > nd + nwd) { 121 | # implicitly pad on right 122 | wd <- unique(c(wd, (nd + 1L):max_wd)) 123 | ndout <- max_wd 124 | } else 125 | ndout <- nd + nwd 126 | 127 | 128 | if(anyDuplicated(wd)) { 129 | warning("Duplicate axis specified, ignored") 130 | wd <- unique(wd) 131 | } 132 | 133 | dims <- rep(1L, ndout) 134 | dims[-wd] <- d 135 | 136 | dim(x) <- dims 137 | x 138 | } 139 | -------------------------------------------------------------------------------- /R/dim2.R: -------------------------------------------------------------------------------- 1 | #' Make or reshape an array with C-style (row-major) semantics 2 | #' 3 | #' These functions reshape or make an array using C-style, row-major semantics. 4 | #' The returned array is still R's native F-style, (meaning, the underlying 5 | #' vector has been reordered). 6 | #' 7 | #' Other than the C-style semantics, these functions behave identically to their 8 | #' counterparts (`array2()` behaves identically to `array()`, \code{`dim2<-`()} 9 | #' to \code{`dim<-`()}). `set_dim2()` is just a wrapper around `set_dim(..., 10 | #' order = "C")`. 11 | #' 12 | #' See examples for a drop-in pure R replacement to `reticulate::array_reshape()` 13 | #' 14 | #' @param data what to fill the array with 15 | #' @param dim numeric vector of dimensions 16 | #' @param dimnames a list of dimnames, must be the same length as `dims` 17 | #' 18 | #' @export 19 | #' @examples 20 | #' array(1:4, c(2,2)) 21 | #' array2(1:4, c(2,2)) 22 | #' 23 | #' # for a drop-in replacement to reticulate::array_reshape 24 | #' array_reshape <- listarrays:::array_reshape 25 | #' array_reshape(1:4, c(2,2)) 26 | array2 <- function(data, dim = length(data), dimnames = NULL) { 27 | pd <- prod(dim) 28 | if(length(data) != pd) 29 | data <- rep_len(data, pd) 30 | dim2(data) <- dim 31 | dimnames(data) <- dimnames 32 | data 33 | } 34 | 35 | 36 | #' @export 37 | #' @rdname array2 38 | matrix2 <- function(...) 39 | matrix(..., byrow = TRUE) 40 | 41 | #' @export 42 | #' @rdname array2 43 | #' @param x object to set dimensions on (array or atomic vector) 44 | #' @param value a numeric (integerish) vector of new dimensions 45 | `dim2<-` <- function(x, value) { 46 | if (is.null(value)) { 47 | if (is.null(dim(x) -> dx)) 48 | return(x) 49 | 50 | if(length(dx) > 1L) 51 | x <- t(x) 52 | 53 | dim(x) <- NULL 54 | 55 | return(x) 56 | } 57 | 58 | dim_x <- dim(x) 59 | if(identical(dim_x, as.integer(value))) 60 | return(x) 61 | 62 | if (!is.null(dim_x)) 63 | x <- t(x) 64 | 65 | dim(x) <- rev(value) 66 | t(x) 67 | } 68 | 69 | 70 | #' @export 71 | #' @rdname array2 72 | #' @param ... passed on to `set_dim()` 73 | set_dim2 <- function(...) { 74 | set_dim(..., order = "C") 75 | } 76 | 77 | 78 | 79 | # equivelant to reticulate::array_reshape(), 80 | # but a pure R solution (and therefore usually faster) 81 | array_reshape <- function(x, dim, order = c("C", "F")) { 82 | 83 | # rename to avoid possible recursive loop when calling dim() 84 | # arg is named `dim` for compatability with reticulate::array_reshape() 85 | new_dim <- dim; rm(dim) 86 | 87 | order <- match.arg(order) 88 | if (identical(order, "C")) 89 | dim2(x) <- new_dim 90 | else 91 | dim(x) <- new_dim 92 | 93 | # match reticulate behavior 94 | if(identical(storage.mode(x), "integer")) 95 | storage.mode(x) <- "double" 96 | 97 | x 98 | } 99 | 100 | 101 | #' transpose an array 102 | #' 103 | #' @param x an array 104 | #' 105 | #' This reverses the dimensions of an array 106 | #' 107 | #' #export 108 | #' @noRd 109 | #' @examples 110 | #' x <- array(1:27, c(3,3,3)) 111 | #' tx <- t(x) 112 | #' for (i in 1:3) 113 | #' for(j in 1:3) 114 | #' stopifnot(x[,j,i] == tx[i,j,]) 115 | 116 | # this is no longer exported because it is now invoked for 2d arrays (matrixes) 117 | # too, and before dispatch to the primitive. This introduces substantial 118 | # overhead in code that would otherwise not dispatch. Additionally, aperm() does 119 | # not preserve attributes. This was discovered when utils::getParseData() was 120 | # raising an error, because the expression `t(unclass(data))` was losing 121 | # attributes(data) if listarrays was loaded. 122 | 123 | # t.array <- 124 | function(x) { 125 | if(is.matrix(x)) return(NextMethod()) # copies attrs already 126 | 127 | # handle bug in aperm(), R 4.3.2. aperm() docs say it copies over other attrs, 128 | # but in actuality, it doesn't. 129 | out <- aperm(x) 130 | attrs <- attributes(x) 131 | attrs$dim <- attrs$dimnames <- NULL 132 | attributes(out) <- a 133 | out 134 | } 135 | -------------------------------------------------------------------------------- /R/dimnames.R: -------------------------------------------------------------------------------- 1 | #' Set dimnames 2 | #' 3 | #' A more flexible and pipe-friendly version of `dimnames<-`. 4 | #' 5 | #' @param x an array 6 | #' @param nm A list or character vector. 7 | #' @param which_dim a character vector or numeric vector or `NULL` 8 | #' 9 | #' @details This function is quite flexible. See examples for the complete 10 | #' picture. 11 | #' 12 | #' @note The word "dimnames" is slightly overloaded. Most commonly it refers to 13 | #' the names of entries along a particular axis (e.g., date1, date2, date3, 14 | #' ...), but occasionally it is also used to refer to the names of the array 15 | #' axes themselves (e.g, dates, temperature, pressure, ...). To disambiguate, 16 | #' in the examples 'dimnames' always refers to the first case, while 'axis 17 | #' names' refers to the second. `set_dimnames()` can be used to set either or both 18 | #' axis names and dimnames. 19 | #' 20 | #' @return x, with modified dimnames and or axisnames 21 | #' @export 22 | #' @importFrom utils modifyList 23 | #' 24 | #' @examples 25 | #' x <- array(1:8, 2:4) 26 | #' 27 | #' # to set axis names, leave which_dim=NULL and pass a character vector 28 | #' dimnames(set_dimnames(x, c("a", "b", "c"))) 29 | #' 30 | #' # to set names along a single axis, specify which_dim 31 | #' dimnames(set_dimnames(x, c("a", "b", "c"), 2)) 32 | #' 33 | #' # to set an axis name and names along the axis, pass a named list 34 | #' dimnames(set_dimnames(x, list(axis2 = c("a", "b", "c")), 2)) 35 | #' dimnames(set_dimnames(x, list(axis2 = c("a", "b", "c"), 36 | #' axis3 = 1:4), which_dim = 2:3)) 37 | #' 38 | #' # if the array already has axis names, those are used when possible 39 | #' nx <- set_dimnames(x, paste0("axis", 1:3)) 40 | #' dimnames(nx) 41 | #' dimnames(set_dimnames(nx, list(axis2 = c("x", "y", "z")))) 42 | #' dimnames(set_dimnames(nx, c("x", "y", "z"), which_dim = "axis2")) 43 | #' 44 | #' 45 | #' # pass NULL to drop all dimnames, or just names along a single dimension 46 | #' nx2 <- set_dimnames(nx, c("x", "y", "z"), which_dim = "axis2") 47 | #' nx2 <- set_dimnames(nx2, LETTERS[1:4], which_dim = "axis3") 48 | #' dimnames(nx2) 49 | #' dimnames(set_dimnames(nx2, NULL)) 50 | #' dimnames(set_dimnames(nx2, NULL, 2)) 51 | #' dimnames(set_dimnames(nx2, NULL, c(2, 3))) 52 | #' # to preserve an axis name and only drop the dimnames, wrap the NULL in a list() 53 | #' dimnames(set_dimnames(nx2, list(NULL))) 54 | #' dimnames(set_dimnames(nx2, list(NULL), 2)) 55 | #' dimnames(set_dimnames(nx2, list(axis2 = NULL))) 56 | #' dimnames(set_dimnames(nx2, list(axis2 = NULL, axis3 = NULL))) 57 | #' dimnames(set_dimnames(nx2, list(NULL), 2:3)) 58 | set_dimnames <- function(x, nm, which_dim = NULL) { 59 | if (is.null(nm)) 60 | return(drop_dimnames(x, which_dim)) 61 | 62 | else if (is.list(nm)) { 63 | nm <- lapply(nm, as.character) 64 | if (identical(nm, list(character()))) 65 | return(drop_dimnames(x, which_dim, keep_axis_names = TRUE)) 66 | } else 67 | nm <- as.character(nm) 68 | 69 | dim_nms <- dimnames(x) %||% vector("list", ndim(x)) 70 | 71 | if (is.null(which_dim)) { 72 | if (is.character(nm)) { 73 | stopifnot(identical(length(nm), length(dim(x)))) 74 | names(dim_nms) <- nm 75 | dimnames(x) <- dim_nms 76 | return(x) 77 | 78 | } else if (is.list(nm)) { 79 | # if x dims are named and nm is a named list, match names 80 | if (!is.null(names(nm)) && 81 | all(nzchar(names(nm))) && 82 | all(names(nm) %in% names(dim_nms))) { 83 | dim_nms <- modifyList(dim_nms, nm, keep.null = TRUE) 84 | dimnames(x) <- dim_nms 85 | return(x) 86 | 87 | } else { 88 | stopifnot(length(nm) == dim(x)) 89 | if (!is.null(names(nm))) 90 | warning("Names supplied to `nm` are ignored") 91 | dimnames(x) <- nm 92 | return(x) 93 | } 94 | } 95 | } else { # which_dim supplied 96 | if(is.character(which_dim)) { 97 | stopifnot(all(which_dim %in% names(dimnames(x)))) 98 | which_dim <- match(which_dim, names(dimnames(x))) 99 | } 100 | 101 | if (is.character(nm)) { 102 | stopifnot(identical(length(which_dim), 1L), 103 | identical(length(nm), dim(x)[which_dim])) 104 | dimnames(x)[[which_dim]] <- nm 105 | return(x) 106 | 107 | } else { # nm is a list and dim supplied 108 | 109 | stopifnot(identical(length(nm), length(which_dim))) 110 | for (i in seq_along(nm)) { 111 | dim_nms[[which_dim[i]]] <- nm[[i]] 112 | } 113 | 114 | if(!is.null(names(nm))) { 115 | new_axis_nms <- names(dim_nms) %||% character(length(dim_nms)) 116 | new_axis_nms[which_dim] <- names(nm) 117 | new_axis_nms[!nzchar(new_axis_nms)] <- 118 | names(dim_nms)[!nzchar(new_axis_nms)] %||% "" 119 | names(dim_nms) <- new_axis_nms 120 | } 121 | 122 | dimnames(x) <- dim_nms 123 | return(x) 124 | 125 | } # end nm is a list and dim supplied 126 | 127 | } # end else which_dim supplied 128 | stop("invalid input") 129 | } 130 | 131 | 132 | #' Drop dimnames 133 | #' 134 | #' A pipe-friendly wrapper for `dim(x) <- NULL` and `dimnames(x) <- NULL` or, if 135 | #' `which_dim` is not `NULL`, \code{dimnames(x)[which_dim] <- list(NULL)} 136 | #' 137 | #' @param x an object, potentially with dimnames 138 | #' @param which_dim If `NULL` (the default) then all dimnames are dropped. If 139 | #' integer vector, then dimnames only at the specified dimensions are dropped. 140 | #' @param keep_axis_names TRUE or FALSE, whether to preserve the axis names when 141 | #' dropping the dimnames 142 | #' 143 | #' @export 144 | drop_dimnames <- function(x, which_dim = NULL, keep_axis_names = FALSE) { 145 | if(is.null(which_dim)) { 146 | if(keep_axis_names) 147 | dimnames(x) <- lapply(dimnames(x), function(...) NULL) 148 | else 149 | dimnames(x) <- NULL 150 | } else { 151 | which_dim <- standardize_which_dim(which_dim, x, multiple_OK = TRUE) 152 | dimnames(x)[which_dim] <- list(NULL) 153 | if(!keep_axis_names) 154 | names(dimnames(x))[which_dim] <- "" 155 | } 156 | x 157 | } 158 | 159 | #' @rdname drop_dimnames 160 | #' @export 161 | drop_dim <- function(x) { 162 | dim(x) <- NULL 163 | x 164 | } 165 | 166 | #' @rdname drop_dimnames 167 | #' @export 168 | drop_dim2 <- function(x) { 169 | dim2(x) <- NULL 170 | x 171 | } 172 | -------------------------------------------------------------------------------- /R/extract.R: -------------------------------------------------------------------------------- 1 | #' Extract with `[` on a specified dimension 2 | #' 3 | #' @param X Typically, an array, but any object with a `[` method is accepted 4 | #' (e.g., dataframe, vectors) 5 | #' @param which_dim A scalar integer or character, specifying the dimension to 6 | #' extract from 7 | #' @param idx A numeric, boolean, or character vector to perform subsetting 8 | #' with. 9 | #' @param drop Passed on to `[`. If `NULL` (the default), then drop is omitted 10 | #' from the argument, and the default is used (defaults to TRUE for most 11 | #' objects, including arrays) 12 | #' @param depth Scalar number, how many levels to recurse down if `X` is a list 13 | #' of arrays. Set this if you want to explicitly treat a list as a vector 14 | #' (that is, a one-dimensional array). (You can alternatively set a dim 15 | #' attribute with `dim<-` on the list to prevent recursion) 16 | #' 17 | #' @export 18 | #' 19 | #' @examples 20 | #' # extract_rows is useful to keep the same code path for arrays of various sizes 21 | #' X <- array(1:8, c(4, 3, 2)) 22 | #' y <- c("a", "b", "c", "d") 23 | #' (Y <- onehot(y)) 24 | #' 25 | #' extract_rows(X, 2) 26 | #' extract_rows(Y, 2) 27 | #' extract_rows(y, 2) 28 | #' 29 | #' library(zeallot) 30 | #' c(X2, Y2, y2) %<-% extract_rows(list(X, Y, y), 2) 31 | #' X2 32 | #' Y2 33 | #' y2 34 | extract_dim <- function(X, which_dim, idx, drop = NULL, depth = Inf) { 35 | which_dim <- standardize_which_dim(which_dim, X) 36 | 37 | if(is.list(X) && is.null(dim(X)) && depth > 0L) 38 | return(lapply(X, function(x) 39 | extract_dim(x, which_dim, idx, drop = drop, depth = depth - 1L))) 40 | 41 | expr <- extract_dim_expr(X, which_dim, idx_var_sym = quote(idx), drop = drop) 42 | eval(expr) 43 | } 44 | 45 | 46 | #' @rdname extract_dim 47 | #' @export 48 | extract_rows <- function(X, idx, drop = NULL, depth = Inf) 49 | extract_dim(X, 1L, idx, drop = drop, depth = depth) 50 | 51 | #' @rdname extract_dim 52 | #' @export 53 | extract_cols <- function(X, idx, drop = NULL, depth = Inf) 54 | extract_dim(X, -1L, idx, drop = drop, depth = depth) 55 | 56 | 57 | 58 | 59 | extract_dim_chr_expr <- 60 | function(X, which_dim, drop = NULL, ndims = ndim(X), 61 | idx_var_nm = names(which_dim) %||% 62 | paste0("idx", if(length(which_dim) > 1L) seq_along(which_dim)), 63 | var_to_subset = deparse(substitute(X))) { 64 | 65 | force(var_to_subset) 66 | 67 | which_dim <- as.integer(which_dim) 68 | stopifnot(identical(length(idx_var_nm), length(which_dim)), 69 | !anyDuplicated(idx_var_nm)) 70 | 71 | args <- character(ndims) 72 | args[which_dim] <- idx_var_nm 73 | 74 | if(!is.null(drop)) 75 | args <- c(args, " drop = drop") 76 | 77 | args <- paste0(args, collapse = ",") 78 | sprintf("%s[%s]", var_to_subset, args) 79 | } 80 | 81 | 82 | extract_dim_expr <- 83 | function(X, 84 | which_dim, 85 | drop = NULL, 86 | ndims = ndim(X), 87 | idx_var_sym = names(which_dim) %||% 88 | paste0("idx", if (length(which_dim) > 1L) 89 | seq_along(which_dim)), 90 | var_to_subset = substitute(X)) { 91 | args <- rep(list(quote(expr =)), ndims) 92 | args[[which_dim]] <- idx_var_sym 93 | if(!is.null(drop)) 94 | args$drop <- drop 95 | as.call(c(quote(`[`), var_to_subset, args)) 96 | 97 | } 98 | 99 | 100 | 101 | # 102 | # extract_call <- 103 | # function(X, 104 | # which_dim, 105 | # drop = NULL, 106 | # ndims = ndim(X), 107 | # idx_var_sym = names(which_dim) %||% 108 | # paste0("idx", if (length(which_dim) > 1L) 109 | # seq_along(which_dim)), 110 | # var_to_subset = substitute(X)) { 111 | # args <- rep(list(quote(expr =)), ndims) 112 | # args[[which_dim]] <- idx_var_sym 113 | # if(!is.null(drop)) 114 | # args$drop <- drop 115 | # as.call(c(quote(`[`), var_to_subset, args)) 116 | # 117 | # } 118 | -------------------------------------------------------------------------------- /R/map.R: -------------------------------------------------------------------------------- 1 | #' Apply a function across subsets along an array dimension 2 | #' 3 | #' @description 4 | #' `map_along_dim(X, dim, func)` is a simple wrapper around `split_along_dim(X, 5 | #' dim) %>% map(func)`. It is conceptually and functionally equivalent to 6 | #' `base::apply()`, with the following key differences: 7 | #' 8 | #' + it is guaranteed to return a list (`base::apply()` attempts to simplify the 9 | #' output to an array, sometimes unsuccessfully, making the output unstable) 10 | #' 11 | #' + it accepts the compact lambda notation `~.x` just like in [`purrr::map`] 12 | #' (and [`modify_along_dim()`]) 13 | #' 14 | #' 15 | #' @param X an R array 16 | #' @param .dim which dimension to map along. Passed on to [`split_along_dim()`], 17 | #' and accepts all the same inputs. Valid inputs include 18 | #' 19 | #' * positive integers (index position(s) of dimension), 20 | #' * negative integers (index positions(s) of dimensions, counting from the back), or 21 | #' * character vector (corresponding to array dimnames) 22 | #' @param .f A function, string of a function name, or `purrr` style compact lambda syntax (e.g, `~.x + 1`) 23 | #' @param ... passed on to `.f()` 24 | #' 25 | #' @return An R list 26 | #' @export 27 | #' 28 | #' @rdname map_along_dim 29 | #' @examples 30 | #' X <- matrix2(letters[1:15], ncol = 3) 31 | #' 32 | #' apply(X, 1, function(x) paste(x, collapse = "")) # simplifies to a vector 33 | #' map_along_dim(X, 1, ~paste(.x, collapse = "")) # returns a list 34 | #' 35 | #' identical( 36 | #' map_along_rows(X, identity), 37 | #' map_along_dim(X, 1, identity)) # TRUE 38 | #' 39 | #' identical( 40 | #' map_along_cols(X, identity), 41 | #' map_along_dim(X, -1, identity)) # TRUE 42 | map_along_dim <- function(X, .dim, .f, ...) { 43 | stopifnot(is.array(X)) 44 | if (requireNamespace("rlang", quietly = TRUE)) { 45 | .f <- rlang::as_function(.f) 46 | } else { 47 | if (inherits(.f, "formula")) 48 | stop("Specifing functions via forumla syntax requires ", 49 | "package rlang to be available") 50 | .f <- match.fun(.f) 51 | } 52 | lapply(split_along_dim(X, .dim), .f, ...) 53 | } 54 | 55 | #' @export 56 | #' @rdname map_along_dim 57 | map_along_rows <- function(X, .f, ...) 58 | map_along_dim(X, 1L, .f, ...) 59 | 60 | #' @export 61 | #' @rdname map_along_dim 62 | map_along_cols <- function(X, .f, ...) 63 | map_along_dim(X, -1L, .f, ...) 64 | 65 | 66 | 67 | ## Maybe add this? 68 | # map_*_dim <- function(x, which_dim, .f) { 69 | # map(x, .f) %>% 70 | # bind_*_dim() 71 | # } 72 | -------------------------------------------------------------------------------- /R/modify.R: -------------------------------------------------------------------------------- 1 | 2 | #' Modify an array by mapping over 1 or more dimensions 3 | #' 4 | #' This function can be thought of as a version of `base::apply()` that is 5 | #' guaranteed to return a object of the same dimensions as it was input. It also 6 | #' generally preserves attributes, as it's built on top of `[<-`. 7 | #' 8 | #' @param X An array, or a list of arrays 9 | #' @param which_dim integer vector of dimensions to modify at 10 | #' @param .f a function or formula defining a function(same semantics as 11 | #' [`purrr::map()`]). The function must return either an array the same shape 12 | #' as it was passed, a vector of the same length, or a scalar, although the 13 | #' type of the returned object does not need to be the same as was passed in. 14 | #' @param ... passed on to `.f()` 15 | #' 16 | #' @export 17 | #' @rdname modify_along_dim 18 | #' @return An array, or if `X` was a list, a list of arrays of the same shape as 19 | #' was passed in. 20 | #' @examples 21 | #' x <- array(1:6, 1:3) 22 | #' modify_along_dim(x, 3, ~mean(.x)) 23 | #' modify_along_dim(x, 3, ~.x/mean(.x)) 24 | modify_along_dim <- function(X, which_dim, .f, ...) { 25 | 26 | if(is.list(X) && is.null(dim(X))) 27 | return(lapply(X, function(x) modify_along_dim(x, which_dim, .f, ...))) 28 | 29 | if (requireNamespace("rlang", quietly = TRUE)) { 30 | .f <- rlang::as_function(.f) 31 | } else { 32 | if (inherits(.f, "formula")) 33 | stop("Specifing functions via forumla syntax requires ", 34 | "package rlang to be available") 35 | .f <- match.fun(.f) 36 | } 37 | 38 | which_dim <- standardize_which_dim(which_dim, X, multiple_OK = TRUE) 39 | 40 | names(which_dim) <- paste0("idx", seq_along(which_dim)) 41 | 42 | Xe <- extract_dim_chr_expr(X, which_dim, 43 | idx_var_nm = names(which_dim), 44 | var_to_subset = "X") 45 | 46 | loop_controlflow <- paste0( 47 | "for (", names(which_dim), 48 | " in .seq_along_dim(X,", as.integer(which_dim), "))", 49 | collapse = "\n") 50 | 51 | args <- as.pairlist(alist(X = , .f = , ... = )) 52 | body <- parse1(sprintf("{ 53 | oX <- X 54 | storage.mode(X) <- 'logical' 55 | %s 56 | %s <- .f(o%s, ...) 57 | X 58 | }", loop_controlflow, Xe, Xe)) 59 | 60 | modify_it <- eval(call("function", args, body)) 61 | 62 | if (prod(DIM(X)[which_dim]) > 100) 63 | modify_it <- cmpfun(modify_it) 64 | 65 | modify_it(X, .f, ...) 66 | } 67 | 68 | #' @export 69 | #' @rdname modify_along_dim 70 | modify_along_rows <- function(X, .f, ...) 71 | modify_along_dim(X, 1L, .f, ...) 72 | 73 | #' @export 74 | #' @rdname modify_along_dim 75 | modify_along_cols <- function(X, .f, ...) 76 | modify_along_dim(X, -1L, .f, ...) 77 | 78 | MODIFY_ALONG_FN_TEMPLATE <- alist(X = , .f = , ... = , { 79 | oX <- X 80 | storage.mode(X) <- "logical" 81 | for (idx1 in .seq_along_dim(X, 3)) 82 | X[, , idx1] <- .f(oX[, , idx1], ...) 83 | X 84 | }) 85 | -------------------------------------------------------------------------------- /R/onehot.R: -------------------------------------------------------------------------------- 1 | #' Convert vector to a onehot representation (binary class matrix) 2 | #' 3 | #' @param y character, factor, or numeric vector 4 | #' @param Y a matrix, as returned by `onehot()` or similar. 5 | #' @param order `NULL`, `FALSE`, or a character vector. If `NULL` (the default), 6 | #' then levels are sorted with `sort()`. If `FALSE`, then levels are taken in 7 | #' order of their first appearance in `y`. If a character vector, then `order` 8 | #' must contain all levels found in `y`. 9 | #' @param named if the returned matrix should have column names 10 | #' @param classes A character vector of class names in the order corresponding 11 | #' to `Y`'s onehot encoding. Typically, `colnames(Y)`. if `NULL`, then the 12 | #' decoder returns the column number. 13 | #' @param n_classes The total number of classes expected in `Y`. Used for input 14 | #' checking in the returned decoder, also, to reconstruct the correct 15 | #' dimensions if the passed in `Y` is missing `dim()` attributes. 16 | #' 17 | #' @return A binary class matrix 18 | #' @export 19 | #' @seealso [keras::to_categorical] 20 | #' @rdname onehot 21 | #' 22 | #' @examples 23 | #' if(require(zeallot)) { 24 | #' y <- letters[1:4] 25 | #' c(Y, decode) %<-% onehot_with_decoder(y) 26 | #' Y 27 | #' decode(Y) 28 | #' identical(y, decode(Y)) 29 | #' decode(Y[2,,drop = TRUE]) 30 | #' decode(Y[2,,drop = FALSE]) 31 | #' decode(Y[2:3,]) 32 | #' 33 | #' rm(Y, decode) 34 | #' } 35 | #' 36 | #' # more peicemeal functions 37 | #' Y <- onehot(y) 38 | #' decode_onehot(Y) 39 | #' 40 | #' # if you need to decode a matrix that lost colnames, 41 | #' # make your own decoder that remembers classes 42 | #' my_decode <- onehot_decoder(Y) 43 | #' colnames(Y) <- NULL 44 | #' my_decode(Y) 45 | #' decode_onehot(Y) 46 | #' 47 | #' # factor and numeric vectors also accepted 48 | #' onehot(factor(letters[1:4])) 49 | #' onehot(4:8) 50 | #' 51 | onehot_with_decoder <- function(y, order = NULL, named = TRUE) { 52 | 53 | Y <- onehot(y, order = order, named = TRUE) 54 | decode <- onehot_decoder(classes = colnames(Y), 55 | n_classes = ncol(Y)) 56 | 57 | if(!isTRUE(named)) 58 | colnames(Y) <- NULL 59 | 60 | list(onehot = Y, decode = decode) 61 | } 62 | 63 | 64 | #' @export 65 | #' @rdname onehot 66 | onehot <- function(y, order = NULL, named = TRUE) { 67 | 68 | if (is.factor(y)) { 69 | 70 | if (!missing(order) && !is.null(order)) 71 | warning("value supplied to `order` is ignored since y is a factor") 72 | 73 | order <- levels(y) 74 | ncats <- length(order) 75 | idx_col <- unclass(y) 76 | 77 | } else if (is.character(y) || is.numeric(y)) { 78 | 79 | if (is.null(order)) 80 | order <- sort(unique(y)) 81 | else if (identical(order, FALSE)) 82 | order <- unique(y) 83 | else if (is.character(order) || is.numeric(order)) 84 | stopifnot(typeof(y) == typeof(order), unique(y) %in% order) 85 | else 86 | stop("`order` must be NULL, FALSE, or a character vector") 87 | 88 | idx_col <- match(y, order) 89 | ncats <- length(order) 90 | 91 | } else 92 | stop("`y` must be a character, numeric, or factor") 93 | 94 | Y <- array(0, dim = c(DIM(y), ncats)) 95 | idx <- cbind(seq_along(y), idx_col, deparse.level = 0L) 96 | Y[idx] <- 1 97 | 98 | if(named) 99 | colnames(Y) <- as.character(order) 100 | 101 | Y 102 | } 103 | 104 | 105 | #' @export 106 | #' @rdname onehot 107 | decode_onehot <- function(Y, classes = colnames(Y), 108 | n_classes = ncol(Y) %||% length(classes)) { 109 | decode <- onehot_decoder(classes = classes, n_classes = n_classes) 110 | decode(Y) 111 | } 112 | 113 | #' @export 114 | #' @rdname onehot 115 | onehot_decoder <- function(Y, classes = colnames(Y), n_classes = length(classes)) { 116 | force(classes) 117 | n_classes <- as.integer(n_classes) 118 | rm(Y) 119 | 120 | if(n_classes <= 0L || is.na(n_classes)) 121 | stop("`n_classes` must be a scalar integer greater than 0") 122 | 123 | robust_max.col <- function(m) { 124 | if (is.matrix(m)) 125 | stopifnot(identical(ncol(m), n_classes)) 126 | else { # dim was probably dropped by [, drop = TRUE] 127 | if (length(m) %% n_classes) 128 | stop("length(Y) must be a multiple of n_classes, ", n_classes, 129 | ", not", length(m)) 130 | dim(m) <- c(length(m) %/% n_classes, n_classes) 131 | } 132 | 133 | max.col(m) 134 | } 135 | 136 | if (is.null(classes)) { 137 | function(Y) robust_max.col(Y) 138 | } else { 139 | function(Y) classes[robust_max.col(Y)] 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /R/reshape.R: -------------------------------------------------------------------------------- 1 | #' Reshape an array to send a dimension forward or back 2 | #' 3 | #' @param X an array 4 | #' @param which_dim scalar integer or string, which dim to bring forward. 5 | #' Negative numbers count from the back 6 | #' 7 | #' This is a powered by `base::aperm()`. 8 | #' 9 | #' @return a reshaped array 10 | #' @export 11 | #' 12 | #' @seealso `base::aperm()` `set_dim()` `keras::array_reshape()` 13 | #' 14 | #' @examples 15 | #' x <- array(1:24, 2:4) 16 | #' y <- set_as_rows(x, 3) 17 | #' 18 | #' for (i in seq_along_dim(x, 3)) 19 | #' stopifnot( identical(x[,,i], y[i,,]) ) 20 | set_as_rows <- function(X, which_dim) { 21 | stopifnot(is.array(X)) 22 | 23 | which_dim <- standardize_which_dim(which_dim, X) 24 | 25 | cur_dim_order <- seq_along(dim(X)) 26 | new_dim_order <- c(which_dim, cur_dim_order[-which_dim]) 27 | 28 | aperm(X, new_dim_order) 29 | } 30 | 31 | # other name ideas: 32 | # bring_dim_forward() 33 | 34 | #' @export 35 | #' @rdname set_as_rows 36 | set_as_cols <- function(X, which_dim) { 37 | stopifnot(is.array(X)) 38 | which_dim <- standardize_which_dim(which_dim, X) 39 | 40 | cur_dim_order <- seq_along(dim(X)) 41 | new_dim_order <- c(cur_dim_order[-which_dim], which_dim) 42 | 43 | aperm(X, new_dim_order) 44 | } 45 | -------------------------------------------------------------------------------- /R/seq_along.R: -------------------------------------------------------------------------------- 1 | #' Sequence along a dimension 2 | #' 3 | #' @param x a dataframe, array or vector. For `seq_along_rows`, and 4 | #' `seq_along_cols` sequence along the first and last dimensions, 5 | #' respectively. Atomic vectors are treated as 1 dimensional 6 | #' arrays (i.e., `seq_along_rows` is equivalent to `seq_along` when `x` is an 7 | #' atomic vector or list). 8 | #' @param which_dim a scalar integer or character string, specifying which 9 | #' dimension to generate a sequence for. Negative numbers count from the back. 10 | #' 11 | #' @return a vector of integers 1:nrow(x), safe for use in `for` loops and 12 | #' vectorized equivalents. 13 | #' @export 14 | #' @examples 15 | #' for (r in seq_along_rows(mtcars[1:4,])) 16 | #' print(mtcars[r,]) 17 | #' 18 | #' x <- 1:3 19 | #' identical(seq_along_rows(x), seq_along(x)) 20 | #' 21 | #' @export 22 | seq_along_dim <- function(x, which_dim) 23 | seq_len( DIM(x)[[standardize_which_dim(which_dim, x)]] ) 24 | 25 | # assumes cannonical which_dim, no checking 26 | .seq_along_dim <- function(x, which_dim) 27 | seq_len( DIM(x)[[which_dim]] ) 28 | 29 | 30 | #' @rdname seq_along_dim 31 | #' @export 32 | seq_along_rows <- function(x) seq_along_dim(x, 1L) 33 | 34 | #' @rdname seq_along_dim 35 | #' @export 36 | seq_along_cols <- function(x) seq_along_dim(x, -1L) 37 | -------------------------------------------------------------------------------- /R/shuffle.R: -------------------------------------------------------------------------------- 1 | #' Shuffle along the first dimension multiple arrays in sync 2 | #' 3 | #' @param ... arrays of various dimensions (vectors and data.frames OK too) 4 | #' 5 | #' @return A list of objects passed on to `...`, or if a single object was 6 | #' supplied, then the single object shuffled 7 | #' @export 8 | #' 9 | #' @examples 10 | #' x <- 1:3 11 | #' y <- matrix(1:9, ncol = 3) 12 | #' z <- array(1:27, c(3,3,3)) 13 | #' 14 | #' if(require(zeallot)) { 15 | #' c(xs, ys, zs) %<-% shuffle_rows(x, y, z) 16 | #' 17 | #' l <- lapply(seq_along_rows(y), function(r) { 18 | #' list(x = x[r], y = y[r,], z = z[r,,]) 19 | #' }) 20 | #' 21 | #' ls <- lapply(seq_along_rows(y), function(r) { 22 | #' list(x = xs[r], y = ys[r,], z = zs[r,,]) 23 | #' }) 24 | #' 25 | #' stopifnot( 26 | #' length(unique(c(l, ls))) == length(l)) 27 | #' } 28 | shuffle_rows <- function(...) { 29 | l <- list(...) 30 | 31 | single_obj_in <- identical(length(l), 1L) 32 | single_list_in <- is.list(l[[1]]) && is.null(dim(l[[1]])) 33 | 34 | if(single_list_in) { 35 | single_obj_in <- FALSE 36 | l <- l[[1L]] 37 | } 38 | 39 | nrows <- unique(vapply(l, NROW, 0L)) 40 | if(!identical(length(nrows), 1L)) 41 | stop("All objects passed to `...` must have the same number of rows") 42 | 43 | idx <- sample.int(nrows) 44 | 45 | for (i in seq_along(l)) 46 | l[[i]] <- extract_rows(l[[i]], idx, drop = FALSE) 47 | 48 | if (single_obj_in) 49 | l[[1L]] 50 | else 51 | l 52 | } 53 | -------------------------------------------------------------------------------- /R/split.R: -------------------------------------------------------------------------------- 1 | #' Split an array along a dimension 2 | #' 3 | #' @param X an array, or list of arrays. An atomic vector without a dimension 4 | #' attribute is treated as a 1 dimensional array (Meaning, atomic vectors 5 | #' without a dim attribute are only accepted if `which_dim` is `1`. Names of 6 | #' the passed list are preserved. If a list of arrays, all the arrays must 7 | #' have the same length of the dimension being split. 8 | #' @param which_dim a scalar string or integer, specifying which dimension to 9 | #' split along. Negative integers count from the back. If a string, it must 10 | #' refer to a named dimension (e.g, one of `names(dimnames(X))`. 11 | #' @param f Specify how to split the dimension. \describe{ 12 | #' 13 | #' \item{character, integer, factor}{passed on to `base::split()`. Must be the 14 | #' same length as the dimension being split.} 15 | #' 16 | #' \item{a list of vectors}{Passed on to `base::interaction()` then 17 | #' `base::split()`. Each vector in the list must be the same length as the 18 | #' dimension being split.} 19 | #' 20 | #' \item{a scalar integer}{used to split into that many groups of equal size} 21 | #' 22 | #' \item{a numeric vector where \code{all(f<0)}}{specifies the relative size 23 | #' proportions of the groups being split. \code{sum(f)} must be \code{1}. For 24 | #' example \code{c(0.2, 0.2, 0.6)} will return approximately a 20\%-20\%-60\% 25 | #' split.} } 26 | #' @param drop passed on to `[`. 27 | #' @param depth Scalar number, how many levels to recurse down. Set this if you 28 | #' want to explicitly treat a list as a vector (that is, a one-dimensional 29 | #' array). (You can alternatively set dim attributes with `dim<-` on the list 30 | #' to prevent recursion) 31 | #' 32 | #' `split_along_dim(X, which_dim)` is equivalent to `split_on_dim(X, 33 | #' which_dim, seq_along_dim(X, which_dim))`. 34 | #' 35 | #' @return A list of arrays, or if a list of arrays was passed in, then a list 36 | #' of lists of arrays. 37 | #' @rdname split-array 38 | #' @export 39 | #' 40 | #' @examples 41 | #' X <- array(1:8, c(2,3,4)) 42 | #' X 43 | #' split_along_dim(X, 2) 44 | #' 45 | #' # specify f as a factor, akin to base::split() 46 | #' split_on_dim(X, 2, c("a", "a", "b"), drop = FALSE) 47 | #' 48 | #' d <- c(10, 3, 3) 49 | #' X <- array(1:prod(d), d) 50 | #' y <- letters[1:10] 51 | #' Y <- onehot(y) 52 | #' 53 | #' # specify `f`` as relative partition sizes 54 | #' if(require(zeallot) && require(magrittr) && require(purrr)) { 55 | #' 56 | #' c(train, validate, test) %<-% { 57 | #' list(X = X, Y = Y, y = y) %>% 58 | #' shuffle_rows() %>% 59 | #' split_on_rows(c(0.6, 0.2, 0.2)) %>% 60 | #' transpose() 61 | #' } 62 | #' 63 | #' str(test) 64 | #' str(train) 65 | #' str(validate) 66 | #' 67 | #' } 68 | #' 69 | #' 70 | #' # with with array data in a data frame by splitting row-wise 71 | #' if(require(tibble)) 72 | #' tibble(y, X = split_along_rows(X)) 73 | split_on_dim <- function(X, which_dim, 74 | f = dimnames(X)[[which_dim]], 75 | drop = FALSE, depth = Inf) { 76 | 77 | stopifnot(!is.null(f)) 78 | 79 | if(is.list(f)) 80 | f <- interaction(f, drop = TRUE) 81 | 82 | if (is.list(X) && is.null(dim(X)) && depth > 0L) 83 | return(lapply(X, function(x) 84 | split_on_dim(x, which_dim, f = f, drop = drop, depth = depth - 1L))) 85 | 86 | which_dim <- standardize_which_dim(which_dim, X) 87 | 88 | id <- .seq_along_dim(X, which_dim) 89 | 90 | if(is.scalar.integerish(f)) 91 | f <- cut(id, f, labels = paste0("grp", seq_len(f))) 92 | else if (is.numeric(f) && all(f < 1)) { 93 | stopifnot(sum(f) == 1) 94 | f <- cut(id, c(0, cumsum(f) * length(id)), 95 | labels = names(f) %||% paste0("grp", seq_along(f))) 96 | } 97 | 98 | if (!identical(length(id), length(f))) 99 | stop("`f` must be the same length as the dimension being split on.") 100 | 101 | l <- split(id, f) 102 | 103 | extract_call <- extract_dim_expr(X, which_dim, 104 | idx_var_sym = quote(l[[i]]), drop = drop) 105 | split_it <- new_split_on_fn(extract_call) 106 | 107 | if(length(l) > 5000) 108 | split_it <- cmpfun(split_it) 109 | 110 | out <- split_it(X, l) 111 | 112 | # names(out) <- names(l) 113 | out 114 | } 115 | 116 | SPLIT_ON_FN_TEMPLATE <- alist(X = , l = , { 117 | out <- vector("list", length(l)) 118 | for (i in seq_along(l)) 119 | out[[i]] <- EXTRACT_EXPR 120 | out 121 | }) 122 | new_split_on_fn <- function(extract_expr) { 123 | SPLIT_ON_FN_TEMPLATE[[c(3, 3, 4, 3)]] <- extract_expr 124 | } 125 | 126 | SPLIT_ON_FN_TEMPLATE <- alist(X = , l = , { 127 | for (i in seq_along(l)) 128 | l[[i]] <- EXTRACT_EXPR 129 | l 130 | }) 131 | 132 | new_split_on_fn <- function(extract_expr) { 133 | SPLIT_ON_FN_TEMPLATE[[c(3, 2, 4, 3)]] <- extract_expr 134 | as.function.default(SPLIT_ON_FN_TEMPLATE, envir = minimal_split_along_fn_env) 135 | } 136 | 137 | 138 | 139 | #' @rdname split-array 140 | #' @export 141 | split_on_rows <- function(X, 142 | f = rownames(X), 143 | drop = FALSE, depth = Inf) 144 | split_on_dim(X, 1L, f = f, drop = drop, depth = depth) 145 | 146 | #' @rdname split-array 147 | #' @export 148 | split_on_cols <- function(X, 149 | f = rownames(X), 150 | drop = FALSE, depth = Inf) 151 | split_on_dim(X, -1L, f = f, drop = drop, depth = depth) 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | minimal_split_along_fn_env <- list2env(list( 160 | `<-` = `<-`, 161 | `{` = `{`, 162 | `[` = `[`, 163 | `[[<-` = `[[<-`, 164 | `[[` = `[[`, 165 | vector = vector, 166 | `for` = `for`, 167 | seq_len = seq_len, 168 | seq_along = seq_along 169 | )) 170 | 171 | 172 | SPLIT_ALONG_FN_TEMPLATE <- alist(X = , { 173 | out <- vector('list', LENGTH_OUT) 174 | for (i in seq_len(LENGTH_OUT)) 175 | out[[i]] <- EXTRACT_CALL 176 | out 177 | }) 178 | 179 | 180 | new_split_along_fn <- function(extract_call, length_out) { 181 | SPLIT_ALONG_FN_TEMPLATE[[c(2L, 3L, 4L, 3L)]] <- extract_call 182 | SPLIT_ALONG_FN_TEMPLATE[[c(2L, 2L, 3L, 3L)]] <- length_out 183 | SPLIT_ALONG_FN_TEMPLATE[[c(2L, 3L, 3L, 2L)]] <- length_out 184 | 185 | as.function.default(SPLIT_ALONG_FN_TEMPLATE, 186 | envir = minimal_split_along_fn_env) 187 | } 188 | 189 | #' @rdname split-array 190 | #' @export 191 | #' @importFrom compiler cmpfun 192 | split_along_dim <- function(X, which_dim, depth = Inf) { 193 | 194 | # don't recurse on data.frame or other overloaded array-type classes 195 | if (is.list(X) && is.null(dim(X)) && depth > 0L) 196 | return(lapply(X, function(x) 197 | split_along_dim(x, which_dim, depth = depth - 1L))) 198 | 199 | which_dim <- standardize_which_dim(which_dim, X, multiple_OK = TRUE) 200 | if(ndim(X) == 1L) 201 | X <- expand_dims(X) 202 | X <- as.array(X) 203 | 204 | # TODO: consider adding support for `drop = TRUE` old behavior 205 | X <- asplit(as.array(X), which_dim) 206 | X 207 | } 208 | 209 | 210 | 211 | #' @rdname split-array 212 | #' @export 213 | split_along_rows <- function(X, depth = Inf) 214 | split_along_dim(X, 1L, depth = depth) 215 | 216 | #' @rdname split-array 217 | #' @export 218 | split_along_cols <- function(X, depth = Inf) 219 | split_along_dim(X, -1L, depth = depth) 220 | 221 | 222 | # TODO: 223 | if(FALSE) { 224 | as_listarray <- function() {} 225 | unlist.listarray <- function() {} 226 | } 227 | 228 | 229 | -------------------------------------------------------------------------------- /R/standardize.R: -------------------------------------------------------------------------------- 1 | 2 | standardize_which_dim <- function(which_dim, X, 3 | n_dim = ndim(X), 4 | names_dimnames_X = names(dimnames(X)), 5 | multiple_OK = FALSE 6 | ) { 7 | # 3 valid inputs 8 | # a) string for a name 9 | # b) negative number for counting backwards 10 | # c) positive integer (canonical) 11 | # outputs: 12 | # case c always 13 | 14 | if (isTRUE(multiple_OK)) 15 | return(vapply(which_dim, function(d) 16 | standardize_which_dim(d, X, n_dim, names_dimnames_X, multiple_OK = FALSE), 17 | 1L)) 18 | 19 | if (is.character(which_dim)) { 20 | stopifnot(is.scalar(which_dim)) 21 | which_dim <- match(which_dim, names_dimnames_X) 22 | if(is.na(which_dim)) 23 | stop("'which_dim %in% names(dimnames(X))' must be TRUE") 24 | 25 | } else if (is.scalar.integerish(which_dim)) { 26 | which_dim <- as.integer(which_dim) 27 | 28 | stopifnot(abs(which_dim) <= n_dim) 29 | 30 | if(which_dim < 0L) 31 | which_dim <- n_dim + which_dim + 1L 32 | 33 | } else 34 | stop("`which_dim` must be a positive or negative integer, or character string") 35 | 36 | which_dim 37 | 38 | } 39 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #' Length of `DIM()` 7 | #' 8 | #' Returns the number of dimensions, or 1 for an atomic vector. 9 | #' @param x a matrix or atomic vector 10 | #' @export 11 | ndim <- function(x) { 12 | if (is.null(dx <- dim(x))) 13 | 1L 14 | else 15 | length(dx) 16 | } 17 | 18 | 19 | 20 | #' Helpers for working with 1-d arrays 21 | #' 22 | #' `DIM()` is to `dim()` as `NROW()` is to `nrow()`. That is, it is identical to 23 | #' `dim()` in most cases except if the input is a bare atomic vector with no 24 | #' `dim` attribute, in which case, the length of the vector is returned instead 25 | #' of `NULL`. 26 | #' 27 | #' @param x an R vector, potentially with a dim attribute 28 | #' @rdname DIM 29 | #' 30 | #' @export 31 | #' @return For `DIM`, the `dim` attribute, or if that's not found, then `length(x)` 32 | #' @examples 33 | #' x <- 1:3 34 | #' dim(x) 35 | #' dim(array(x)) 36 | #' 37 | #' DIM(x) 38 | #' DIM(array(x)) 39 | #' 40 | DIM <- function(x) dim(x) %||% length(x) 41 | 42 | 43 | #' DROP 44 | #' 45 | #' `DROP` first calls `base::drop` and then completely removes the `dim` 46 | #' attribute if the result is a 1-d array 47 | #' 48 | #' 49 | #' @return For `DROP` an array with 2 or more axes, or a vector with no `dim` 50 | #' attributes. 51 | #' @export 52 | #' @rdname DIM 53 | #' 54 | #' @examples 55 | #' x <- array(1:3) 56 | #' str(drop(x)) 57 | #' str(DROP(x)) 58 | DROP <- function(x) { 59 | x <- drop(x) 60 | if(identical(length(DIM(x)), 1L)) 61 | dim(x) <- NULL 62 | x 63 | } 64 | 65 | 66 | `%||%` <- function (x, y) { 67 | if (is.null(x)) 68 | y 69 | else 70 | x 71 | } 72 | 73 | #' @importFrom compiler cmpfun 74 | 75 | parse1 <- function(x) parse(text = x, keep.source = FALSE)[[1]] 76 | 77 | is.negative <- function(x) x < 0 78 | 79 | is.integerish <- function(x, n = NULL, allow_na = FALSE) { 80 | if (!is.null(n) && n != length(x)) 81 | return(FALSE) 82 | if (!allow_na && any(is.na(x) | is.infinite(x))) 83 | return(FALSE) 84 | if (identical(typeof_x <- typeof(x), "integer")) 85 | return(TRUE) 86 | if (identical(typeof_x, "double")) 87 | return(all(x == as.integer(x), na.rm = TRUE)) 88 | FALSE 89 | } 90 | 91 | 92 | is.scalar <- function(x) identical(length(x), 1L) 93 | 94 | is.scalar.integerish <- function(x) 95 | is.scalar(x) && is.integerish(x) 96 | 97 | 98 | `%not_in%` <- function(x, y) match(x, y, nomatch = 0L) == 0L 99 | 100 | check.is.integerish <- function(x, n = NULL) { 101 | nm <- deparse(substitute(x)) 102 | if(!(is.integerish(x, n))) { 103 | msg <- paste(nm, "must be an integer") 104 | if (!is.null(n)) 105 | msg <- paste(msg, "of length", n) 106 | stop(msg, call. = FALSE) 107 | } 108 | } 109 | 110 | dropNULLs <- function(x) x[!vapply(x, is.null, TRUE)] 111 | 112 | 113 | quick_cbind <- function(lst) { 114 | x <- unlist(lst) 115 | dim(x) <- c(length(lst[[1]]), length(lst)) 116 | x 117 | } 118 | 119 | # arr <- function(...) array(seq_len(prod(unlist(c(...)))), unlist(c(...))) 120 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | 2 | eval <- NULL 3 | 4 | .onLoad <- function(libname, pkgname) { 5 | if(requireNamespace("rlang", quietly = TRUE)) 6 | eval <<- rlang::eval_bare 7 | else 8 | eval <<- base::eval 9 | } 10 | 11 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | ```{r setup, include = FALSE} 8 | knitr::opts_chunk$set( 9 | collapse = TRUE, 10 | comment = "#>", 11 | fig.path = "man/figures/README-", 12 | out.width = "100%" 13 | ) 14 | ``` 15 | # listarrays 16 | 17 | [![CRAN status](https://www.r-pkg.org/badges/version/listarrays)](https://cran.r-project.org/package=listarrays) 18 | [![CRAN RStudio mirror downloads](https://cranlogs.r-pkg.org/badges/last-month/listarrays?color=blue)](https://r-pkg.org/pkg/listarrays) 19 | 20 | A toolbox for working with R arrays in a functional programming style. Flexibly 21 | split, bind, reshape, modify, subset, and name arrays. 22 | 23 | The package provides: 24 | 25 | + `split_on_dim()` and `split_along_dim()` which take an array and return a list. 26 | 27 | + `bind_on_dim()` and `bind_as_dim()` take a list and return an array. 28 | 29 | + `modify_along_dim()` takes an array, calls the passed function `.f()` on each 30 | subset of the specified dimension, and returns an array of the same shape. 31 | (think of this as a safer and sometimes faster alternative to `base::apply()` 32 | that is guaranteed to return an array of the same shape as it received) 33 | 34 | + `extract_dim()` a wrapper around `[` that allows you to specify the dimension 35 | being subset as a function argument. For example, `extract_dim(X, 1, idx)` 36 | will extract `idx` on the first dimension, regardless how many dimensions are in 37 | the array `X`. Contrast this with the base alternative `X[idx,,]`, where you 38 | have to match the number of commas `,` to the number of dimensions in `X`. 39 | 40 | + Many of the functions have two variants `*_rows()` and `*_cols()` for the two 41 | most common case of the first and last dimension. For example 42 | `split_on_rows()` which is equivalent to `split_on_dim(X, 1)` and 43 | `split_on_cols()` which is equivalent to `split_on_dim(X, -1)` 44 | 45 | * `set_dim()` and `set_dimnames()`, pipe-friendly and more flexible 46 | versions of `dim<-` and `dimnames<-` 47 | 48 | * `dim2()<-`, `set_dim2()`, `array2()`, which reshape or fills arrays using 49 | row-major (C-style) semantics 50 | 51 | * A handful of lower-level helpers that abstract out patterns commonly 52 | encountered while working with arrays, for example `expand_dims()` 53 | (the inverse of `base::drop()`, or `seq_along_rows()` (a 54 | combination of `seq_along()` and `nrow()`). 55 | 56 | * A set of functions that help encode atomic vectors as `onehot()` binary 57 | matrix's and `decode_onehot()` back into atomic vectors. (for example if 58 | training a neural network with keras) 59 | 60 | * Many of the functions work recursively if provided a list of arrays. 61 | 62 | 63 | ## Installation 64 | 65 | You can install listarrays from CRAN with: 66 | ```{r, eval=FALSE} 67 | install.packages("listarrays") 68 | ``` 69 | 70 | Or install the development version from github with: 71 | ``` {r, eval=FALSE} 72 | devtools::install_github("t-kalinowski/listarrays") 73 | ``` 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # listarrays 5 | 6 | [![CRAN 7 | status](https://www.r-pkg.org/badges/version/listarrays)](https://cran.r-project.org/package=listarrays) 8 | [![CRAN RStudio mirror 9 | downloads](https://cranlogs.r-pkg.org/badges/last-month/listarrays?color=blue)](https://r-pkg.org/pkg/listarrays) 10 | 11 | A toolbox for working with R arrays in a functional programming style. 12 | Flexibly split, bind, reshape, modify, subset, and name arrays. 13 | 14 | The package provides: 15 | 16 | - `split_on_dim()` and `split_along_dim()` which take an array and 17 | return a list. 18 | 19 | - `bind_on_dim()` and `bind_as_dim()` take a list and return an array. 20 | 21 | - `modify_along_dim()` takes an array, calls the passed function 22 | `.f()` on each subset of the specified dimension, and returns an 23 | array of the same shape. (think of this as a safer and sometimes 24 | faster alternative to `base::apply()` that is guaranteed to return 25 | an array of the same shape as it received) 26 | 27 | - `extract_dim()` a wrapper around `[` that allows you to specify the 28 | dimension being subset as a function argument. For example, 29 | `extract_dim(X, 1, idx)` will extract `idx` on the first dimension, 30 | regardless how many dimensions are in the array `X`. Contrast this 31 | with the base alternative `X[idx,,]`, where you have to match the 32 | number of commas `,` to the number of dimensions in `X`. 33 | 34 | - Many of the functions have two variants `*_rows()` and `*_cols()` 35 | for the two most common case of the first and last dimension. For 36 | example `split_on_rows()` which is equivalent to 37 | `split_on_dim(X, 1)` and `split_on_cols()` which is equivalent to 38 | `split_on_dim(X, -1)` 39 | 40 | - `set_dim()` and `set_dimnames()`, pipe-friendly and more flexible 41 | versions of `dim<-` and `dimnames<-` 42 | 43 | - `dim2()<-`, `set_dim2()`, `array2()`, which reshape or fills arrays 44 | using row-major (C-style) semantics 45 | 46 | - A handful of lower-level helpers that abstract out patterns commonly 47 | encountered while working with arrays, for example `expand_dims()` 48 | (the inverse of `base::drop()`, or `seq_along_rows()` (a combination 49 | of `seq_along()` and `nrow()`). 50 | 51 | - A set of functions that help encode atomic vectors as `onehot()` 52 | binary matrix’s and `decode_onehot()` back into atomic vectors. (for 53 | example if training a neural network with keras) 54 | 55 | - Many of the functions work recursively if provided a list of arrays. 56 | 57 | ## Installation 58 | 59 | You can install listarrays from CRAN with: 60 | 61 | ``` r 62 | install.packages("listarrays") 63 | ``` 64 | 65 | Or install the development version from github with: 66 | 67 | ``` r 68 | devtools::install_github("t-kalinowski/listarrays") 69 | ``` 70 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t-kalinowski/listarrays/b38f44fe776a9fd7f48ce096c5f5922550ea84a6/_pkgdown.yml -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/t-kalinowski/listarrays/b38f44fe776a9fd7f48ce096c5f5922550ea84a6/cran-comments.md -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Page not found (404) • listarrays 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |
64 |
65 | 104 | 105 | 106 | 107 |
108 | 109 |
110 |
111 | 114 | 115 | Content not found. Please use links in the navbar. 116 | 117 |
118 | 119 | 124 | 125 |
126 | 127 | 128 | 129 | 139 |
140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /docs/authors.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Authors • listarrays 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |
64 |
65 | 104 | 105 | 106 | 107 |
108 | 109 |
110 |
111 | 114 | 115 |
    116 |
  • 117 |

    Tomasz Kalinowski. Author, maintainer. 118 |

    119 |
  • 120 |
121 | 122 |
123 | 124 |
125 | 126 | 127 | 128 | 138 |
139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /docs/bootstrap-toc.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Table of Contents v0.4.1 (http://afeld.github.io/bootstrap-toc/) 3 | * Copyright 2015 Aidan Feldman 4 | * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */ 5 | 6 | /* modified from https://github.com/twbs/bootstrap/blob/94b4076dd2efba9af71f0b18d4ee4b163aa9e0dd/docs/assets/css/src/docs.css#L548-L601 */ 7 | 8 | /* All levels of nav */ 9 | nav[data-toggle='toc'] .nav > li > a { 10 | display: block; 11 | padding: 4px 20px; 12 | font-size: 13px; 13 | font-weight: 500; 14 | color: #767676; 15 | } 16 | nav[data-toggle='toc'] .nav > li > a:hover, 17 | nav[data-toggle='toc'] .nav > li > a:focus { 18 | padding-left: 19px; 19 | color: #563d7c; 20 | text-decoration: none; 21 | background-color: transparent; 22 | border-left: 1px solid #563d7c; 23 | } 24 | nav[data-toggle='toc'] .nav > .active > a, 25 | nav[data-toggle='toc'] .nav > .active:hover > a, 26 | nav[data-toggle='toc'] .nav > .active:focus > a { 27 | padding-left: 18px; 28 | font-weight: bold; 29 | color: #563d7c; 30 | background-color: transparent; 31 | border-left: 2px solid #563d7c; 32 | } 33 | 34 | /* Nav: second level (shown on .active) */ 35 | nav[data-toggle='toc'] .nav .nav { 36 | display: none; /* Hide by default, but at >768px, show it */ 37 | padding-bottom: 10px; 38 | } 39 | nav[data-toggle='toc'] .nav .nav > li > a { 40 | padding-top: 1px; 41 | padding-bottom: 1px; 42 | padding-left: 30px; 43 | font-size: 12px; 44 | font-weight: normal; 45 | } 46 | nav[data-toggle='toc'] .nav .nav > li > a:hover, 47 | nav[data-toggle='toc'] .nav .nav > li > a:focus { 48 | padding-left: 29px; 49 | } 50 | nav[data-toggle='toc'] .nav .nav > .active > a, 51 | nav[data-toggle='toc'] .nav .nav > .active:hover > a, 52 | nav[data-toggle='toc'] .nav .nav > .active:focus > a { 53 | padding-left: 28px; 54 | font-weight: 500; 55 | } 56 | 57 | /* from https://github.com/twbs/bootstrap/blob/e38f066d8c203c3e032da0ff23cd2d6098ee2dd6/docs/assets/css/src/docs.css#L631-L634 */ 58 | nav[data-toggle='toc'] .nav > .active > ul { 59 | display: block; 60 | } 61 | -------------------------------------------------------------------------------- /docs/bootstrap-toc.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Table of Contents v0.4.1 (http://afeld.github.io/bootstrap-toc/) 3 | * Copyright 2015 Aidan Feldman 4 | * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */ 5 | (function() { 6 | 'use strict'; 7 | 8 | window.Toc = { 9 | helpers: { 10 | // return all matching elements in the set, or their descendants 11 | findOrFilter: function($el, selector) { 12 | // http://danielnouri.org/notes/2011/03/14/a-jquery-find-that-also-finds-the-root-element/ 13 | // http://stackoverflow.com/a/12731439/358804 14 | var $descendants = $el.find(selector); 15 | return $el.filter(selector).add($descendants).filter(':not([data-toc-skip])'); 16 | }, 17 | 18 | generateUniqueIdBase: function(el) { 19 | var text = $(el).text(); 20 | var anchor = text.trim().toLowerCase().replace(/[^A-Za-z0-9]+/g, '-'); 21 | return anchor || el.tagName.toLowerCase(); 22 | }, 23 | 24 | generateUniqueId: function(el) { 25 | var anchorBase = this.generateUniqueIdBase(el); 26 | for (var i = 0; ; i++) { 27 | var anchor = anchorBase; 28 | if (i > 0) { 29 | // add suffix 30 | anchor += '-' + i; 31 | } 32 | // check if ID already exists 33 | if (!document.getElementById(anchor)) { 34 | return anchor; 35 | } 36 | } 37 | }, 38 | 39 | generateAnchor: function(el) { 40 | if (el.id) { 41 | return el.id; 42 | } else { 43 | var anchor = this.generateUniqueId(el); 44 | el.id = anchor; 45 | return anchor; 46 | } 47 | }, 48 | 49 | createNavList: function() { 50 | return $(''); 51 | }, 52 | 53 | createChildNavList: function($parent) { 54 | var $childList = this.createNavList(); 55 | $parent.append($childList); 56 | return $childList; 57 | }, 58 | 59 | generateNavEl: function(anchor, text) { 60 | var $a = $(''); 61 | $a.attr('href', '#' + anchor); 62 | $a.text(text); 63 | var $li = $('
  • '); 64 | $li.append($a); 65 | return $li; 66 | }, 67 | 68 | generateNavItem: function(headingEl) { 69 | var anchor = this.generateAnchor(headingEl); 70 | var $heading = $(headingEl); 71 | var text = $heading.data('toc-text') || $heading.text(); 72 | return this.generateNavEl(anchor, text); 73 | }, 74 | 75 | // Find the first heading level (`

    `, then `

    `, etc.) that has more than one element. Defaults to 1 (for `

    `). 76 | getTopLevel: function($scope) { 77 | for (var i = 1; i <= 6; i++) { 78 | var $headings = this.findOrFilter($scope, 'h' + i); 79 | if ($headings.length > 1) { 80 | return i; 81 | } 82 | } 83 | 84 | return 1; 85 | }, 86 | 87 | // returns the elements for the top level, and the next below it 88 | getHeadings: function($scope, topLevel) { 89 | var topSelector = 'h' + topLevel; 90 | 91 | var secondaryLevel = topLevel + 1; 92 | var secondarySelector = 'h' + secondaryLevel; 93 | 94 | return this.findOrFilter($scope, topSelector + ',' + secondarySelector); 95 | }, 96 | 97 | getNavLevel: function(el) { 98 | return parseInt(el.tagName.charAt(1), 10); 99 | }, 100 | 101 | populateNav: function($topContext, topLevel, $headings) { 102 | var $context = $topContext; 103 | var $prevNav; 104 | 105 | var helpers = this; 106 | $headings.each(function(i, el) { 107 | var $newNav = helpers.generateNavItem(el); 108 | var navLevel = helpers.getNavLevel(el); 109 | 110 | // determine the proper $context 111 | if (navLevel === topLevel) { 112 | // use top level 113 | $context = $topContext; 114 | } else if ($prevNav && $context === $topContext) { 115 | // create a new level of the tree and switch to it 116 | $context = helpers.createChildNavList($prevNav); 117 | } // else use the current $context 118 | 119 | $context.append($newNav); 120 | 121 | $prevNav = $newNav; 122 | }); 123 | }, 124 | 125 | parseOps: function(arg) { 126 | var opts; 127 | if (arg.jquery) { 128 | opts = { 129 | $nav: arg 130 | }; 131 | } else { 132 | opts = arg; 133 | } 134 | opts.$scope = opts.$scope || $(document.body); 135 | return opts; 136 | } 137 | }, 138 | 139 | // accepts a jQuery object, or an options object 140 | init: function(opts) { 141 | opts = this.helpers.parseOps(opts); 142 | 143 | // ensure that the data attribute is in place for styling 144 | opts.$nav.attr('data-toggle', 'toc'); 145 | 146 | var $topContext = this.helpers.createChildNavList(opts.$nav); 147 | var topLevel = this.helpers.getTopLevel(opts.$scope); 148 | var $headings = this.helpers.getHeadings(opts.$scope, topLevel); 149 | this.helpers.populateNav($topContext, topLevel, $headings); 150 | } 151 | }; 152 | 153 | $(function() { 154 | $('nav[data-toggle="toc"]').each(function(i, el) { 155 | var $nav = $(el); 156 | Toc.init($nav); 157 | }); 158 | }); 159 | })(); 160 | -------------------------------------------------------------------------------- /docs/docsearch.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | // register a handler to move the focus to the search bar 4 | // upon pressing shift + "/" (i.e. "?") 5 | $(document).on('keydown', function(e) { 6 | if (e.shiftKey && e.keyCode == 191) { 7 | e.preventDefault(); 8 | $("#search-input").focus(); 9 | } 10 | }); 11 | 12 | $(document).ready(function() { 13 | // do keyword highlighting 14 | /* modified from https://jsfiddle.net/julmot/bL6bb5oo/ */ 15 | var mark = function() { 16 | 17 | var referrer = document.URL ; 18 | var paramKey = "q" ; 19 | 20 | if (referrer.indexOf("?") !== -1) { 21 | var qs = referrer.substr(referrer.indexOf('?') + 1); 22 | var qs_noanchor = qs.split('#')[0]; 23 | var qsa = qs_noanchor.split('&'); 24 | var keyword = ""; 25 | 26 | for (var i = 0; i < qsa.length; i++) { 27 | var currentParam = qsa[i].split('='); 28 | 29 | if (currentParam.length !== 2) { 30 | continue; 31 | } 32 | 33 | if (currentParam[0] == paramKey) { 34 | keyword = decodeURIComponent(currentParam[1].replace(/\+/g, "%20")); 35 | } 36 | } 37 | 38 | if (keyword !== "") { 39 | $(".contents").unmark({ 40 | done: function() { 41 | $(".contents").mark(keyword); 42 | } 43 | }); 44 | } 45 | } 46 | }; 47 | 48 | mark(); 49 | }); 50 | }); 51 | 52 | /* Search term highlighting ------------------------------*/ 53 | 54 | function matchedWords(hit) { 55 | var words = []; 56 | 57 | var hierarchy = hit._highlightResult.hierarchy; 58 | // loop to fetch from lvl0, lvl1, etc. 59 | for (var idx in hierarchy) { 60 | words = words.concat(hierarchy[idx].matchedWords); 61 | } 62 | 63 | var content = hit._highlightResult.content; 64 | if (content) { 65 | words = words.concat(content.matchedWords); 66 | } 67 | 68 | // return unique words 69 | var words_uniq = [...new Set(words)]; 70 | return words_uniq; 71 | } 72 | 73 | function updateHitURL(hit) { 74 | 75 | var words = matchedWords(hit); 76 | var url = ""; 77 | 78 | if (hit.anchor) { 79 | url = hit.url_without_anchor + '?q=' + escape(words.join(" ")) + '#' + hit.anchor; 80 | } else { 81 | url = hit.url + '?q=' + escape(words.join(" ")); 82 | } 83 | 84 | return url; 85 | } 86 | -------------------------------------------------------------------------------- /docs/extra.css: -------------------------------------------------------------------------------- 1 | 2 | .dl-horizontal dt { 3 | white-space: normal; 4 | } -------------------------------------------------------------------------------- /docs/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /docs/pkgdown.css: -------------------------------------------------------------------------------- 1 | /* Sticky footer */ 2 | 3 | /** 4 | * Basic idea: https://philipwalton.github.io/solved-by-flexbox/demos/sticky-footer/ 5 | * Details: https://github.com/philipwalton/solved-by-flexbox/blob/master/assets/css/components/site.css 6 | * 7 | * .Site -> body > .container 8 | * .Site-content -> body > .container .row 9 | * .footer -> footer 10 | * 11 | * Key idea seems to be to ensure that .container and __all its parents__ 12 | * have height set to 100% 13 | * 14 | */ 15 | 16 | html, body { 17 | height: 100%; 18 | } 19 | 20 | body { 21 | position: relative; 22 | } 23 | 24 | body > .container { 25 | display: flex; 26 | height: 100%; 27 | flex-direction: column; 28 | } 29 | 30 | body > .container .row { 31 | flex: 1 0 auto; 32 | } 33 | 34 | footer { 35 | margin-top: 45px; 36 | padding: 35px 0 36px; 37 | border-top: 1px solid #e5e5e5; 38 | color: #666; 39 | display: flex; 40 | flex-shrink: 0; 41 | } 42 | footer p { 43 | margin-bottom: 0; 44 | } 45 | footer div { 46 | flex: 1; 47 | } 48 | footer .pkgdown { 49 | text-align: right; 50 | } 51 | footer p { 52 | margin-bottom: 0; 53 | } 54 | 55 | img.icon { 56 | float: right; 57 | } 58 | 59 | img { 60 | max-width: 100%; 61 | } 62 | 63 | /* Fix bug in bootstrap (only seen in firefox) */ 64 | summary { 65 | display: list-item; 66 | } 67 | 68 | /* Typographic tweaking ---------------------------------*/ 69 | 70 | .contents .page-header { 71 | margin-top: calc(-60px + 1em); 72 | } 73 | 74 | dd { 75 | margin-left: 3em; 76 | } 77 | 78 | /* Section anchors ---------------------------------*/ 79 | 80 | a.anchor { 81 | margin-left: -30px; 82 | display:inline-block; 83 | width: 30px; 84 | height: 30px; 85 | visibility: hidden; 86 | 87 | background-image: url(./link.svg); 88 | background-repeat: no-repeat; 89 | background-size: 20px 20px; 90 | background-position: center center; 91 | } 92 | 93 | .hasAnchor:hover a.anchor { 94 | visibility: visible; 95 | } 96 | 97 | @media (max-width: 767px) { 98 | .hasAnchor:hover a.anchor { 99 | visibility: hidden; 100 | } 101 | } 102 | 103 | 104 | /* Fixes for fixed navbar --------------------------*/ 105 | 106 | .contents h1, .contents h2, .contents h3, .contents h4 { 107 | padding-top: 60px; 108 | margin-top: -40px; 109 | } 110 | 111 | /* Navbar submenu --------------------------*/ 112 | 113 | .dropdown-submenu { 114 | position: relative; 115 | } 116 | 117 | .dropdown-submenu>.dropdown-menu { 118 | top: 0; 119 | left: 100%; 120 | margin-top: -6px; 121 | margin-left: -1px; 122 | border-radius: 0 6px 6px 6px; 123 | } 124 | 125 | .dropdown-submenu:hover>.dropdown-menu { 126 | display: block; 127 | } 128 | 129 | .dropdown-submenu>a:after { 130 | display: block; 131 | content: " "; 132 | float: right; 133 | width: 0; 134 | height: 0; 135 | border-color: transparent; 136 | border-style: solid; 137 | border-width: 5px 0 5px 5px; 138 | border-left-color: #cccccc; 139 | margin-top: 5px; 140 | margin-right: -10px; 141 | } 142 | 143 | .dropdown-submenu:hover>a:after { 144 | border-left-color: #ffffff; 145 | } 146 | 147 | .dropdown-submenu.pull-left { 148 | float: none; 149 | } 150 | 151 | .dropdown-submenu.pull-left>.dropdown-menu { 152 | left: -100%; 153 | margin-left: 10px; 154 | border-radius: 6px 0 6px 6px; 155 | } 156 | 157 | /* Sidebar --------------------------*/ 158 | 159 | #pkgdown-sidebar { 160 | margin-top: 30px; 161 | position: -webkit-sticky; 162 | position: sticky; 163 | top: 70px; 164 | } 165 | 166 | #pkgdown-sidebar h2 { 167 | font-size: 1.5em; 168 | margin-top: 1em; 169 | } 170 | 171 | #pkgdown-sidebar h2:first-child { 172 | margin-top: 0; 173 | } 174 | 175 | #pkgdown-sidebar .list-unstyled li { 176 | margin-bottom: 0.5em; 177 | } 178 | 179 | /* bootstrap-toc tweaks ------------------------------------------------------*/ 180 | 181 | /* All levels of nav */ 182 | 183 | nav[data-toggle='toc'] .nav > li > a { 184 | padding: 4px 20px 4px 6px; 185 | font-size: 1.5rem; 186 | font-weight: 400; 187 | color: inherit; 188 | } 189 | 190 | nav[data-toggle='toc'] .nav > li > a:hover, 191 | nav[data-toggle='toc'] .nav > li > a:focus { 192 | padding-left: 5px; 193 | color: inherit; 194 | border-left: 1px solid #878787; 195 | } 196 | 197 | nav[data-toggle='toc'] .nav > .active > a, 198 | nav[data-toggle='toc'] .nav > .active:hover > a, 199 | nav[data-toggle='toc'] .nav > .active:focus > a { 200 | padding-left: 5px; 201 | font-size: 1.5rem; 202 | font-weight: 400; 203 | color: inherit; 204 | border-left: 2px solid #878787; 205 | } 206 | 207 | /* Nav: second level (shown on .active) */ 208 | 209 | nav[data-toggle='toc'] .nav .nav { 210 | display: none; /* Hide by default, but at >768px, show it */ 211 | padding-bottom: 10px; 212 | } 213 | 214 | nav[data-toggle='toc'] .nav .nav > li > a { 215 | padding-left: 16px; 216 | font-size: 1.35rem; 217 | } 218 | 219 | nav[data-toggle='toc'] .nav .nav > li > a:hover, 220 | nav[data-toggle='toc'] .nav .nav > li > a:focus { 221 | padding-left: 15px; 222 | } 223 | 224 | nav[data-toggle='toc'] .nav .nav > .active > a, 225 | nav[data-toggle='toc'] .nav .nav > .active:hover > a, 226 | nav[data-toggle='toc'] .nav .nav > .active:focus > a { 227 | padding-left: 15px; 228 | font-weight: 500; 229 | font-size: 1.35rem; 230 | } 231 | 232 | /* orcid ------------------------------------------------------------------- */ 233 | 234 | .orcid { 235 | font-size: 16px; 236 | color: #A6CE39; 237 | /* margins are required by official ORCID trademark and display guidelines */ 238 | margin-left:4px; 239 | margin-right:4px; 240 | vertical-align: middle; 241 | } 242 | 243 | /* Reference index & topics ----------------------------------------------- */ 244 | 245 | .ref-index th {font-weight: normal;} 246 | 247 | .ref-index td {vertical-align: top;} 248 | .ref-index .icon {width: 40px;} 249 | .ref-index .alias {width: 40%;} 250 | .ref-index-icons .alias {width: calc(40% - 40px);} 251 | .ref-index .title {width: 60%;} 252 | 253 | .ref-arguments th {text-align: right; padding-right: 10px;} 254 | .ref-arguments th, .ref-arguments td {vertical-align: top;} 255 | .ref-arguments .name {width: 20%;} 256 | .ref-arguments .desc {width: 80%;} 257 | 258 | /* Nice scrolling for wide elements --------------------------------------- */ 259 | 260 | table { 261 | display: block; 262 | overflow: auto; 263 | } 264 | 265 | /* Syntax highlighting ---------------------------------------------------- */ 266 | 267 | pre { 268 | word-wrap: normal; 269 | word-break: normal; 270 | border: 1px solid #eee; 271 | } 272 | 273 | pre, code { 274 | background-color: #f8f8f8; 275 | color: #333; 276 | } 277 | 278 | pre code { 279 | overflow: auto; 280 | word-wrap: normal; 281 | white-space: pre; 282 | } 283 | 284 | pre .img { 285 | margin: 5px 0; 286 | } 287 | 288 | pre .img img { 289 | background-color: #fff; 290 | display: block; 291 | height: auto; 292 | } 293 | 294 | code a, pre a { 295 | color: #375f84; 296 | } 297 | 298 | a.sourceLine:hover { 299 | text-decoration: none; 300 | } 301 | 302 | .fl {color: #1514b5;} 303 | .fu {color: #000000;} /* function */ 304 | .ch,.st {color: #036a07;} /* string */ 305 | .kw {color: #264D66;} /* keyword */ 306 | .co {color: #888888;} /* comment */ 307 | 308 | .message { color: black; font-weight: bolder;} 309 | .error { color: orange; font-weight: bolder;} 310 | .warning { color: #6A0366; font-weight: bolder;} 311 | 312 | /* Clipboard --------------------------*/ 313 | 314 | .hasCopyButton { 315 | position: relative; 316 | } 317 | 318 | .btn-copy-ex { 319 | position: absolute; 320 | right: 0; 321 | top: 0; 322 | visibility: hidden; 323 | } 324 | 325 | .hasCopyButton:hover button.btn-copy-ex { 326 | visibility: visible; 327 | } 328 | 329 | /* headroom.js ------------------------ */ 330 | 331 | .headroom { 332 | will-change: transform; 333 | transition: transform 200ms linear; 334 | } 335 | .headroom--pinned { 336 | transform: translateY(0%); 337 | } 338 | .headroom--unpinned { 339 | transform: translateY(-100%); 340 | } 341 | 342 | /* mark.js ----------------------------*/ 343 | 344 | mark { 345 | background-color: rgba(255, 255, 51, 0.5); 346 | border-bottom: 2px solid rgba(255, 153, 51, 0.3); 347 | padding: 1px; 348 | } 349 | 350 | /* vertical spacing after htmlwidgets */ 351 | .html-widget { 352 | margin-bottom: 10px; 353 | } 354 | 355 | /* fontawesome ------------------------ */ 356 | 357 | .fab { 358 | font-family: "Font Awesome 5 Brands" !important; 359 | } 360 | 361 | /* don't display links in code chunks when printing */ 362 | /* source: https://stackoverflow.com/a/10781533 */ 363 | @media print { 364 | code a:link:after, code a:visited:after { 365 | content: ""; 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /docs/pkgdown.js: -------------------------------------------------------------------------------- 1 | /* http://gregfranko.com/blog/jquery-best-practices/ */ 2 | (function($) { 3 | $(function() { 4 | 5 | $('.navbar-fixed-top').headroom(); 6 | 7 | $('body').css('padding-top', $('.navbar').height() + 10); 8 | $(window).resize(function(){ 9 | $('body').css('padding-top', $('.navbar').height() + 10); 10 | }); 11 | 12 | $('[data-toggle="tooltip"]').tooltip(); 13 | 14 | var cur_path = paths(location.pathname); 15 | var links = $("#navbar ul li a"); 16 | var max_length = -1; 17 | var pos = -1; 18 | for (var i = 0; i < links.length; i++) { 19 | if (links[i].getAttribute("href") === "#") 20 | continue; 21 | // Ignore external links 22 | if (links[i].host !== location.host) 23 | continue; 24 | 25 | var nav_path = paths(links[i].pathname); 26 | 27 | var length = prefix_length(nav_path, cur_path); 28 | if (length > max_length) { 29 | max_length = length; 30 | pos = i; 31 | } 32 | } 33 | 34 | // Add class to parent
  • , and enclosing
  • if in dropdown 35 | if (pos >= 0) { 36 | var menu_anchor = $(links[pos]); 37 | menu_anchor.parent().addClass("active"); 38 | menu_anchor.closest("li.dropdown").addClass("active"); 39 | } 40 | }); 41 | 42 | function paths(pathname) { 43 | var pieces = pathname.split("/"); 44 | pieces.shift(); // always starts with / 45 | 46 | var end = pieces[pieces.length - 1]; 47 | if (end === "index.html" || end === "") 48 | pieces.pop(); 49 | return(pieces); 50 | } 51 | 52 | // Returns -1 if not found 53 | function prefix_length(needle, haystack) { 54 | if (needle.length > haystack.length) 55 | return(-1); 56 | 57 | // Special case for length-0 haystack, since for loop won't run 58 | if (haystack.length === 0) { 59 | return(needle.length === 0 ? 0 : -1); 60 | } 61 | 62 | for (var i = 0; i < haystack.length; i++) { 63 | if (needle[i] != haystack[i]) 64 | return(i); 65 | } 66 | 67 | return(haystack.length); 68 | } 69 | 70 | /* Clipboard --------------------------*/ 71 | 72 | function changeTooltipMessage(element, msg) { 73 | var tooltipOriginalTitle=element.getAttribute('data-original-title'); 74 | element.setAttribute('data-original-title', msg); 75 | $(element).tooltip('show'); 76 | element.setAttribute('data-original-title', tooltipOriginalTitle); 77 | } 78 | 79 | if(ClipboardJS.isSupported()) { 80 | $(document).ready(function() { 81 | var copyButton = ""; 82 | 83 | $(".examples, div.sourceCode").addClass("hasCopyButton"); 84 | 85 | // Insert copy buttons: 86 | $(copyButton).prependTo(".hasCopyButton"); 87 | 88 | // Initialize tooltips: 89 | $('.btn-copy-ex').tooltip({container: 'body'}); 90 | 91 | // Initialize clipboard: 92 | var clipboardBtnCopies = new ClipboardJS('[data-clipboard-copy]', { 93 | text: function(trigger) { 94 | return trigger.parentNode.textContent; 95 | } 96 | }); 97 | 98 | clipboardBtnCopies.on('success', function(e) { 99 | changeTooltipMessage(e.trigger, 'Copied!'); 100 | e.clearSelection(); 101 | }); 102 | 103 | clipboardBtnCopies.on('error', function() { 104 | changeTooltipMessage(e.trigger,'Press Ctrl+C or Command+C to copy'); 105 | }); 106 | }); 107 | } 108 | })(window.jQuery || window.$) 109 | -------------------------------------------------------------------------------- /docs/pkgdown.yml: -------------------------------------------------------------------------------- 1 | pandoc: '2.5' 2 | pkgdown: 1.5.1 3 | pkgdown_sha: ~ 4 | articles: [] 5 | last_built: 2020-06-02T03:22Z 6 | 7 | -------------------------------------------------------------------------------- /docs/reference/DIM.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Helpers for working with 1-d arrays — DIM • listarrays 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 63 | 64 | 65 | 66 | 67 | 68 | 69 |
    70 |
    71 | 110 | 111 | 112 | 113 |
    114 | 115 |
    116 |
    117 | 122 | 123 |
    124 |

    DIM() is to dim() as NROW() is to nrow(). That is, it is identical to 125 | dim() in most cases except if the input is a bare atomic vector with no 126 | dim attribute, in which case, the length of the vector is returned instead 127 | of NULL.

    128 |

    DROP first calls base::drop and then completely removes the dim 129 | attribute if the result is a 1-d array

    130 |
    131 | 132 |
    DIM(x)
    133 | 
    134 | DROP(x)
    135 | 136 |

    Arguments

    137 | 138 | 139 | 140 | 141 | 142 | 143 |
    x

    an R vector, potentially with a dim attribute

    144 | 145 |

    Value

    146 | 147 |

    For DIM, the dim attribute, or if that's not found, then length(x)

    148 |

    For DROP an array with 2 or more axes, or a vector with no dim 149 | attributes.

    150 | 151 |

    Examples

    152 |
    x <- 1:3 153 | dim(x)
    #> NULL
    #> [1] 3
    154 | DIM(x)
    #> [1] 3
    DIM(array(x))
    #> [1] 3
    155 | x <- array(1:3) 156 | str(drop(x))
    #> int [1:3(1d)] 1 2 3
    str(DROP(x))
    #> int [1:3] 1 2 3
    157 |
    158 | 163 |
    164 | 165 | 166 |
    167 | 170 | 171 |
    172 |

    Site built with pkgdown 1.5.1.

    173 |
    174 | 175 |
    176 |
    177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /docs/reference/array2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Make or reshape an array with C-style (row-major) semantics — array2 • listarrays 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
    67 |
    68 | 107 | 108 | 109 | 110 |
    111 | 112 |
    113 |
    114 | 119 | 120 |
    121 |

    These functions reshape or make an array using C-style, row-major semantics. 122 | The returned array is still R's native F-style, (meaning, the underlying 123 | vector has been reordered).

    124 |
    125 | 126 |
    array2(data, dim = length(data), dimnames = NULL)
    127 | 
    128 | matrix2(...)
    129 | 
    130 | dim2(x) <- value
    131 | 
    132 | set_dim2(...)
    133 | 134 |

    Arguments

    135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 |
    data

    what to fill the array with

    dim

    numeric vector of dimensions

    dimnames

    a list of dimnames, must be the same length as dims

    ...

    passed on to set_dim()

    x

    object to set dimensions on (array or atomic vector)

    value

    a numeric (integerish) vector of new dimensions

    162 | 163 |

    Details

    164 | 165 |

    Other than the C-style semantics, these functions behave identically to their 166 | counterparts (array2() behaves identically to array(), `dim2<-`() 167 | to `dim<-`()). set_dim2() is just a wrapper around set_dim(..., order = "C").

    168 |

    See examples for a drop-in pure R replacement to reticulate::array_reshape()

    169 | 170 |

    Examples

    171 |
    array(1:4, c(2,2))
    #> [,1] [,2] 172 | #> [1,] 1 3 173 | #> [2,] 2 4
    array2(1:4, c(2,2))
    #> [,1] [,2] 174 | #> [1,] 1 2 175 | #> [2,] 3 4
    176 | # for a drop-in replacement to reticulate::array_reshape 177 | array_reshape <- listarrays:::array_reshape 178 | array_reshape(1:4, c(2,2))
    #> [,1] [,2] 179 | #> [1,] 1 2 180 | #> [2,] 3 4
    181 |
    182 | 187 |
    188 | 189 | 190 |
    191 | 194 | 195 |
    196 |

    Site built with pkgdown 1.5.1.

    197 |
    198 | 199 |
    200 |
    201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | -------------------------------------------------------------------------------- /docs/reference/drop_dimnames.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Drop dimnames — drop_dimnames • listarrays 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |
    66 |
    67 | 106 | 107 | 108 | 109 |
    110 | 111 |
    112 |
    113 | 118 | 119 |
    120 |

    A pipe-friendly wrapper for dim(x) <- NULL and dimnames(x) <- NULL or, if 121 | which_dim is not NULL, dimnames(x)[which_dim] <- list(NULL)

    122 |
    123 | 124 |
    drop_dimnames(x, which_dim = NULL, keep_axis_names = FALSE)
    125 | 
    126 | drop_dim(x)
    127 | 
    128 | drop_dim2(x)
    129 | 130 |

    Arguments

    131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 141 | 142 | 143 | 144 | 146 | 147 |
    x

    an object, potentially with dimnames

    which_dim

    If NULL (the default) then all dimnames are dropped. If 140 | integer vector, then dimnames only at the specified dimensions are dropped.

    keep_axis_names

    TRUE or FALSE, whether to preserve the axis names when 145 | dropping the dimnames

    148 | 149 | 150 |
    151 | 156 |
    157 | 158 | 159 |
    160 | 163 | 164 |
    165 |

    Site built with pkgdown 1.5.1.

    166 |
    167 | 168 |
    169 |
    170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /docs/reference/expand_dims.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Expand the shape of an array — expand_dims • listarrays 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
    67 |
    68 | 107 | 108 | 109 | 110 |
    111 | 112 |
    113 |
    114 | 119 | 120 |
    121 |

    This is the inverse operation of base::drop(). 122 | It is analogous to python's numpy.expand_dims(), but vectorized on 123 | which_dim.

    124 |
    125 | 126 |
    expand_dims(x, which_dim = -1L)
    127 | 128 |

    Arguments

    129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 140 | 141 |
    x

    an array. Bare vectors are treated as 1-d arrays.

    which_dim

    numeric. Desired index position of the new axis or axes in 138 | the returned array. Negative numbers count from the back. Can be any 139 | length.Throws a warning if any duplicates are provided.

    142 | 143 |

    Value

    144 | 145 |

    the array x with new dim

    146 | 147 |

    Examples

    148 |
    x <- array(1:24, 2:4) 149 | dim(x)
    #> [1] 2 3 4
    dim(expand_dims(x))
    #> [1] 2 3 4 1
    dim(expand_dims(x, 2))
    #> [1] 2 1 3 4
    dim(expand_dims(x, c(1,2)))
    #> [1] 1 1 2 3 4
    dim(expand_dims(x, c(1,-1)))
    #> [1] 1 2 3 4 1
    dim(expand_dims(x, 6)) # implicitly also expands dims 4,5
    #> [1] 2 3 4 1 1 1
    dim(expand_dims(x, 4:6))
    #> [1] 2 3 4 1 1 1
    150 | # error, implicit expansion with negative indexes not supported 151 | try(expand_dims(x, -6))
    #> Error in expand_dims(x, -6) : 152 | #> Implicit additional dims for expansion with negative indexes not supported
    153 | # supply them explicitly instead 154 | dim(expand_dims(x, -(4:6)))
    #> [1] 1 1 1 2 3 4
    155 |
    156 | 161 |
    162 | 163 | 164 |
    165 | 168 | 169 |
    170 |

    Site built with pkgdown 1.5.1.

    171 |
    172 | 173 |
    174 |
    175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | -------------------------------------------------------------------------------- /docs/reference/modify_along_dim.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Modify an array by mapping over 1 or more dimensions — modify_along_dim • listarrays 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
    67 |
    68 | 107 | 108 | 109 | 110 |
    111 | 112 |
    113 |
    114 | 119 | 120 |
    121 |

    This function can be thought of as a version of base::apply() that is 122 | guaranteed to return a object of the same dimensions as it was input. It also 123 | generally preserves attributes, as it's built on top of [<-.

    124 |
    125 | 126 |
    modify_along_dim(X, which_dim, .f, ...)
    127 | 
    128 | modify_along_rows(X, .f, ...)
    129 | 
    130 | modify_along_cols(X, .f, ...)
    131 | 132 |

    Arguments

    133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 149 | 150 | 151 | 152 | 153 | 154 |
    X

    An array, or a list of arrays

    which_dim

    integer vector of dimensions to modify at

    .f

    a function or formula defining a function(same semantics as 146 | purrr::map()). The function must return either an array the same shape 147 | as it was passed, a vector of the same length, or a scalar, although the 148 | type of the returned object does not need to be the same as was passed in.

    ...

    passed on to .f()

    155 | 156 |

    Value

    157 | 158 |

    An array, or if X was a list, a list of arrays of the same shape as 159 | was passed in.

    160 | 161 |

    Examples

    162 |
    x <- array(1:6, 1:3) 163 | modify_along_dim(x, 3, ~mean(.x))
    #> , , 1 164 | #> 165 | #> [,1] [,2] 166 | #> [1,] 1.5 1.5 167 | #> 168 | #> , , 2 169 | #> 170 | #> [,1] [,2] 171 | #> [1,] 3.5 3.5 172 | #> 173 | #> , , 3 174 | #> 175 | #> [,1] [,2] 176 | #> [1,] 5.5 5.5 177 | #>
    modify_along_dim(x, 3, ~.x/mean(.x))
    #> , , 1 178 | #> 179 | #> [,1] [,2] 180 | #> [1,] 0.6666667 1.333333 181 | #> 182 | #> , , 2 183 | #> 184 | #> [,1] [,2] 185 | #> [1,] 0.8571429 1.142857 186 | #> 187 | #> , , 3 188 | #> 189 | #> [,1] [,2] 190 | #> [1,] 0.9090909 1.090909 191 | #>
    192 |
    193 | 198 |
    199 | 200 | 201 |
    202 | 205 | 206 |
    207 |

    Site built with pkgdown 1.5.1.

    208 |
    209 | 210 |
    211 |
    212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | -------------------------------------------------------------------------------- /docs/reference/ndim.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Length of <code>DIM()</code> — ndim • listarrays 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
    65 |
    66 | 105 | 106 | 107 | 108 |
    109 | 110 |
    111 |
    112 | 117 | 118 |
    119 |

    Returns the number of dimensions, or 1 for an atomic vector.

    120 |
    121 | 122 |
    ndim(x)
    123 | 124 |

    Arguments

    125 | 126 | 127 | 128 | 129 | 130 | 131 |
    x

    a matrix or atomic vector

    132 | 133 | 134 |
    135 | 140 |
    141 | 142 | 143 |
    144 | 147 | 148 |
    149 |

    Site built with pkgdown 1.5.1.

    150 |
    151 | 152 |
    153 |
    154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /docs/reference/seq_along_dim.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Sequence along a dimension — seq_along_dim • listarrays 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
    65 |
    66 | 105 | 106 | 107 | 108 |
    109 | 110 |
    111 |
    112 | 117 | 118 |
    119 |

    Sequence along a dimension

    120 |
    121 | 122 |
    seq_along_dim(x, which_dim)
    123 | 
    124 | seq_along_rows(x)
    125 | 
    126 | seq_along_cols(x)
    127 | 128 |

    Arguments

    129 | 130 | 131 | 132 | 133 | 138 | 139 | 140 | 141 | 143 | 144 |
    x

    a dataframe, array or vector. For seq_along_rows, and 134 | seq_along_cols sequence along the first and last dimensions, 135 | respectively. Atomic vectors are treated as 1 dimensional 136 | arrays (i.e., seq_along_rows is equivalent to seq_along when x is an 137 | atomic vector or list).

    which_dim

    a scalar integer or character string, specifying which 142 | dimension to generate a sequence for. Negative numbers count from the back.

    145 | 146 |

    Value

    147 | 148 |

    a vector of integers 1:nrow(x), safe for use in for loops and 149 | vectorized equivalents.

    150 | 151 |

    Examples

    152 |
    for (r in seq_along_rows(mtcars[1:4,])) 153 | print(mtcars[r,])
    #> mpg cyl disp hp drat wt qsec vs am gear carb 154 | #> Mazda RX4 21 6 160 110 3.9 2.62 16.46 0 1 4 4 155 | #> mpg cyl disp hp drat wt qsec vs am gear carb 156 | #> Mazda RX4 Wag 21 6 160 110 3.9 2.875 17.02 0 1 4 4 157 | #> mpg cyl disp hp drat wt qsec vs am gear carb 158 | #> Datsun 710 22.8 4 108 93 3.85 2.32 18.61 1 1 4 1 159 | #> mpg cyl disp hp drat wt qsec vs am gear carb 160 | #> Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
    161 | x <- 1:3 162 | identical(seq_along_rows(x), seq_along(x))
    #> [1] TRUE
    163 |
    164 |
    165 | 170 |
    171 | 172 | 173 |
    174 | 177 | 178 |
    179 |

    Site built with pkgdown 1.5.1.

    180 |
    181 | 182 |
    183 |
    184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /docs/reference/set_as_rows.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Reshape an array to send a dimension forward or back — set_as_rows • listarrays 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
    65 |
    66 | 105 | 106 | 107 | 108 |
    109 | 110 |
    111 |
    112 | 117 | 118 |
    119 |

    Reshape an array to send a dimension forward or back

    120 |
    121 | 122 |
    set_as_rows(X, which_dim)
    123 | 
    124 | set_as_cols(X, which_dim)
    125 | 126 |

    Arguments

    127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 138 | 139 |
    X

    an array

    which_dim

    scalar integer or string, which dim to bring forward. 136 | Negative numbers count from the back

    137 |

    This is a powered by base::aperm().

    140 | 141 |

    Value

    142 | 143 |

    a reshaped array

    144 |

    See also

    145 | 146 | 147 | 148 |

    Examples

    149 |
    x <- array(1:24, 2:4) 150 | y <- set_as_rows(x, 3) 151 | 152 | for (i in seq_along_dim(x, 3)) 153 | stopifnot( identical(x[,,i], y[i,,]) )
    154 |
    155 | 160 |
    161 | 162 | 163 |
    164 | 167 | 168 |
    169 |

    Site built with pkgdown 1.5.1.

    170 |
    171 | 172 |
    173 |
    174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /docs/reference/shuffle_rows.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Shuffle along the first dimension multiple arrays in sync — shuffle_rows • listarrays 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
    65 |
    66 | 105 | 106 | 107 | 108 |
    109 | 110 |
    111 |
    112 | 117 | 118 |
    119 |

    Shuffle along the first dimension multiple arrays in sync

    120 |
    121 | 122 |
    shuffle_rows(...)
    123 | 124 |

    Arguments

    125 | 126 | 127 | 128 | 129 | 130 | 131 |
    ...

    arrays of various dimensions (vectors and data.frames OK too)

    132 | 133 |

    Value

    134 | 135 |

    A list of objects passed on to ..., or if a single object was 136 | supplied, then the single object shuffled

    137 | 138 |

    Examples

    139 |
    x <- 1:3 140 | y <- matrix(1:9, ncol = 3) 141 | z <- array(1:27, c(3,3,3)) 142 | 143 | if(require(zeallot)) { 144 | c(xs, ys, zs) %<-% shuffle_rows(x, y, z) 145 | 146 | l <- lapply(seq_along_rows(y), function(r) { 147 | list(x = x[r], y = y[r,], z = z[r,,]) 148 | }) 149 | 150 | ls <- lapply(seq_along_rows(y), function(r) { 151 | list(x = xs[r], y = ys[r,], z = zs[r,,]) 152 | }) 153 | 154 | stopifnot( 155 | length(unique(c(l, ls))) == length(l)) 156 | }
    157 |
    158 | 163 |
    164 | 165 | 166 |
    167 | 170 | 171 |
    172 |

    Site built with pkgdown 1.5.1.

    173 |
    174 | 175 |
    176 |
    177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /docs/reference/t.array.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | transpose an array — t.array • listarrays 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
    65 |
    66 | 105 | 106 | 107 | 108 |
    109 | 110 |
    111 |
    112 | 117 | 118 |
    119 |

    transpose an array

    120 |
    121 | 122 |
    # S3 method for array
    123 | t(x)
    124 | 125 |

    Arguments

    126 | 127 | 128 | 129 | 130 | 132 | 133 |
    x

    an array

    131 |

    This reverses the dimensions of an array

    134 | 135 | 136 |

    Examples

    137 |
    x <- array(1:27, c(3,3,3)) 138 | tx <- t(x) 139 | for (i in 1:3) 140 | for(j in 1:3) 141 | stopifnot(x[,j,i] == tx[i,j,])
    142 |
    143 | 148 |
    149 | 150 | 151 | 161 |
    162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /listarrays.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageRoxygenize: rd,collate,namespace 22 | 23 | QuitChildProcessesOnExit: Yes 24 | -------------------------------------------------------------------------------- /man/DIM.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{DIM} 4 | \alias{DIM} 5 | \alias{DROP} 6 | \title{Helpers for working with 1-d arrays} 7 | \usage{ 8 | DIM(x) 9 | 10 | DROP(x) 11 | } 12 | \arguments{ 13 | \item{x}{an R vector, potentially with a dim attribute} 14 | } 15 | \value{ 16 | For \code{DIM}, the \code{dim} attribute, or if that's not found, then \code{length(x)} 17 | 18 | For \code{DROP} an array with 2 or more axes, or a vector with no \code{dim} 19 | attributes. 20 | } 21 | \description{ 22 | \code{DIM()} is to \code{dim()} as \code{NROW()} is to \code{nrow()}. That is, it is identical to 23 | \code{dim()} in most cases except if the input is a bare atomic vector with no 24 | \code{dim} attribute, in which case, the length of the vector is returned instead 25 | of \code{NULL}. 26 | 27 | \code{DROP} first calls \code{base::drop} and then completely removes the \code{dim} 28 | attribute if the result is a 1-d array 29 | } 30 | \examples{ 31 | x <- 1:3 32 | dim(x) 33 | dim(array(x)) 34 | 35 | DIM(x) 36 | DIM(array(x)) 37 | 38 | x <- array(1:3) 39 | str(drop(x)) 40 | str(DROP(x)) 41 | } 42 | -------------------------------------------------------------------------------- /man/array2.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dim2.R 3 | \name{array2} 4 | \alias{array2} 5 | \alias{matrix2} 6 | \alias{dim2<-} 7 | \alias{set_dim2} 8 | \title{Make or reshape an array with C-style (row-major) semantics} 9 | \usage{ 10 | array2(data, dim = length(data), dimnames = NULL) 11 | 12 | matrix2(...) 13 | 14 | dim2(x) <- value 15 | 16 | set_dim2(...) 17 | } 18 | \arguments{ 19 | \item{data}{what to fill the array with} 20 | 21 | \item{dim}{numeric vector of dimensions} 22 | 23 | \item{dimnames}{a list of dimnames, must be the same length as \code{dims}} 24 | 25 | \item{...}{passed on to \code{set_dim()}} 26 | 27 | \item{x}{object to set dimensions on (array or atomic vector)} 28 | 29 | \item{value}{a numeric (integerish) vector of new dimensions} 30 | } 31 | \description{ 32 | These functions reshape or make an array using C-style, row-major semantics. 33 | The returned array is still R's native F-style, (meaning, the underlying 34 | vector has been reordered). 35 | } 36 | \details{ 37 | Other than the C-style semantics, these functions behave identically to their 38 | counterparts (\code{array2()} behaves identically to \code{array()}, \code{`dim2<-`()} 39 | to \code{`dim<-`()}). \code{set_dim2()} is just a wrapper around \code{set_dim(..., order = "C")}. 40 | 41 | See examples for a drop-in pure R replacement to \code{reticulate::array_reshape()} 42 | } 43 | \examples{ 44 | array(1:4, c(2,2)) 45 | array2(1:4, c(2,2)) 46 | 47 | # for a drop-in replacement to reticulate::array_reshape 48 | array_reshape <- listarrays:::array_reshape 49 | array_reshape(1:4, c(2,2)) 50 | } 51 | -------------------------------------------------------------------------------- /man/bind-arrays.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/bind.R 3 | \name{bind_as_dim} 4 | \alias{bind_as_dim} 5 | \alias{bind_as_rows} 6 | \alias{bind_as_cols} 7 | \alias{bind_on_dim} 8 | \alias{bind_on_rows} 9 | \alias{bind_on_cols} 10 | \title{Bind arrays along a specified dimension} 11 | \usage{ 12 | bind_as_dim(list_of_arrays, which_dim) 13 | 14 | bind_as_rows(...) 15 | 16 | bind_as_cols(...) 17 | 18 | bind_on_dim(list_of_arrays, which_dim) 19 | 20 | bind_on_rows(...) 21 | 22 | bind_on_cols(...) 23 | } 24 | \arguments{ 25 | \item{list_of_arrays}{a list of arrays. All arrays must be of the same 26 | dimension. NULL's in place of arrays are automatically dropped.} 27 | 28 | \item{which_dim}{Scalar integer specifying the index position of where to 29 | introduce the new dimension to introduce. Negative numbers count from the 30 | back. For example, given a 3 dimensional array, \code{-1}, is equivalent to \code{3}, 31 | \code{-2} to \code{2} and \code{-3} to \code{1}.} 32 | 33 | \item{...}{Arrays to be bound, specified individually or supplied as a single 34 | list} 35 | } 36 | \value{ 37 | An array, with one additional dimension. 38 | } 39 | \description{ 40 | \verb{bind_as_*} introduces a new dimension, such that each element in 41 | \code{list_of_arrays} corresponds to one index position along the new dimension in 42 | the returned array. \verb{bind_on_*} binds all elements along an existing 43 | dimension, (meaning, the returned array has the same number of dimensions as 44 | each of the arrays in the list). 45 | } 46 | \details{ 47 | \verb{bind_*_rows()} is a wrapper for the common case of \verb{bind_*_dim(X, 1)}. 48 | \verb{bind_*_cols()} is a wrapper for the common case of \verb{bind_*_dim(X, -1)}. 49 | } 50 | \examples{ 51 | list_of_arrays <- replicate(10, array(1:8, dim = c(2,3,4)), FALSE) 52 | 53 | dim(list_of_arrays[[1]]) 54 | 55 | # bind on a new dimension 56 | combined_as <- bind_as_rows(list_of_arrays) 57 | dim(combined_as) 58 | dim(combined_as)[1] == length(list_of_arrays) 59 | 60 | # each element in `list_of_arrays` corresponds to one "row" 61 | # (i.e., one entry in along the first dimension) 62 | for(i in seq_along(list_of_arrays)) 63 | stopifnot(identical(combined_as[i,,,], list_of_arrays[[i]])) 64 | 65 | # bind on an existing dimension 66 | combined_on <- bind_on_rows(list_of_arrays) 67 | dim(combined_on) 68 | dim(combined_on)[1] == sum(sapply(list_of_arrays, function(x) dim(x)[1])) 69 | identical(list_of_arrays[[1]], combined_on[1:2,,]) 70 | for (i in seq_along(list_of_arrays)) 71 | stopifnot(identical( 72 | list_of_arrays[[i]], combined_on[ (1:2) + (i-1)*2,,] 73 | )) 74 | 75 | # bind on any dimension 76 | combined <- bind_as_dim(list_of_arrays, 3) 77 | dim(combined) 78 | for(i in seq_along(list_of_arrays)) 79 | stopifnot(identical(combined[,,i,], list_of_arrays[[i]])) 80 | } 81 | -------------------------------------------------------------------------------- /man/drop_dimnames.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dimnames.R 3 | \name{drop_dimnames} 4 | \alias{drop_dimnames} 5 | \alias{drop_dim} 6 | \alias{drop_dim2} 7 | \title{Drop dimnames} 8 | \usage{ 9 | drop_dimnames(x, which_dim = NULL, keep_axis_names = FALSE) 10 | 11 | drop_dim(x) 12 | 13 | drop_dim2(x) 14 | } 15 | \arguments{ 16 | \item{x}{an object, potentially with dimnames} 17 | 18 | \item{which_dim}{If \code{NULL} (the default) then all dimnames are dropped. If 19 | integer vector, then dimnames only at the specified dimensions are dropped.} 20 | 21 | \item{keep_axis_names}{TRUE or FALSE, whether to preserve the axis names when 22 | dropping the dimnames} 23 | } 24 | \description{ 25 | A pipe-friendly wrapper for \code{dim(x) <- NULL} and \code{dimnames(x) <- NULL} or, if 26 | \code{which_dim} is not \code{NULL}, \code{dimnames(x)[which_dim] <- list(NULL)} 27 | } 28 | -------------------------------------------------------------------------------- /man/expand_dims.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dim.R 3 | \name{expand_dims} 4 | \alias{expand_dims} 5 | \title{Expand the shape of an array} 6 | \usage{ 7 | expand_dims(x, which_dim = -1L) 8 | } 9 | \arguments{ 10 | \item{x}{an array. Bare vectors are treated as 1-d arrays.} 11 | 12 | \item{which_dim}{numeric. Desired index position of the new axis or axes in 13 | the returned array. Negative numbers count from the back. Can be any 14 | length.Throws a warning if any duplicates are provided.} 15 | } 16 | \value{ 17 | the array \code{x} with new dim 18 | } 19 | \description{ 20 | This is the inverse operation of \code{base::drop()}. 21 | It is analogous to python's \code{numpy.expand_dims()}, but vectorized on 22 | \code{which_dim}. 23 | } 24 | \examples{ 25 | x <- array(1:24, 2:4) 26 | dim(x) 27 | dim(expand_dims(x)) 28 | dim(expand_dims(x, 2)) 29 | dim(expand_dims(x, c(1,2))) 30 | dim(expand_dims(x, c(1,-1))) 31 | dim(expand_dims(x, 6)) # implicitly also expands dims 4,5 32 | dim(expand_dims(x, 4:6)) 33 | 34 | # error, implicit expansion with negative indexes not supported 35 | try(expand_dims(x, -6)) 36 | 37 | # supply them explicitly instead 38 | dim(expand_dims(x, -(4:6))) 39 | } 40 | -------------------------------------------------------------------------------- /man/extract_dim.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/extract.R 3 | \name{extract_dim} 4 | \alias{extract_dim} 5 | \alias{extract_rows} 6 | \alias{extract_cols} 7 | \title{Extract with \code{[} on a specified dimension} 8 | \usage{ 9 | extract_dim(X, which_dim, idx, drop = NULL, depth = Inf) 10 | 11 | extract_rows(X, idx, drop = NULL, depth = Inf) 12 | 13 | extract_cols(X, idx, drop = NULL, depth = Inf) 14 | } 15 | \arguments{ 16 | \item{X}{Typically, an array, but any object with a \code{[} method is accepted 17 | (e.g., dataframe, vectors)} 18 | 19 | \item{which_dim}{A scalar integer or character, specifying the dimension to 20 | extract from} 21 | 22 | \item{idx}{A numeric, boolean, or character vector to perform subsetting 23 | with.} 24 | 25 | \item{drop}{Passed on to \code{[}. If \code{NULL} (the default), then drop is omitted 26 | from the argument, and the default is used (defaults to TRUE for most 27 | objects, including arrays)} 28 | 29 | \item{depth}{Scalar number, how many levels to recurse down if \code{X} is a list 30 | of arrays. Set this if you want to explicitly treat a list as a vector 31 | (that is, a one-dimensional array). (You can alternatively set a dim 32 | attribute with \verb{dim<-} on the list to prevent recursion)} 33 | } 34 | \description{ 35 | Extract with \code{[} on a specified dimension 36 | } 37 | \examples{ 38 | # extract_rows is useful to keep the same code path for arrays of various sizes 39 | X <- array(1:8, c(4, 3, 2)) 40 | y <- c("a", "b", "c", "d") 41 | (Y <- onehot(y)) 42 | 43 | extract_rows(X, 2) 44 | extract_rows(Y, 2) 45 | extract_rows(y, 2) 46 | 47 | library(zeallot) 48 | c(X2, Y2, y2) \%<-\% extract_rows(list(X, Y, y), 2) 49 | X2 50 | Y2 51 | y2 52 | } 53 | -------------------------------------------------------------------------------- /man/map_along_dim.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/map.R 3 | \name{map_along_dim} 4 | \alias{map_along_dim} 5 | \alias{map_along_rows} 6 | \alias{map_along_cols} 7 | \title{Apply a function across subsets along an array dimension} 8 | \usage{ 9 | map_along_dim(X, .dim, .f, ...) 10 | 11 | map_along_rows(X, .f, ...) 12 | 13 | map_along_cols(X, .f, ...) 14 | } 15 | \arguments{ 16 | \item{X}{an R array} 17 | 18 | \item{.dim}{which dimension to map along. Passed on to \code{\link[=split_along_dim]{split_along_dim()}}, 19 | and accepts all the same inputs. Valid inputs include 20 | \itemize{ 21 | \item positive integers (index position(s) of dimension), 22 | \item negative integers (index positions(s) of dimensions, counting from the back), or 23 | \item character vector (corresponding to array dimnames) 24 | }} 25 | 26 | \item{.f}{A function, string of a function name, or \code{purrr} style compact lambda syntax (e.g, \code{~.x + 1})} 27 | 28 | \item{...}{passed on to \code{.f()}} 29 | } 30 | \value{ 31 | An R list 32 | } 33 | \description{ 34 | \code{map_along_dim(X, dim, func)} is a simple wrapper around \code{split_along_dim(X, dim) \%>\% map(func)}. It is conceptually and functionally equivalent to 35 | \code{base::apply()}, with the following key differences: 36 | \itemize{ 37 | \item it is guaranteed to return a list (\code{base::apply()} attempts to simplify the 38 | output to an array, sometimes unsuccessfully, making the output unstable) 39 | \item it accepts the compact lambda notation \code{~.x} just like in \code{\link[purrr:map]{purrr::map}} 40 | (and \code{\link[=modify_along_dim]{modify_along_dim()}}) 41 | } 42 | } 43 | \examples{ 44 | X <- matrix2(letters[1:15], ncol = 3) 45 | 46 | apply(X, 1, function(x) paste(x, collapse = "")) # simplifies to a vector 47 | map_along_dim(X, 1, ~paste(.x, collapse = "")) # returns a list 48 | 49 | identical( 50 | map_along_rows(X, identity), 51 | map_along_dim(X, 1, identity)) # TRUE 52 | 53 | identical( 54 | map_along_cols(X, identity), 55 | map_along_dim(X, -1, identity)) # TRUE 56 | } 57 | -------------------------------------------------------------------------------- /man/modify_along_dim.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/modify.R 3 | \name{modify_along_dim} 4 | \alias{modify_along_dim} 5 | \alias{modify_along_rows} 6 | \alias{modify_along_cols} 7 | \title{Modify an array by mapping over 1 or more dimensions} 8 | \usage{ 9 | modify_along_dim(X, which_dim, .f, ...) 10 | 11 | modify_along_rows(X, .f, ...) 12 | 13 | modify_along_cols(X, .f, ...) 14 | } 15 | \arguments{ 16 | \item{X}{An array, or a list of arrays} 17 | 18 | \item{which_dim}{integer vector of dimensions to modify at} 19 | 20 | \item{.f}{a function or formula defining a function(same semantics as 21 | \code{\link[purrr:map]{purrr::map()}}). The function must return either an array the same shape 22 | as it was passed, a vector of the same length, or a scalar, although the 23 | type of the returned object does not need to be the same as was passed in.} 24 | 25 | \item{...}{passed on to \code{.f()}} 26 | } 27 | \value{ 28 | An array, or if \code{X} was a list, a list of arrays of the same shape as 29 | was passed in. 30 | } 31 | \description{ 32 | This function can be thought of as a version of \code{base::apply()} that is 33 | guaranteed to return a object of the same dimensions as it was input. It also 34 | generally preserves attributes, as it's built on top of \verb{[<-}. 35 | } 36 | \examples{ 37 | x <- array(1:6, 1:3) 38 | modify_along_dim(x, 3, ~mean(.x)) 39 | modify_along_dim(x, 3, ~.x/mean(.x)) 40 | } 41 | -------------------------------------------------------------------------------- /man/ndim.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{ndim} 4 | \alias{ndim} 5 | \title{Length of \code{DIM()}} 6 | \usage{ 7 | ndim(x) 8 | } 9 | \arguments{ 10 | \item{x}{a matrix or atomic vector} 11 | } 12 | \description{ 13 | Returns the number of dimensions, or 1 for an atomic vector. 14 | } 15 | -------------------------------------------------------------------------------- /man/onehot.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/onehot.R 3 | \name{onehot_with_decoder} 4 | \alias{onehot_with_decoder} 5 | \alias{onehot} 6 | \alias{decode_onehot} 7 | \alias{onehot_decoder} 8 | \title{Convert vector to a onehot representation (binary class matrix)} 9 | \usage{ 10 | onehot_with_decoder(y, order = NULL, named = TRUE) 11 | 12 | onehot(y, order = NULL, named = TRUE) 13 | 14 | decode_onehot( 15 | Y, 16 | classes = colnames(Y), 17 | n_classes = ncol(Y) \%||\% length(classes) 18 | ) 19 | 20 | onehot_decoder(Y, classes = colnames(Y), n_classes = length(classes)) 21 | } 22 | \arguments{ 23 | \item{y}{character, factor, or numeric vector} 24 | 25 | \item{order}{\code{NULL}, \code{FALSE}, or a character vector. If \code{NULL} (the default), 26 | then levels are sorted with \code{sort()}. If \code{FALSE}, then levels are taken in 27 | order of their first appearance in \code{y}. If a character vector, then \code{order} 28 | must contain all levels found in \code{y}.} 29 | 30 | \item{named}{if the returned matrix should have column names} 31 | 32 | \item{Y}{a matrix, as returned by \code{onehot()} or similar.} 33 | 34 | \item{classes}{A character vector of class names in the order corresponding 35 | to \code{Y}'s onehot encoding. Typically, \code{colnames(Y)}. if \code{NULL}, then the 36 | decoder returns the column number.} 37 | 38 | \item{n_classes}{The total number of classes expected in \code{Y}. Used for input 39 | checking in the returned decoder, also, to reconstruct the correct 40 | dimensions if the passed in \code{Y} is missing \code{dim()} attributes.} 41 | } 42 | \value{ 43 | A binary class matrix 44 | } 45 | \description{ 46 | Convert vector to a onehot representation (binary class matrix) 47 | } 48 | \examples{ 49 | if(require(zeallot)) { 50 | y <- letters[1:4] 51 | c(Y, decode) \%<-\% onehot_with_decoder(y) 52 | Y 53 | decode(Y) 54 | identical(y, decode(Y)) 55 | decode(Y[2,,drop = TRUE]) 56 | decode(Y[2,,drop = FALSE]) 57 | decode(Y[2:3,]) 58 | 59 | rm(Y, decode) 60 | } 61 | 62 | # more peicemeal functions 63 | Y <- onehot(y) 64 | decode_onehot(Y) 65 | 66 | # if you need to decode a matrix that lost colnames, 67 | # make your own decoder that remembers classes 68 | my_decode <- onehot_decoder(Y) 69 | colnames(Y) <- NULL 70 | my_decode(Y) 71 | decode_onehot(Y) 72 | 73 | # factor and numeric vectors also accepted 74 | onehot(factor(letters[1:4])) 75 | onehot(4:8) 76 | 77 | } 78 | \seealso{ 79 | \link[keras:to_categorical]{keras::to_categorical} 80 | } 81 | -------------------------------------------------------------------------------- /man/seq_along_dim.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/seq_along.R 3 | \name{seq_along_dim} 4 | \alias{seq_along_dim} 5 | \alias{seq_along_rows} 6 | \alias{seq_along_cols} 7 | \title{Sequence along a dimension} 8 | \usage{ 9 | seq_along_dim(x, which_dim) 10 | 11 | seq_along_rows(x) 12 | 13 | seq_along_cols(x) 14 | } 15 | \arguments{ 16 | \item{x}{a dataframe, array or vector. For \code{seq_along_rows}, and 17 | \code{seq_along_cols} sequence along the first and last dimensions, 18 | respectively. Atomic vectors are treated as 1 dimensional 19 | arrays (i.e., \code{seq_along_rows} is equivalent to \code{seq_along} when \code{x} is an 20 | atomic vector or list).} 21 | 22 | \item{which_dim}{a scalar integer or character string, specifying which 23 | dimension to generate a sequence for. Negative numbers count from the back.} 24 | } 25 | \value{ 26 | a vector of integers 1:nrow(x), safe for use in \code{for} loops and 27 | vectorized equivalents. 28 | } 29 | \description{ 30 | Sequence along a dimension 31 | } 32 | \examples{ 33 | for (r in seq_along_rows(mtcars[1:4,])) 34 | print(mtcars[r,]) 35 | 36 | x <- 1:3 37 | identical(seq_along_rows(x), seq_along(x)) 38 | 39 | } 40 | -------------------------------------------------------------------------------- /man/set_as_rows.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/reshape.R 3 | \name{set_as_rows} 4 | \alias{set_as_rows} 5 | \alias{set_as_cols} 6 | \title{Reshape an array to send a dimension forward or back} 7 | \usage{ 8 | set_as_rows(X, which_dim) 9 | 10 | set_as_cols(X, which_dim) 11 | } 12 | \arguments{ 13 | \item{X}{an array} 14 | 15 | \item{which_dim}{scalar integer or string, which dim to bring forward. 16 | Negative numbers count from the back 17 | 18 | This is a powered by \code{base::aperm()}.} 19 | } 20 | \value{ 21 | a reshaped array 22 | } 23 | \description{ 24 | Reshape an array to send a dimension forward or back 25 | } 26 | \examples{ 27 | x <- array(1:24, 2:4) 28 | y <- set_as_rows(x, 3) 29 | 30 | for (i in seq_along_dim(x, 3)) 31 | stopifnot( identical(x[,,i], y[i,,]) ) 32 | } 33 | \seealso{ 34 | \code{base::aperm()} \code{set_dim()} \code{keras::array_reshape()} 35 | } 36 | -------------------------------------------------------------------------------- /man/set_dim.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dim.R 3 | \name{set_dim} 4 | \alias{set_dim} 5 | \title{Reshape an array} 6 | \usage{ 7 | set_dim( 8 | x, 9 | new_dim, 10 | pad = getOption("listarrays.autopad_arrays_with", NULL), 11 | order = c("F", "C"), 12 | verbose = getOption("verbose") 13 | ) 14 | } 15 | \arguments{ 16 | \item{x}{A vector or array to set dimensions on} 17 | 18 | \item{new_dim}{The desired dimensions (an integer(ish) vector)} 19 | 20 | \item{pad}{The value to pad the vector with. \code{NULL} (the default) performs no 21 | padding.} 22 | 23 | \item{order}{whether to use row-major (C) or column major (F) style 24 | semantics. The default, "F", corresponds to the default behavior of R's 25 | \verb{dim<-()}, while "C" corresponds to the default behavior of 26 | \code{reticulate::array_reshape()}, numpy, reshaping semantics commonly 27 | encountered in the python world.} 28 | 29 | \item{verbose}{Whether to emit a message if padding. By default, \code{FALSE}.} 30 | } 31 | \value{ 32 | Object with dimensions set 33 | } 34 | \description{ 35 | Pipe friendly \verb{dim<-()}, with option to pad to necessary length. Also allows 36 | for filling the array using C style row-major semantics. 37 | } 38 | \examples{ 39 | set_dim(1:10, c(2, 5)) 40 | try( set_dim(1:7, c(2, 5)) ) # error by default, just like `dim<-`() 41 | set_dim(1:7, c(2, 5), pad = 99) 42 | set_dim(1:7, c(2, 5), pad = 99, order = "C") # fills row-wise 43 | 44 | y <- x <- 1:4 45 | # base::dim<- fills the array column wise 46 | dim(x) <- c(2, 2) 47 | x 48 | 49 | # dim2 will fill the array row-wise 50 | dim2(y) <- c(2, 2) 51 | y 52 | 53 | identical(x, set_dim(1:4, c(2,2))) 54 | identical(y, set_dim(1:4, c(2,2), order = "C")) 55 | 56 | \dontrun{ 57 | py_reshaped <- reticulate::array_reshape(1:4, c(2,2)) 58 | storage.mode(py_reshaped) <- "integer" # reticulate coerces to double 59 | identical(y, py_reshaped) 60 | # if needed, see listarrays:::array_reshape() for 61 | # a drop-in pure R replacement for reticulate::array_reshape() 62 | } 63 | } 64 | \seealso{ 65 | \code{set_dim2()}, \code{`dim<-`()}, \code{reticulate::array_reshape()} 66 | } 67 | -------------------------------------------------------------------------------- /man/set_dimnames.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dimnames.R 3 | \name{set_dimnames} 4 | \alias{set_dimnames} 5 | \title{Set dimnames} 6 | \usage{ 7 | set_dimnames(x, nm, which_dim = NULL) 8 | } 9 | \arguments{ 10 | \item{x}{an array} 11 | 12 | \item{nm}{A list or character vector.} 13 | 14 | \item{which_dim}{a character vector or numeric vector or \code{NULL}} 15 | } 16 | \value{ 17 | x, with modified dimnames and or axisnames 18 | } 19 | \description{ 20 | A more flexible and pipe-friendly version of \verb{dimnames<-}. 21 | } 22 | \details{ 23 | This function is quite flexible. See examples for the complete 24 | picture. 25 | } 26 | \note{ 27 | The word "dimnames" is slightly overloaded. Most commonly it refers to 28 | the names of entries along a particular axis (e.g., date1, date2, date3, 29 | ...), but occasionally it is also used to refer to the names of the array 30 | axes themselves (e.g, dates, temperature, pressure, ...). To disambiguate, 31 | in the examples 'dimnames' always refers to the first case, while 'axis 32 | names' refers to the second. \code{set_dimnames()} can be used to set either or both 33 | axis names and dimnames. 34 | } 35 | \examples{ 36 | x <- array(1:8, 2:4) 37 | 38 | # to set axis names, leave which_dim=NULL and pass a character vector 39 | dimnames(set_dimnames(x, c("a", "b", "c"))) 40 | 41 | # to set names along a single axis, specify which_dim 42 | dimnames(set_dimnames(x, c("a", "b", "c"), 2)) 43 | 44 | # to set an axis name and names along the axis, pass a named list 45 | dimnames(set_dimnames(x, list(axis2 = c("a", "b", "c")), 2)) 46 | dimnames(set_dimnames(x, list(axis2 = c("a", "b", "c"), 47 | axis3 = 1:4), which_dim = 2:3)) 48 | 49 | # if the array already has axis names, those are used when possible 50 | nx <- set_dimnames(x, paste0("axis", 1:3)) 51 | dimnames(nx) 52 | dimnames(set_dimnames(nx, list(axis2 = c("x", "y", "z")))) 53 | dimnames(set_dimnames(nx, c("x", "y", "z"), which_dim = "axis2")) 54 | 55 | 56 | # pass NULL to drop all dimnames, or just names along a single dimension 57 | nx2 <- set_dimnames(nx, c("x", "y", "z"), which_dim = "axis2") 58 | nx2 <- set_dimnames(nx2, LETTERS[1:4], which_dim = "axis3") 59 | dimnames(nx2) 60 | dimnames(set_dimnames(nx2, NULL)) 61 | dimnames(set_dimnames(nx2, NULL, 2)) 62 | dimnames(set_dimnames(nx2, NULL, c(2, 3))) 63 | # to preserve an axis name and only drop the dimnames, wrap the NULL in a list() 64 | dimnames(set_dimnames(nx2, list(NULL))) 65 | dimnames(set_dimnames(nx2, list(NULL), 2)) 66 | dimnames(set_dimnames(nx2, list(axis2 = NULL))) 67 | dimnames(set_dimnames(nx2, list(axis2 = NULL, axis3 = NULL))) 68 | dimnames(set_dimnames(nx2, list(NULL), 2:3)) 69 | } 70 | -------------------------------------------------------------------------------- /man/shuffle_rows.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/shuffle.R 3 | \name{shuffle_rows} 4 | \alias{shuffle_rows} 5 | \title{Shuffle along the first dimension multiple arrays in sync} 6 | \usage{ 7 | shuffle_rows(...) 8 | } 9 | \arguments{ 10 | \item{...}{arrays of various dimensions (vectors and data.frames OK too)} 11 | } 12 | \value{ 13 | A list of objects passed on to \code{...}, or if a single object was 14 | supplied, then the single object shuffled 15 | } 16 | \description{ 17 | Shuffle along the first dimension multiple arrays in sync 18 | } 19 | \examples{ 20 | x <- 1:3 21 | y <- matrix(1:9, ncol = 3) 22 | z <- array(1:27, c(3,3,3)) 23 | 24 | if(require(zeallot)) { 25 | c(xs, ys, zs) \%<-\% shuffle_rows(x, y, z) 26 | 27 | l <- lapply(seq_along_rows(y), function(r) { 28 | list(x = x[r], y = y[r,], z = z[r,,]) 29 | }) 30 | 31 | ls <- lapply(seq_along_rows(y), function(r) { 32 | list(x = xs[r], y = ys[r,], z = zs[r,,]) 33 | }) 34 | 35 | stopifnot( 36 | length(unique(c(l, ls))) == length(l)) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /man/split-array.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/split.R 3 | \name{split_on_dim} 4 | \alias{split_on_dim} 5 | \alias{split_on_rows} 6 | \alias{split_on_cols} 7 | \alias{split_along_dim} 8 | \alias{split_along_rows} 9 | \alias{split_along_cols} 10 | \title{Split an array along a dimension} 11 | \usage{ 12 | split_on_dim( 13 | X, 14 | which_dim, 15 | f = dimnames(X)[[which_dim]], 16 | drop = FALSE, 17 | depth = Inf 18 | ) 19 | 20 | split_on_rows(X, f = rownames(X), drop = FALSE, depth = Inf) 21 | 22 | split_on_cols(X, f = rownames(X), drop = FALSE, depth = Inf) 23 | 24 | split_along_dim(X, which_dim, depth = Inf) 25 | 26 | split_along_rows(X, depth = Inf) 27 | 28 | split_along_cols(X, depth = Inf) 29 | } 30 | \arguments{ 31 | \item{X}{an array, or list of arrays. An atomic vector without a dimension 32 | attribute is treated as a 1 dimensional array (Meaning, atomic vectors 33 | without a dim attribute are only accepted if \code{which_dim} is \code{1}. Names of 34 | the passed list are preserved. If a list of arrays, all the arrays must 35 | have the same length of the dimension being split.} 36 | 37 | \item{which_dim}{a scalar string or integer, specifying which dimension to 38 | split along. Negative integers count from the back. If a string, it must 39 | refer to a named dimension (e.g, one of \code{names(dimnames(X))}.} 40 | 41 | \item{f}{Specify how to split the dimension. \describe{ 42 | 43 | \item{character, integer, factor}{passed on to \code{base::split()}. Must be the 44 | same length as the dimension being split.} 45 | 46 | \item{a list of vectors}{Passed on to \code{base::interaction()} then 47 | \code{base::split()}. Each vector in the list must be the same length as the 48 | dimension being split.} 49 | 50 | \item{a scalar integer}{used to split into that many groups of equal size} 51 | 52 | \item{a numeric vector where \code{all(f<0)}}{specifies the relative size 53 | proportions of the groups being split. \code{sum(f)} must be \code{1}. For 54 | example \code{c(0.2, 0.2, 0.6)} will return approximately a 20\\%-20\\%-60\\% 55 | split.} }} 56 | 57 | \item{drop}{passed on to \code{[}.} 58 | 59 | \item{depth}{Scalar number, how many levels to recurse down. Set this if you 60 | want to explicitly treat a list as a vector (that is, a one-dimensional 61 | array). (You can alternatively set dim attributes with \verb{dim<-} on the list 62 | to prevent recursion) 63 | 64 | \code{split_along_dim(X, which_dim)} is equivalent to \code{split_on_dim(X, which_dim, seq_along_dim(X, which_dim))}.} 65 | } 66 | \value{ 67 | A list of arrays, or if a list of arrays was passed in, then a list 68 | of lists of arrays. 69 | } 70 | \description{ 71 | Split an array along a dimension 72 | } 73 | \examples{ 74 | X <- array(1:8, c(2,3,4)) 75 | X 76 | split_along_dim(X, 2) 77 | 78 | # specify f as a factor, akin to base::split() 79 | split_on_dim(X, 2, c("a", "a", "b"), drop = FALSE) 80 | 81 | d <- c(10, 3, 3) 82 | X <- array(1:prod(d), d) 83 | y <- letters[1:10] 84 | Y <- onehot(y) 85 | 86 | # specify `f`` as relative partition sizes 87 | if(require(zeallot) && require(magrittr) && require(purrr)) { 88 | 89 | c(train, validate, test) \%<-\% { 90 | list(X = X, Y = Y, y = y) \%>\% 91 | shuffle_rows() \%>\% 92 | split_on_rows(c(0.6, 0.2, 0.2)) \%>\% 93 | transpose() 94 | } 95 | 96 | str(test) 97 | str(train) 98 | str(validate) 99 | 100 | } 101 | 102 | 103 | # with with array data in a data frame by splitting row-wise 104 | if(require(tibble)) 105 | tibble(y, X = split_along_rows(X)) 106 | } 107 | -------------------------------------------------------------------------------- /pkgdown/extra.css: -------------------------------------------------------------------------------- 1 | 2 | .dl-horizontal dt { 3 | white-space: normal; 4 | } -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(listarrays) 3 | 4 | test_check("listarrays") 5 | -------------------------------------------------------------------------------- /tests/testthat/test-bind.R: -------------------------------------------------------------------------------- 1 | context("test-bind.R") 2 | 3 | test_that("bind arrays works", { 4 | 5 | lx <- replicate(10, array(1:8, 2:4), FALSE) 6 | 7 | for(d in 1:3) 8 | expect_equal(dim(bind_as_dim(lx, d))[d], 10L) 9 | 10 | for(d in 1:3) 11 | expect_equal(dim(bind_on_dim(lx, d))[d], 10L*dim(lx[[1]])[d]) 12 | 13 | 14 | # dnn <- dimnames(provideDimnames(lx[[1]])) 15 | # dnn <- lapply(seq_along(dnn), function(i) { 16 | # paste(i, dnn[[i]], sep = "_") 17 | # }) 18 | dnn <- list(c("1_A", "1_B"), 19 | c("2_A", "2_B", "2_C"), 20 | c("3_A", "3_B", "3_C", "3_D")) 21 | 22 | lx <- lapply(lx, function(x) { 23 | dimnames(x) <- dnn 24 | x 25 | }) 26 | 27 | new_dnn <- dimnames(bind_as_rows(lx)) 28 | expect_identical(new_dnn, c(list(NULL), dnn)) 29 | 30 | names(lx) <- paste0("newdim_", 1:10) 31 | new_dnn <- dimnames(bind_as_rows(lx)) 32 | expect_identical(new_dnn, c(list(names(lx)), dnn)) 33 | 34 | # trip cmpfun limit 35 | lx <- as.list(1:2000) 36 | names(lx) <- as.character(1:2000) 37 | lx <- lapply(lx, function(.) matrix(as.character(1:12), nrow = 4)) 38 | 39 | x <- bind_on_rows(lx) 40 | 41 | y <- do.call(rbind, lx) 42 | rownames(y) <- rep(names(lx), each = 4) 43 | 44 | expect_identical(x, y) 45 | 46 | }) 47 | -------------------------------------------------------------------------------- /tests/testthat/test-dimnames.R: -------------------------------------------------------------------------------- 1 | context("test-dimnames.R") 2 | 3 | test_that("setting dimnames works", { 4 | # ar <- function() array(1:8, 2:4) 5 | x <- array(1:8, 2:4) 6 | xyz <- c("x", "y", "z") 7 | 8 | # bare character string sets names of dims 9 | x_named <- set_dimnames(x, xyz) 10 | expect_equal(names(dimnames(x_named)), xyz) 11 | 12 | # setting a single dim with a character string works 13 | y <- set_dimnames(x, xyz, which_dim = 2) 14 | expect_equal(dimnames(y)[[2L]], xyz) 15 | 16 | y <- set_dimnames(x_named, xyz, which_dim = 2) 17 | expect_equal(dimnames(y)[[2L]], xyz) 18 | expect_equal(names(dimnames(y)), xyz) 19 | 20 | y <- set_dimnames(x_named, xyz, which_dim = "y") 21 | expect_equal(dimnames(y)[[2L]], xyz) 22 | expect_equal(names(dimnames(y)), xyz) 23 | 24 | 25 | # setting with a list works 26 | y <- set_dimnames(x, list(xyz), which_dim = 2) 27 | expect_equal(dimnames(y)[[2L]], xyz) 28 | 29 | y <- set_dimnames(x_named, list(xyz), which_dim = 2) 30 | expect_equal(dimnames(y)[[2L]], xyz) 31 | expect_equal(names(dimnames(y)), xyz) 32 | 33 | y <- set_dimnames(x_named, list(xyz), which_dim = "y") 34 | expect_equal(dimnames(y)[[2L]], xyz) 35 | expect_equal(names(dimnames(y)), xyz) 36 | 37 | y2 <- set_dimnames(x_named, list(y = xyz)) 38 | expect_identical(y, y2) 39 | 40 | y <- set_dimnames(x, list(z = xyz), which_dim = 2) 41 | expect_equal(names(dimnames(y)), c("", "z", "")) 42 | expect_equal(dimnames(y)[["z"]], xyz) 43 | 44 | 45 | # setting more than one dimensionat a time works 46 | y <- set_dimnames(x, list(a = xyz, b = letters[1:4]), which_dim = 2:3) 47 | expect_equal(names(dimnames(y)), c("", "a", "b")) 48 | expect_equal(dimnames(y)[["a"]], xyz) 49 | expect_equal(dimnames(y)[["b"]], letters[1:4]) 50 | 51 | 52 | }) 53 | 54 | 55 | 56 | test_that("specifying `which_dim` by name works", { 57 | 58 | x <- provideDimnames(array(1:8, 2:4)) 59 | x <- set_dimnames(x, paste0("axis", 1:3)) 60 | 61 | expect_identical(extract_dim(x, "axis2", 1), 62 | extract_dim(x, 2L, 1)) 63 | 64 | f <- c(TRUE, TRUE, FALSE) 65 | expect_identical(split_on_dim(x, "axis2", f), 66 | split_on_dim(x, 2L, f)) 67 | 68 | expect_identical(split_along_dim(x, "axis2"), 69 | split_along_dim(x, 2L)) 70 | 71 | expect_identical(modify_along_dim(x, "axis2", function(x) x + 100), 72 | modify_along_dim(x, 2L, function(x) x + 100)) 73 | 74 | xx <- rep_len(list(x), 5) 75 | expect_identical(bind_on_dim(xx, "axis2"), 76 | bind_on_dim(xx, 2L)) 77 | 78 | 79 | }) 80 | -------------------------------------------------------------------------------- /tests/testthat/test-map.R: -------------------------------------------------------------------------------- 1 | context("test-map.R") 2 | 3 | 4 | test_that("map_along_dim", { 5 | A <- matrix2(letters[1:15], ncol = 3) 6 | 7 | for (d in seq_along(dim(A))) 8 | expect_identical( 9 | as.list(apply(A, 1, function(x) paste(x, collapse = ""))), 10 | map_along_rows(A, ~ paste(.x, collapse = ""))) 11 | }) 12 | -------------------------------------------------------------------------------- /tests/testthat/test-modify.R: -------------------------------------------------------------------------------- 1 | context("test-modify.R") 2 | 3 | test_that("modify_along_dim works", { 4 | x <- array(1:840, 4:7) 5 | 6 | for (d in seq_along(4:7)) { 7 | expect_identical(x, modify_along_dim(x, d, identity)) 8 | } 9 | 10 | # accepts returned without dim 11 | 12 | for (d in seq_along(4:7)) 13 | expect_identical(x, modify_along_dim(x, d, as.vector)) 14 | 15 | cx <- array(as.character(x), dim = 4:7) 16 | 17 | for (d in seq_along(4:7)) 18 | expect_identical(cx, modify_along_dim(x, d, paste)) 19 | 20 | 21 | # works with more than one dimension 22 | for (d in combn(1:length(dim(x)), 2, simplify = FALSE)) { 23 | expect_identical(x, modify_along_dim(x, d, identity)) 24 | } 25 | 26 | for (d in combn(1:length(dim(x)), 3, simplify = FALSE)) { 27 | expect_identical(x, modify_along_dim(x, d, identity)) 28 | } 29 | 30 | # go big enough to trigger calling cmpfun() 31 | arr <- function(...) array(seq_len(prod(c(...))), c(...)) 32 | x <- arr(5:9) 33 | expect_identical(x, modify_along_dim(x, 3:5, identity)) 34 | 35 | }) 36 | -------------------------------------------------------------------------------- /tests/testthat/test-onehot.R: -------------------------------------------------------------------------------- 1 | context("test-onehot.R") 2 | 3 | test_that("onehot encoding and decoding", { 4 | 5 | y <- letters[1:4] 6 | Y <- matrix(c(1, 0, 0, 0, 7 | 0, 1, 0, 0, 8 | 0, 0, 1, 0, 9 | 0, 0, 0, 1), 10 | byrow = TRUE, ncol = 4) 11 | colnames(Y) <- y 12 | 13 | 14 | 15 | expect_identical(Y, onehot(y)) 16 | expect_identical(y, decode_onehot(Y)) 17 | 18 | decode <- onehot_with_decoder(y)[[2]] 19 | expect_identical(y, decode(Y)) 20 | 21 | 22 | }) 23 | -------------------------------------------------------------------------------- /tests/testthat/test-shuffle.R: -------------------------------------------------------------------------------- 1 | context("test-shuffle.R") 2 | 3 | test_that("shuffle_rows", { 4 | x <- 1:3 5 | y <- matrix(1:9, ncol = 3) 6 | z <- array(1:27, c(3,3,3)) 7 | 8 | suppressWarnings(RNGversion("3.5.0")) 9 | set.seed(1) 10 | l <- shuffle_rows(x, y, z) 11 | 12 | i <- c(1L, 3L, 2L) 13 | expect_identical(x[i], l[[1]]) 14 | expect_identical(y[i,], l[[2]]) 15 | expect_identical(z[i,,], l[[3]]) 16 | 17 | }) 18 | -------------------------------------------------------------------------------- /tests/testthat/test-split.R: -------------------------------------------------------------------------------- 1 | context("test-split.R") 2 | 3 | test_that("split_along_dim", { 4 | x <- array(1:840, 4:7) 5 | 6 | for (d in seq_along(dim(x))) 7 | expect_length(split_along_dim(x, d), dim(x)[d]) 8 | 9 | }) 10 | 11 | 12 | test_that("split_on_dim", { 13 | x <- array(1:840, 4:7) 14 | 15 | for (d in seq_along(dim(x))) { 16 | f <- rep_len(1:2, dim(x)[d]) 17 | expect_length(split_on_dim(x, d, f), 2L) 18 | } 19 | 20 | }) 21 | 22 | 23 | test_that("split* works recursively", { 24 | x1 <- 1:4 25 | x2 <- matrix(1:20, nrow = 4, ncol = 5) 26 | x3 <- array(1:840, 4:7) 27 | 28 | l <- split_along_rows(list(x1, x2, x3)) 29 | 30 | for (i in seq_along(l)) { 31 | expect_length(l[[i]], 4) 32 | } 33 | 34 | }) 35 | 36 | test_that("split_on f inputs", { 37 | x <- array(1:840, 4:7) 38 | 39 | # test a scalar integer 40 | l <- split_on_rows(x, 4) 41 | expect_length(l, 4L) 42 | for (i in seq_along(l)) 43 | expect_equal(dim(l[[i]]), c(1, 5:7)) 44 | 45 | l <- split_on_rows(x, 4, drop = TRUE) 46 | expect_length(l, 4L) 47 | for (i in seq_along(l)) 48 | expect_equal(dim(l[[i]]), 5:7) 49 | 50 | # test a vector of proportions 51 | x <- bind_as_rows(rep_len(list(array(1:8, 2:4)), 10)) 52 | l <- split_on_rows(x, c(0.2, 0.2, 0.6), drop = FALSE) 53 | expect_length(l, 3L) 54 | expect_equal(nrow(l[[1]]), 2L) 55 | expect_equal(nrow(l[[2]]), 2L) 56 | expect_equal(nrow(l[[3]]), 6L) 57 | 58 | }) 59 | 60 | 61 | 62 | test_that("split_along_rows works with common vector types", { 63 | m <- matrix(1:20, nrow = 4, ncol = 5) 64 | 65 | set.seed(42) 66 | for (mode in c("integer", "double", "complex", "logical", "list", "character")) { 67 | storage.mode(m) <- mode 68 | if (mode == "logical") 69 | m[] <- sample(c(TRUE, FALSE), length(m), TRUE) 70 | expect_equal(split_along_rows(m), 71 | array(lapply(1:4, function(r) array(m[r, ])))) 72 | } 73 | 74 | }) 75 | --------------------------------------------------------------------------------