Skip to content
  • rray_roll() rolls the elements of x along one or more axes, with one roll per axis. Everything along an axis moves by the same amount.

  • rray_roll_each() rolls the elements of x along a single axis, with a separate roll for each row, column, and so on. For example, with a matrix and axis = 2, each row can move by a different amount.

An element pushed off one end circles back on the other.

Usage

rray_roll(x, ..., n, axes)

rray_roll_each(x, ..., n, axis)

Arguments

x

An array.

...

These dots are for future extensions and must be empty.

n

For rray_roll(), an integer vector indicating the amount to roll, either size 1 or the size of axes.

For rray_roll_each(), an integer array indicating the amount to roll for each row, column, etc., as determined by axis. It is broadcast to the dimensions of x with axis set to 1.

axes

The axes to roll along, given by position or by title.

axis

The axis to roll along, given by position or by title.

Value

An array with the same type and dimensions as x.

Details

A positive n moves elements toward the end of the axis, and a negative n moves them toward the start. Rolls circle around, so an n of 7 along an axis with dimension 5 is the same as an n of 2. An n of 0, or any multiple of the dimension, leaves the axis unchanged.

Roll

Rolling an axis with a uniform n can be thought of as a carefully crafted rray_slice(). Along an axis with dimension d, position i of the result comes from position (i - 1 - n) %% d + 1 of x:

x <- c(a = 1L, b = 2L, c = 3L, d = 4L, e = 5L)

n <- 2L
d <- length(x)
i <- seq_len(d)

(i - 1L - n) %% d + 1L
#> [1] 4 5 1 2 3

rray_roll(x, n = n, axes = 1)
#> d e a b c
#> 4 5 1 2 3

rray_slice_axis(x, c(4, 5, 1, 2, 3), axis = 1)
#> d e a b c
#> 4 5 1 2 3

Rolling several axes at once is one rray_slice(), with these locations on each rolled axis and TRUE on the rest.

Since a uniform roll is a slice, names on a rolled axis move with the data, just like they do in rray_slice(). Every other axis keeps its names untouched. The titles of x are kept.

Roll each

rray_roll_each() rolls along a single axis, but each row, column, and so on can move by its own amount. That is why n is an array rather than a vector. It is broadcast to the dimensions of x with axis set to 1, so it holds one n for every row, column, and so on that gets rolled.

Variable rolling can be mimicked with the more flexible indexing of rray_index(). Along axis, the coordinates come from the same (i - 1 - n) %% d + 1 formula as rray_roll(), with a different n for each row, column, and so on. Along every other axis, the coordinates are just the positions themselves.

rray_roll_each() drops names along axis, but keeps all other names and all titles. When rows move by different amounts, the output's column 1 can hold data from column c in one row and from column b in another, so the original column names are unlikely to make sense:

x <- matrix(1:6, nrow = 2, dimnames = list(c("r1", "r2"), c("a", "b", "c")))

rray_roll_each(x, n = matrix(c(1, 2), ncol = 1), axis = 2)
#>    [,1] [,2] [,3]
#> r1    5    1    3
#> r2    4    6    2

Because variable rolling can be a bit complicated the first time you see it, we'll work through 2D and 3D examples below.

Two dimensions

Start with a matrix:

x <- matrix(1:12, nrow = 3, byrow = TRUE)

x
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    2    3    4
#> [2,]    5    6    7    8
#> [3,]    9   10   11   12

Rolling along the columns, axis = 2, moves elements within each row. x has dimensions c(3, 4), so n is broadcast to c(3, 1), a one column matrix with one n per row:

n <- matrix(c(1L, 0L, -1L), ncol = 1)

rray_roll_each(x, n = n, axis = 2)
#>      [,1] [,2] [,3] [,4]
#> [1,]    4    1    2    3
#> [2,]    5    6    7    8
#> [3,]   10   11   12    9

The first row moves 1 toward the end, the second row stays put, and the third row moves 1 toward the start.

Rolling along the rows, axis = 1, moves elements within each column. Now n is broadcast to c(1, 4), so it must be a one row matrix:

rray_roll_each(x, n = matrix(c(0L, 1L, 2L, 3L), nrow = 1), axis = 1)
#>      [,1] [,2] [,3] [,4]
#> [1,]    1   10    7    4
#> [2,]    5    2   11    8
#> [3,]    9    6    3   12

The last column has an n of 3 on a dimension of 3, a full circle, so it doesn't move.

Three dimensions

Three dimensions get a bit more complicated, but demonstrate why n must be an array. Think of an array with dimensions c(2, 5, 3) as 3 tables, each with 2 rows and 5 columns:

