vec_chop()
provides an efficient method to repeatedly slice a vector. It captures the pattern ofmap(indices, vec_slice, x = x)
. When no indices are supplied, it is generally equivalent toas.list()
.list_unchop()
combines a list of vectors into a single vector, placing elements in the output according to the locations specified byindices
. It is similar tovec_c()
, but gives greater control over how the elements are combined. When no indices are supplied, it is identical tovec_c()
, but typically a little faster.
If indices
selects every value in x
exactly once, in any order, then
list_unchop()
is the inverse of vec_chop()
and the following invariant
holds:
list_unchop(vec_chop(x, indices = indices), indices = indices) == x
Usage
vec_chop(x, ..., indices = NULL, sizes = NULL)
list_unchop(
x,
...,
indices = NULL,
ptype = NULL,
name_spec = NULL,
name_repair = c("minimal", "unique", "check_unique", "universal", "unique_quiet",
"universal_quiet"),
error_arg = "x",
error_call = current_env()
)
Arguments
- x
A vector
- ...
These dots are for future extensions and must be empty.
- indices
For
vec_chop()
, a list of positive integer vectors to slicex
with, orNULL
. Can't be used ifsizes
is already specified. If bothindices
andsizes
areNULL
,x
is split into its individual elements, equivalent to using anindices
ofas.list(vec_seq_along(x))
.For
list_unchop()
, a list of positive integer vectors specifying the locations to place elements ofx
in. Each element ofx
is recycled to the size of the corresponding index vector. The size ofindices
must match the size ofx
. IfNULL
,x
is combined in the order it is provided in, which is equivalent to usingvec_c()
.- sizes
An integer vector of non-negative sizes representing sequential indices to slice
x
with, orNULL
. Can't be used ifindices
is already specified.For example,
sizes = c(2, 4)
is equivalent toindices = list(1:2, 3:6)
, but is typically faster.sum(sizes)
must be equal tovec_size(x)
, i.e.sizes
must completely partitionx
, but an individual size is allowed to be0
.- ptype
If
NULL
, the default, the output type is determined by computing the common type across all elements ofx
. Alternatively, you can supplyptype
to give the output a known type.- name_spec
A name specification for combining inner and outer names. This is relevant for inputs passed with a name, when these inputs are themselves named, like
outer = c(inner = 1)
, or when they have length greater than 1:outer = 1:2
. By default, these cases trigger an error. You can resolve the error by providing a specification that describes how to combine the names or the indices of the inner vector with the name of the input. This specification can be:A function of two arguments. The outer name is passed as a string to the first argument, and the inner names or positions are passed as second argument.
An anonymous function as a purrr-style formula.
A glue specification of the form
"{outer}_{inner}"
.An
rlang::zap()
object, in which case both outer and inner names are ignored and the result is unnamed.
See the name specification topic.
- name_repair
How to repair names, see
repair
options invec_as_names()
.- error_arg
An argument name as a string. This argument will be mentioned in error messages as the input that is at the origin of a problem.
- error_call
The execution environment of a currently running function, e.g.
caller_env()
. The function will be mentioned in error messages as the source of the error. See thecall
argument ofabort()
for more information.
Value
vec_chop()
: A list where each element has the same type asx
. The size of the list is equal tovec_size(indices)
,vec_size(sizes)
, orvec_size(x)
depending on whether or notindices
orsizes
is provided.list_unchop()
: A vector of typevec_ptype_common(!!!x)
, orptype
, if specified. The size is computed asvec_size_common(!!!indices)
unless the indices areNULL
, in which case the size isvec_size_common(!!!x)
.
Examples
vec_chop(1:5)
#> [[1]]
#> [1] 1
#>
#> [[2]]
#> [1] 2
#>
#> [[3]]
#> [1] 3
#>
#> [[4]]
#> [1] 4
#>
#> [[5]]
#> [1] 5
#>
# These two are equivalent
vec_chop(1:5, indices = list(1:2, 3:5))
#> [[1]]
#> [1] 1 2
#>
#> [[2]]
#> [1] 3 4 5
#>
vec_chop(1:5, sizes = c(2, 3))
#> [[1]]
#> [1] 1 2
#>
#> [[2]]
#> [1] 3 4 5
#>
# Can also be used on data frames
vec_chop(mtcars, indices = list(1:3, 4:6))
#> [[1]]
#> mpg cyl disp hp drat wt qsec vs am gear carb
#> Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
#> Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
#> Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
#>
#> [[2]]
#> mpg cyl disp hp drat wt qsec vs am gear carb
#> Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
#> Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
#> Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
#>
# If `indices` selects every value in `x` exactly once,
# in any order, then `list_unchop()` inverts `vec_chop()`
x <- c("a", "b", "c", "d")
indices <- list(2, c(3, 1), 4)
vec_chop(x, indices = indices)
#> [[1]]
#> [1] "b"
#>
#> [[2]]
#> [1] "c" "a"
#>
#> [[3]]
#> [1] "d"
#>
list_unchop(vec_chop(x, indices = indices), indices = indices)
#> [1] "a" "b" "c" "d"
# When unchopping, size 1 elements of `x` are recycled
# to the size of the corresponding index
list_unchop(list(1, 2:3), indices = list(c(1, 3, 5), c(2, 4)))
#> [1] 1 2 1 3 1
# Names are retained, and outer names can be combined with inner
# names through the use of a `name_spec`
lst <- list(x = c(a = 1, b = 2), y = 1)
list_unchop(lst, indices = list(c(3, 2), c(1, 4)), name_spec = "{outer}_{inner}")
#> y_1 x_b x_a y_2
#> 1 2 1 1
# An alternative implementation of `ave()` can be constructed using
# `vec_chop()` and `list_unchop()` in combination with `vec_group_loc()`
ave2 <- function(.x, .by, .f, ...) {
indices <- vec_group_loc(.by)$loc
chopped <- vec_chop(.x, indices = indices)
out <- lapply(chopped, .f, ...)
list_unchop(out, indices = indices)
}
breaks <- warpbreaks$breaks
wool <- warpbreaks$wool
ave2(breaks, wool, mean)
#> [1] 31.03704 31.03704 31.03704 31.03704 31.03704 31.03704 31.03704
#> [8] 31.03704 31.03704 31.03704 31.03704 31.03704 31.03704 31.03704
#> [15] 31.03704 31.03704 31.03704 31.03704 31.03704 31.03704 31.03704
#> [22] 31.03704 31.03704 31.03704 31.03704 31.03704 31.03704 25.25926
#> [29] 25.25926 25.25926 25.25926 25.25926 25.25926 25.25926 25.25926
#> [36] 25.25926 25.25926 25.25926 25.25926 25.25926 25.25926 25.25926
#> [43] 25.25926 25.25926 25.25926 25.25926 25.25926 25.25926 25.25926
#> [50] 25.25926 25.25926 25.25926 25.25926 25.25926
identical(
ave2(breaks, wool, mean),
ave(breaks, wool, FUN = mean)
)
#> [1] TRUE
# If you know your input is sorted and you'd like to split on the groups,
# `vec_run_sizes()` can be efficiently combined with `sizes`
df <- data_frame(
g = c(2, 5, 5, 6, 6, 6, 6, 8, 9, 9),
x = 1:10
)
#> Warning: `data_frame()` was deprecated in tibble 1.1.0.
#> ℹ Please use `tibble()` instead.
vec_chop(df, sizes = vec_run_sizes(df$g))
#> [[1]]
#> # A tibble: 1 × 2
#> g x
#> <dbl> <int>
#> 1 2 1
#>
#> [[2]]
#> # A tibble: 2 × 2
#> g x
#> <dbl> <int>
#> 1 5 2
#> 2 5 3
#>
#> [[3]]
#> # A tibble: 4 × 2
#> g x
#> <dbl> <int>
#> 1 6 4
#> 2 6 5
#> 3 6 6
#> 4 6 7
#>
#> [[4]]
#> # A tibble: 1 × 2
#> g x
#> <dbl> <int>
#> 1 8 8
#>
#> [[5]]
#> # A tibble: 2 × 2
#> g x
#> <dbl> <int>
#> 1 9 9
#> 2 9 10
#>
# If you have a list of homogeneous vectors, sometimes it can be useful to
# unchop, apply a function to the flattened vector, and then rechop according
# to the original indices. This can be done efficiently with `list_sizes()`.
x <- list(c(1, 2, 1), c(3, 1), 5, double())
x_flat <- list_unchop(x)
x_flat <- x_flat + max(x_flat)
vec_chop(x_flat, sizes = list_sizes(x))
#> [[1]]
#> [1] 6 7 6
#>
#> [[2]]
#> [1] 8 6
#>
#> [[3]]
#> [1] 10
#>
#> [[4]]
#> numeric(0)
#>