Skip to content

Export list_transpose()? #2055

@DavisVaughan

Description

@DavisVaughan

It has different invariants than the purrr one, but it seems very well grounded in vctrs theory

https://github.com/DavisVaughan/cross/blob/95a27b17bfb123c8082c54eb972f703a8feccb4a/R/run.R#L249

  • Can probably get away with early recycling/casting if we give vec_interleave() a call argument.
  • Might not even really need a size argument? "All list elements must be size 1 or the same size" seems good enough? i.e. the same reason vec_interleave() doesn't have one.
# Transpose a list of vectors
#
# Invariants:
#
# - `x` must be a list
# - Names of `x` are used in error messages, but are then discarded
# - Each element of `x` is coerced to the common type of the elements, or
#   `ptype`
# - Each element of `x` is recycled to the common size of the elements, or
#   `size`
# - Output is a list of size `size`. Each element is a vector of size `x_size`
#   and type `ptype`.
list_transpose <- function(
  x,
  ...,
  ptype = NULL,
  size = NULL,
  x_arg = caller_arg(x),
  error_call = caller_env()
) {
  obj_check_list(x, arg = x_arg, call = error_call)

  x_size <- vec_size(x)

  ptype <- vec_ptype_common(
    !!!x,
    .ptype = ptype,
    .arg = x_arg,
    .call = error_call
  )
  size <- vec_size_common(
    !!!x,
    .size = size,
    .arg = x_arg,
    .call = error_call
  )

  x <- vec_cast_common(
    !!!x,
    .to = ptype,
    .arg = x_arg,
    .call = error_call
  )
  x <- vec_recycle_common(
    !!!x,
    .size = size,
    .arg = x_arg,
    .call = error_call
  )

  # Don't want outer names after doing common type / size determination,
  # could use `.name_spec = "inner"` with new vctrs in `vec_interleave()`
  x <- unname(x)

  # Combine pieces of size `size` into one big vector via interleaving
  x <- vec_interleave(!!!x, .ptype = ptype)

  # Chop the one big vector into transposed pieces of size `x_size`
  x <- vec_chop(x, sizes = vec_rep(x_size, times = size))

  x
}

Ideal implementation is probably more like this. Need an internal list_interleave().

# TODO: Handle `NULL` list elements well? Is that even well defined?
# TODO: Handle empty `list()`. `list_interleave()` returns `NULL` but would
# be nice if this returned `unspecified()` for further use in `vec_chop()`.
# Need to handle divide by 0 size issue too.

list_transpose <- function(
  x,
  ...,
  ptype = NULL,
  x_arg = caller_arg(x),
  error_call = caller_env()
) {
  # - Does `obj_check_list()`
  # - Does `list_check_all_vectors()`
  # - Takes common size, recycles size 1 inputs
  out <- list_interleave(
    x,
    .ptype = ptype,
    .name_spec = "inner",
    .x_arg = x_arg,
    .error_call = error_call
  )

  x_size <- vec_size(x)
  out_size <- vec_size(out)
  elt_size <- out_size / x_size

  # Chop the one big vector into transposed pieces of size `x_size`
  out <- vec_chop(out, sizes = vec_rep(x_size, times = elt_size))

  out
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions