`purrr::map` to any type
Asked Answered
I

4

13

Is there a way to map to any type with purrr::map

library(tidyverse)
library(lubridate)

df <- data_frame(id = c(1, 1, 1, 2, 2, 2), 
                 val = c(1, 2, 3, 1, 2, 3), 
                 date = ymd("2017-01-01") + days(1:6))

df1 <- df %>% nest(-id) %>% 
  mutate(first_val = map_dbl(data, ~ .$val[1]), 
         first_day = map(data, ~ .$date[1]))

I would like first_day to be a column of type <date> as in df. I have tried flatten, but this does not work as it coerces the column to numeric.

Imperil answered 8/4, 2017 at 9:25 Comment(0)
D
6

With purrr 1.0.0, you can use map_vec.

map_vec() (along with map2_vec(), and pmap_vec()) handles more types of vectors. map_vec() extends map_lgl(), map_int(), map_dbl(), and map_chr() to arbitrary types of vectors, like dates, factors, and date-times:

df %>% nest(data = -id) %>% 
  mutate(first_val = map_dbl(data, ~ .$val[1]), 
         first_day = map_vec(data, ~ .$date[1]))

output

# A tibble: 2 × 4
     id data             first_val first_day 
  <dbl> <list>               <dbl> <date>    
1     1 <tibble [3 × 2]>         1 2017-01-02
2     2 <tibble [3 × 2]>         1 2017-01-05

map_vec will always return a simpler vector with the correct vector class (erroring if there is no common type), but you can also specify it with .ptype:

df %>% nest(data = -id) %>% 
  mutate(first_val = map_vec(data, ~ .$val[1], .ptype = integer()), 
         first_day = map_vec(data, ~ .$date[1], .ptype = Date()))
Dolichocephalic answered 22/12, 2022 at 9:52 Comment(0)
D
12

purrr is type-stable and this takes some getting used to.

In this case, it returns a list where you expect a <date>.

A simple and "stable" solution to you case would be to replace the second map with a map_dbl and have the output turned back to a <date> object using lubridate's as_date, like this:

df3 <- df %>% nest(-id) %>% 
   mutate(first_val = map_dbl(data, ~ .$val[1]), 
          first_day = as_date(map_dbl(data, ~ .$date[1])))

You get:

# A tibble: 2 × 4
  id             data                 first_val  first_day
 <dbl>          <list>                  <dbl>     <date>
 1              <tibble [3 × 2]>         1      2017-01-02
 2              <tibble [3 × 2]>         1      2017-01-05

Which is what you wanted (for this example).

EDIT: for any other types (other than <date>) you would have to find a different solution, however, the standard types are covered by the dedicated map_lgl, map_dbl, map_chr, etc.

Deter answered 8/4, 2017 at 14:37 Comment(0)
C
6

An alternative to the map_dbl() %>% as_date() is to use unnest() on the output column of interest:

library(tidyverse)
library(lubridate)
#> 
#> Attaching package: 'lubridate'
#> The following object is masked from 'package:base':
#> 
#>     date

df <- data_frame(id = c(1, 1, 1, 2, 2, 2), 
                 val = c(1, 2, 3, 1, 2, 3), 
                 date = ymd("2017-01-01") + days(1:6))

df %>% nest(-id) %>% 
  mutate(first_val = map_dbl(data, ~ .$val[1]), 
         first_day = map(data, ~ .$date[1])) %>% 
  unnest(first_day)
#> # A tibble: 2 x 4
#>      id data             first_val first_day 
#>   <dbl> <list>               <dbl> <date>    
#> 1     1 <tibble [3 × 2]>         1 2017-01-02
#> 2     2 <tibble [3 × 2]>         1 2017-01-05

Created on 2018-11-17 by the reprex package (v0.2.1)

Countrywoman answered 17/11, 2018 at 20:40 Comment(0)
D
6

With purrr 1.0.0, you can use map_vec.

map_vec() (along with map2_vec(), and pmap_vec()) handles more types of vectors. map_vec() extends map_lgl(), map_int(), map_dbl(), and map_chr() to arbitrary types of vectors, like dates, factors, and date-times:

df %>% nest(data = -id) %>% 
  mutate(first_val = map_dbl(data, ~ .$val[1]), 
         first_day = map_vec(data, ~ .$date[1]))

output

# A tibble: 2 × 4
     id data             first_val first_day 
  <dbl> <list>               <dbl> <date>    
1     1 <tibble [3 × 2]>         1 2017-01-02
2     2 <tibble [3 × 2]>         1 2017-01-05

map_vec will always return a simpler vector with the correct vector class (erroring if there is no common type), but you can also specify it with .ptype:

df %>% nest(data = -id) %>% 
  mutate(first_val = map_vec(data, ~ .$val[1], .ptype = integer()), 
         first_day = map_vec(data, ~ .$date[1], .ptype = Date()))
Dolichocephalic answered 22/12, 2022 at 9:52 Comment(0)
R
1

You could rely on purrr's reduce() with c():

library(tidyverse)
library(lubridate)

df <- tibble(id = c(1, 1, 1, 2, 2, 2), 
             val = c(1, 2, 3, 1, 2, 3), 
             date = ymd("2017-01-01") + days(1:6))

df1 <- df %>% nest(data = -id) %>% 
    mutate(first_val = map_dbl(data, ~ .$val[1]), 
           first_day = reduce(map(data, ~ .$date[1]), c))

Result:

> df1
# A tibble: 2 × 4
     id data             first_val first_day 
  <dbl> <list>               <dbl> <date>    
1     1 <tibble [3 × 2]>         1 2017-01-02
2     2 <tibble [3 × 2]>         1 2017-01-05
Roan answered 24/3, 2022 at 18:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.