x <- array(1:30, c(2, 5, 3))

x
#> , , 1
#>
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    1    3    5    7    9
#> [2,]    2    4    6    8   10
#>
#> , , 2
#>
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]   11   13   15   17   19
#> [2,]   12   14   16   18   20
#>
#> , , 3
#>
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]   21   23   25   27   29
#> [2,]   22   24   26   28   30

Rolling along the columns, axis = 2, moves elements within each row. There are 2 rows on each of 3 tables, so there are 6 separate rows that can each roll by their own n. A vector can't hold one n for each of them, but an array can. n is broadcast to c(2, 1, 3), which is one n for each row on each table:

n <- array(1:6, c(2, 1, 3))

n
#> , , 1
#>
#>      [,1]
#> [1,]    1
#> [2,]    2
#>
#> , , 2
#>
#>      [,1]
#> [1,]    3
#> [2,]    4
#>
#> , , 3
#>
#>      [,1]
#> [1,]    5
#> [2,]    6

rray_roll_each(x, n = n, axis = 2)
#> , , 1
#>
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    9    1    3    5    7
#> [2,]    8   10    2    4    6
#>
#> , , 2
#>
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]   15   17   19   11   13
#> [2,]   14   16   18   20   12
#>
#> , , 3
#>
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]   21   23   25   27   29
#> [2,]   30   22   24   26   28

Row 1 of table 1 moves 1, row 2 of table 1 moves 2, and so on. Row 1 of table 3 has an n of 5 on a dimension of 5, a full circle, so it doesn't move. Row 2 of table 3 has an n of 6, which circles around to 1.

Broadcasting allows you to provide a smaller n array when either all of the rows or all of the tables roll by the same amount:

# One `n` per row, the same on every table
rray_roll_each(x, n = array(c(1, 2), c(2, 1, 1)), axis = 2)

# One `n` per table, the same for both rows
rray_roll_each(x, n = array(c(0, 1, 2), c(1, 1, 3)), axis = 2)

Examples

# Positive `n` moves elements toward the end
rray_roll(1:5, n = 2, axes = 1)
#> [1] 4 5 1 2 3

# Negative `n` moves them toward the start
rray_roll(1:5, n = -1, axes = 1)
#> [1] 2 3 4 5 1

# Large `n` circles around
rray_roll(1:5, n = 7, axes = 1)
#> [1] 4 5 1 2 3

x <- matrix(1:6, nrow = 2, dimnames = list(c("r1", "r2"), c("a", "b", "c")))

# Roll the columns, names move with the data
rray_roll(x, n = 1, axes = 2)
#>    c a b
#> r1 5 1 3
#> r2 6 2 4

# Roll the rows
rray_roll(x, n = 1, axes = 1)
#>    a b c
#> r2 2 4 6
#> r1 1 3 5

# Roll both axes by the same `n`
rray_roll(x, n = 1, axes = c(1, 2))
#>    c a b
#> r2 6 2 4
#> r1 5 1 3

# Or by one `n` per axis
rray_roll(x, n = c(1, -1), axes = c(1, 2))
#>    b c a
#> r2 4 6 2
#> r1 3 5 1

y <- matrix(1:12, nrow = 3, byrow = TRUE)

# Roll each row by its own `n`, given as a one column matrix
rray_roll_each(y, n = matrix(c(1, 0, -1), ncol = 1), axis = 2)
#>      [,1] [,2] [,3] [,4]
#> [1,]    4    1    2    3
#> [2,]    5    6    7    8
#> [3,]   10   11   12    9

# Roll each row by its own `n`, by title
z <- rray_set_titles(y, c("row", "col"))
rray_roll_each(z, n = matrix(c(1, 0, -1), ncol = 1), axis = "col")
#>       col
#> row    [,1] [,2] [,3] [,4]
#>   [1,]    4    1    2    3
#>   [2,]    5    6    7    8
#>   [3,]   10   11   12    9

# Roll each column by its own `n`, given as a one row matrix
rray_roll_each(y, n = matrix(c(0, 1, 2, 3), nrow = 1), axis = 1)
#>      [,1] [,2] [,3] [,4]
#> [1,]    1   10    7    4
#> [2,]    5    2   11    8
#> [3,]    9    6    3   12

# Names on `axis` are dropped, since no single name fits each position
rray_roll_each(x, n = matrix(c(1, 2), ncol = 1), axis = 2)
#>    [,1] [,2] [,3]
#> r1    5    1    3
#> r2    4    6    2