Skip to content

Commit

Permalink
Rename zip_n to transpose.
Browse files Browse the repository at this point in the history
Deprecate zip2() and zip3(). Fixes #128.
  • Loading branch information
hadley committed Nov 12, 2015
1 parent a74bfbf commit e66b605
Show file tree
Hide file tree
Showing 17 changed files with 109 additions and 90 deletions.
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export(sort_by)
export(splice)
export(split_by)
export(tail_while)
export(transpose)
export(unslice)
export(update_list)
export(walk)
Expand Down
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@
* The map function now use custom C code, rather than relying on `lapply()`,
`mapply()` etc. The performance characteristcs are very similar, but it
allows us greater control over the output (#118).

* `zip2()`, `zip3()`, and `zip_n()` have been replaced by `transpose()`.
It does the same thing but the name is better (#128).
6 changes: 3 additions & 3 deletions R/cross.R
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@
#' out[[i]] <- map(args, i) %>% invoke(paste, .)
#' out
#'
#' # It's easier to zip and then use invoke_map()
#' args %>% zip_n() %>% map_chr(~ invoke(paste, .))
#' # It's easier to transpose and then use invoke_map()
#' args %>% transpose() %>% map_chr(~ invoke(paste, .))
#'
#' # Unwanted combinations can be filtered out with a predicate function
#' filter <- function(x, y) x >= y
Expand Down Expand Up @@ -145,6 +145,6 @@ cross3 <- function(.x, .y, .z, .filter = NULL) {
#' @export
cross_d <- function(.l, .filter = NULL) {
cross_n(.l, .filter = .filter) %>%
zip_n(.simplify = TRUE) %>%
transpose(.simplify = TRUE) %>%
dplyr::as_data_frame()
}
6 changes: 3 additions & 3 deletions R/map.R
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ walk3 <- function(.x, .y, .z, .f, ...) {
#' @rdname map2
walk_n <- function(.l, .f, ...) {
.f <- as_function(.f)
args_list <- recycle_args(.l) %>% zip_n()
args_list <- recycle_args(.l) %>% transpose()
for (args in args_list) {
do.call(".f", c(args, list(...)))
}
Expand All @@ -244,9 +244,9 @@ walk_n <- function(.l, .f, ...) {
#' @name conditional-map
#' @examples
#' list(x = rbernoulli(100), y = 1:100) %>%
#' zip_n() %>%
#' transpose() %>%
#' map_if("x", ~ update_list(., y = ~ y * 100)) %>%
#' zip_n(.simplify = TRUE)
#' transpose(.simplify = TRUE)
#'
#' # Convert factors to characters
#' iris %>%
Expand Down
2 changes: 1 addition & 1 deletion R/negate.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#' @return A new predicate function.
#' @export
#' @examples
#' x <- zip_n(list(x = 1:10, y = rbernoulli(10)))
#' x <- transpose(list(x = 1:10, y = rbernoulli(10)))
#' x %>% keep("y") %>% length()
#' x %>% keep(negate("y")) %>% length()
#' # Same as
Expand Down
6 changes: 3 additions & 3 deletions R/output.R
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@
#'
#' list("a", 10, 100) %>%
#' map(safe_log) %>%
#' zip_n()
#' transpose()
#'
#' # This is a bit easier to work with if you supply a default value
#' # of the same type and use the simplify argument to zip_n():
#' # of the same type and use the simplify argument to transpose():
#' safe_log <- safe(log, otherwise = NA_real_)
#' list("a", 10, 100) %>%
#' map(safe_log) %>%
#' zip_n(.simplify = TRUE)
#' transpose(.simplify = TRUE)
#'
#' # To replace errors with a default value, use maybe().
#' list("a", 10, 100) %>%
Expand Down
6 changes: 3 additions & 3 deletions R/split_by.R
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#' @inheritParams map
#' @export
#' @examples
#' l1 <- zip_n(list(x = sample(10), y = 1:10))
#' l1 <- transpose(list(x = sample(10), y = 1:10))
#' l1
#' l1 %>% order_by("x")
#' l1 %>% sort_by("x")
Expand All @@ -13,14 +13,14 @@
#' l2 %>% split_by("g") %>% map(. %>% map("y"))
split_by <- function(.x, .f, ...) {
vals <- map(.x, .f, ...)
split(.x, zip_n(vals, .simplify = TRUE))
split(.x, transpose(vals, .simplify = TRUE))
}

#' @export
#' @rdname split_by
order_by <- function(.x, .f, ...) {
vals <- map(.x, .f, ...)
do.call("order", zip_n(vals, .simplify = TRUE))
do.call("order", transpose(vals, .simplify = TRUE))
}

#' @export
Expand Down
57 changes: 38 additions & 19 deletions R/zip.R → R/transpose.R
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
#' Transpose a list.
#'
#' Zip turns a list-of-lists "inside-out"; it turns a pair of lists into a
#' Tranpose turns a list-of-lists "inside-out"; it turns a pair of lists into a
#' list of pairs, or a list of pairs into pair of lists. For example,
#' If you had a list of length n where each component had values \code{a} and
#' \code{b}, \code{zip_n()} would make a list with elements \code{a} and
#' \code{b} that contained lists of length n.
#' \code{b}, \code{transpose()} would make a list with elements \code{a} and
#' \code{b} that contained lists of length n. It's called transpose because
#' \code{x[[1]][[2]]} is equivalent to \code{transpose(x)[[2]][[1]]}.
#'
#' Note that \code{zip_n()} is its own inverse, much like the
#' Note that \code{transpose()} is its own inverse, much like the
#' transpose operation on a matrix. You can get back the original
#' input by zipping it twice. \code{zip2(x, y)} is a shortcut for
#' \code{zip_n(list(x, y))}.
#' input by zipping it twice.
#'
#' @param .x,.y,.z Lists or vectors to zip.
#' @param .l A list of lists or vectors to zip.
#' @param .l A list of vectors to zip.
#' @param .fields Fields to use when unzipping - defaults to the names
#' of the first sub-list.
#' @param .simplify If \code{TRUE}, lists containing atomic scalars of
Expand All @@ -21,21 +20,21 @@
#' @examples
#' x <- rerun(5, x = runif(1), y = runif(5))
#' x %>% str()
#' x %>% zip_n() %>% str()
#' x %>% transpose() %>% str()
#' # Back to where we started
#' x %>% zip_n() %>% zip_n() %>% str()
#' x %>% transpose() %>% transpose() %>% str()
#'
#' # zip() is useful in conjunction with safe()
#' # transpose() is useful in conjunction with safe()
#' x <- list("a", 1, 2)
#' y <- x %>% map(safe(log))
#' y %>% str()
#' y %>% zip_n() %>% str()
#' y %>% transpose() %>% str()
#'
#' # The simplify argument reduces list to atomic vectors where possible
#' x <- list(a = 1:5, b = 5:1)
#' x %>% zip_n()
#' x %>% zip_n(.simplify = TRUE)
zip_n <- function(.l, .fields = NULL, .simplify = FALSE) {
#' x %>% transpose()
#' x %>% transpose(.simplify = TRUE)
transpose <- function(.l, .fields = NULL, .simplify = FALSE) {
if (length(.l) == 0) return(list())

if (is.null(.fields)) {
Expand All @@ -55,14 +54,34 @@ zip_n <- function(.l, .fields = NULL, .simplify = FALSE) {
out
}

#' @rdname zip_n
#' @rdname transpose
#' @export
#' @usage NULL
zip_n <- function(...) {
warning("`zip_n()` is deprecated; please use `transpose()` instead.",
call. = FALSE)
transpose(...)
}

#' @rdname transpose
#' @export
#' @usage NULL
zip2 <- function(.x, .y, .fields = NULL, .simplify = FALSE) {
zip_n(list(.x, .y), .fields = .fields, .simplify = .simplify)
warning(
"`zip2(x, y)` is deprecated, please use `transpose(list(x, y))` instead.",
call. = FALSE
)
transpose(list(.x, .y), .fields = .fields, .simplify = .simplify)
}

#' @rdname zip_n
#' @rdname transpose
#' @export
#' @usage NULL
zip3 <- function(.x, .y, .z, .fields = NULL, .simplify = FALSE) {
zip_n(list(.x, .y, .z), .fields = .fields, .simplify = .simplify)
warning(
"`zip2(x, y, z)` is deprecated, please use `transpose(list(x, y, z))` instead.",
call. = FALSE
)

transpose(list(.x, .y, .z), .fields = .fields, .simplify = .simplify)
}
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ random_group <- function(n, probs) {
}
partition <- function(df, n, probs) {
replicate(n, split(df, random_group(nrow(df), probs)), FALSE) %>%
zip_n() %>%
transpose() %>%
as_data_frame()
}

Expand Down Expand Up @@ -103,7 +103,7 @@ mean(unlist(boot$diffs))

### List manipulation and creation

* Zip together two or more lists with `zip_n()`.
* Transpose a list with `transpose()`.

* Create the cartesian product of the elements of several lists with
`cross_n()` and `cross_d()`.
Expand Down Expand Up @@ -163,10 +163,11 @@ The goal is not to try and simulate Haskell in R: purrr does not implement curry
For chains of transformations functions, `. %>% f() %>% g()` is
equivalent to `function(.) . %>% f() %>% g()`.

* R is weakly typed, so we can implement general `zip_n()`, rather than having
to specialise on the number of arguments. (That said I still provide `map2()`
and `map3()` since it's useful to clearly separate which arguments are
vectorised over).
* R is weakly typed, so we can implement more general functions, rather than
having to specialise on the number of arguments. (That said I still provide
`map2()` and `map3()` since it's useful to clearly separate which arguments
are vectorised over). The downside is that we need variants `map_int()`,
`map_dbl()`, etc since we don't know what `.f` will return.

* R has named arguments, so instead of providing different functions for
minor variations (e.g. `detect()` and `detectLast()`) I use a named
Expand Down
4 changes: 2 additions & 2 deletions man/conditional-map.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions man/cross_n.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/negate.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions man/safe.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion man/split_by.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 17 additions & 22 deletions man/zip_n.Rd → man/transpose.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions tests/testthat/test-transpose.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
context("transpose")

test_that("scalars simplified to vector", {
expect_equal(transpose(list(list(1), list(2)), .simplify = TRUE), list(1:2))
expect_equal(transpose(list(list(1L), list(2)), .simplify = TRUE), list(1:2))
})

test_that("names preserved", {
expect_equal(transpose(list(list(x = 1), list(x = 2)), .simplify = TRUE), list(x = 1:2))
})

test_that("uses names if present", {
expect_equal(transpose(list(list(x = 1), list(y = 2))), list(x = list(1, NULL)))
})

test_that("uses names if present", {
expect_equal(
transpose(list(list(x = 1), list(y = 2)), .fields = c("x", "y")),
list(x = list(1, NULL), y = list(NULL, 2))
)
})
Loading

0 comments on commit e66b605

Please sign in to comment.