-
Notifications
You must be signed in to change notification settings - Fork 66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Basic map implementation #495
Comments
My implementation isn't quite right because |
Size 1 constraint too? library(vctrs)
vec_map <- function(.x, .f, ..., .ptype = NULL) {
.x <- lapply(.x, .f, ...)
vec_c(!!!.x, .ptype = .ptype)
}
vec_map(list(1, 1:2), function(x) {x})
#> [1] 1 1 2 Created on 2019-07-22 by the reprex package (v0.2.1) |
Oh yeah, good point. |
Related / my attempt: tidyverse/purrr#683 |
Shouldn't we expect |
I would expect the |
i.e. |
Since there is no hurry for replacing compat-purrr.R, getting the semantics vctrs-y seems more important. Unlike |
Also purrr is now about as lightweight as vctrs in terms of dependencies, so purrr itself can replace compat-purrr.R |
@lionel- and i talked about this a bit more, based on our notes I have come up with this for library(vctrs)
vec_map <- function(.x, .f, ..., .ptype = list()) {
if (is.null(.ptype)) {
.ptype <- list()
simplify <- TRUE
} else {
simplify <- FALSE
}
.f <- as_function(.f)
out <- vec_map_impl(.x, .f, ..., .ptype = .ptype)
if (simplify) {
out <- vec_c(!!!out)
}
out
}
vec_map_impl <- function(.x, .f, ..., .ptype) {
out <- vec_init(.ptype, vec_size(.x))
for (i in seq2(1, vec_size(.x))) {
elt <- .f(vec_slice(.x, i), ...)
elt <- vec_cast(elt, .ptype)
out <- vec_assign(out, i, elt)
}
out
} # each element is castable to a length 1 list
vec_map(1:3, ~.x)
#> [[1]]
#> [1] 1
#>
#> [[2]]
#> [1] 2
#>
#> [[3]]
#> [1] 3
# cannot do this, unlike map(). c(.x, 1) is cast to a length 2 list, which
# cannot be assigned to a single location in the output
vec_map(1:3, ~c(.x, 1))
#> Error: Incompatible lengths: 2, 1
# must wrap in list for it to work
vec_map(1:3, ~list(c(.x, 1)))
#> [[1]]
#> [1] 1 1
#>
#> [[2]]
#> [1] 2 1
#>
#> [[3]]
#> [1] 3 1
vec_map(1:3, ~.x, .ptype = double())
#> [1] 1 2 3
vec_map(1:3, ~.x, .ptype = NULL)
#> [1] 1 2 3
vec_map(1:3, ~if (.x == 1) {"ugh"} else {1}, .ptype = NULL)
#> No common type for `..1` <character> and `..2` <double>. |
That assumes that |
The simplify step seems suspect to me. Could you init a |
Yea i think the simplify part is wrong. cars <- mtcars[1:3,]
# this is wrong
vec_map(1:3, ~list(cars), .ptype = NULL)
#> mpg cyl disp hp drat wt qsec vs am gear carb
#> 1 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
#> 2 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
#> 3 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
#> 4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
#> 5 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
#> 6 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
#> 7 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
#> 8 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
#> 9 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
# ^ should return what this does
vec_map(1:3, ~list(list(cars)), .ptype = NULL)
#> [[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
#> 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
#>
#> [[3]]
#> 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 |
(This is not related to the performance of the two methods mentioned in the original comment, but this seems relevant to the conversation anyways) A few more notes that came out of some slack conversation that might be useful to have documented:
This makes it useful as a building block for flat-map techniques, with
This results in the following difference between library(purrr)
library(vctrs)
library(rlang, warn.conflicts = FALSE)
vec_map <- function(.x, .f, ..., .ptype = list()) {
.f <- as_function(.f)
out <- lapply(.x, .f, ...)
if (vec_size_common(!!! out) != 1L) {
abort("All results must have size 1.")
}
vec_c(!!! out, .ptype = .ptype)
}
# maintain type/size of `.f` result, no matter what that may be
map(1:2, ~c(.x, 1))
#> [[1]]
#> [1] 1 1
#>
#> [[2]]
#> [1] 2 1
# force result of `.f` to be a list of size 1
vec_map(1:2, ~c(.x, 1))
#> All results must have size 1.
# fails because of how this works (which is expected and good).
# result of `.f` is cast to list, but becomes length 2
vec_cast(c(2, 1), list())
#> [[1]]
#> [1] 2
#>
#> [[2]]
#> [1] 1
# must wrap in a list of size 1
vec_map(1:2, ~list(c(.x, 1)))
#> [[1]]
#> [1] 1 1
#>
#> [[2]]
#> [1] 2 1 I think this means:
|
Closing in favour of tidyverse/purrr#894 |
Need to benchmark the two strategies: in the first we just put in a list and then
vec_c()
at the end; in the second we assume that the type is mostly stable so we can create the space for output in the beginning, and only occasionally change.The text was updated successfully, but these errors were encountered: