Reputation: 325
I suspect that this is a bug in the purrr package, but would like to check my logic in StackOverflow first, please.
It seems to me that the possibly
function is not working inside the map2_chr
function. I'm using purrr version 0.2.5
Consider this example:
library(dplyr)
library(purrr)
lets <- tibble(posn = 2:0,
lets_list = list(letters[1:5], letters[1:5], letters[1:5])) %>%
glimpse()
returns
Observations: 3
Variables: 2
$ posn <int> 2, 1, 0
$ lets_list <list> [<"a", "b", "c", "d", "e">, <"a", "b", "c", "d", "e">, <"a", "b", "c", "d", "e">]
In this example, I want to create another column using mutate to return the element in the list "lets_list" based on the value in "posn".
lets %>%
mutate(lets_sel = map2_chr(lets_list, posn, ~.x[.y]))
fails with this error message as the third row have posn = 0.
> lets %>%
+ mutate(lets_sel = map2_chr(lets_list, posn, ~.x[.y]))
# Error in mutate_impl(.data, dots) :
# Evaluation error: Result 3 is not a length 1 atomic vector.
Using the possibly
function with map2_chr
returns an error too.
lets %>%
mutate(lets_sel = map2_chr(lets_list, posn, possibly(~.x[.y], NA_character_)))
# Error in mutate_impl(.data, dots) :
# Evaluation error: Result 3 is not a length 1 atomic vector.
However, the map2
function works fine:
> lets %>%
+ mutate(lets_sel = map2(lets_list, posn, possibly(~.x[.y], NA_character_)))
# A tibble: 3 x 3
posn lets_list lets_sel
<int> <list> <list>
1 2 <chr [5]> <chr [1]>
2 1 <chr [5]> <chr [1]>
3 0 <chr [5]> <chr [0]>
A workaround solution is to use map2 and then map_chr, but I suspect that this is a bug.
> lets %>%
+ mutate(lets_sel = map2(lets_list, posn, ~.x[.y]),
+ lets_sel = map_chr(lets_sel, possibly(~.x[1], NA_character_)))
# A tibble: 3 x 3
posn lets_list lets_sel
<int> <list> <chr>
1 2 <chr [5]> b
2 1 <chr [5]> a
3 0 <chr [5]> NA
Am I missing something here? Thanks.
Upvotes: 2
Views: 1541
Reputation: 11908
possibly()
doesn't work because indexing with 0
doesn't throw an error;
it just returns a length 0 vector:
nth_letter <- function(n) letters[n]
possibly(nth_letter, "not returned")(0)
#> character(0)
nth_letter(0)
#> character(0)
In this case it would probably be easier to replace invalid indices with NA
(using e.g. dplyr::na_if()
, or plain old ifelse
if the real problem is more complex) to get what you are after:
lets %>%
mutate(lets_sel = map2_chr(lets_list, na_if(posn, 0), ~ .x[.y]))
#> # A tibble: 3 x 3
#> posn lets_list lets_sel
#> <int> <list> <chr>
#> 1 2 <chr [5]> b
#> 2 1 <chr [5]> a
#> 3 0 <chr [5]> <NA>
Created on 2018-08-07 by the reprex package (v0.2.0.9000).
Upvotes: 0
Reputation: 325
OK, now I'm thinking that this is just a "feature". The most elegant solution / workaround is just:
lets %>%
mutate(lets_sel = map2(lets_list, posn, ~.x[.y]) %>%
map_chr(., possibly(~.x[1], NA_character_)))
Nowhere in the help screen suggests that safely and possibly can by used with the map2
family of functions. Hence I conclude that this is a "feature" rather than a "bug".
Thanks.
Upvotes: 1