max
max

Reputation: 4521

Map over columns in tidy format using tidyverse

I often use a pattern as seen below, where I store data in a tibble using list-columns, apply functions to the data using purrr::map, and then use pivot_longer to convert to tidy format (long).

Is there a cleaner / more idiomatic way to do this in one step, without having to pivot the data each time?

library(tidyverse)

df <- tibble(n = 5:10)

df$data <- map(df$n, ~rnorm(.x))
df$mean <- map_dbl(df$data, ~mean(.x))
df$median <- map_dbl(df$data, ~median(.x))
# A tibble: 6 x 4
      n data          mean  median
  <int> <list>       <dbl>   <dbl>
1     5 <dbl [5]>  -0.0239 -0.324 
2     6 <dbl [6]>  -0.396   0.0153
3     7 <dbl [7]>   0.506   0.711 
4     8 <dbl [8]>   0.463   0.537 
5     9 <dbl [9]>  -0.248  -0.555 
6    10 <dbl [10]> -0.153  -0.293 
df <- pivot_longer(df, mean:median) 
# A tibble: 12 x 4
       n data       name     value
   <int> <list>     <chr>    <dbl>
 1     5 <dbl [5]>  mean   -0.386 
 2     5 <dbl [5]>  median -0.407 
 3     6 <dbl [6]>  mean   -0.190 
 4     6 <dbl [6]>  median -0.451 
 5     7 <dbl [7]>  mean   -0.456 
 6     7 <dbl [7]>  median -0.0801
 7     8 <dbl [8]>  mean   -0.0408
 8     8 <dbl [8]>  median  0.0577
 9     9 <dbl [9]>  mean    0.273 
10     9 <dbl [9]>  median  0.410 
11    10 <dbl [10]> mean   -0.720 
12    10 <dbl [10]> median -1.01 

Upvotes: 5

Views: 826

Answers (1)

Ronak Shah
Ronak Shah

Reputation: 388807

I think you already have a good approach, I would have used the same by chaining all the function in one pipe (%>%).

If you want to avoid pivot_longer step you can group by each row and create two new rows for each one. This is possible for dplyr 1.0.0 or higher.

library(tidyverse)

df %>%
  mutate(data = map(n, rnorm), 
         group = row_number()) %>%
  group_by(group) %>%
  summarise(n = n,
            data = data,
            value = {tmp <- unlist(data);c(median(tmp), mean(tmp))},
            name = c('median', 'mean')) %>%
  ungroup %>%
  select(-group)

#      n   data        value   name  
#   <int> <list>        <dbl> <chr> 
# 1     5 <dbl [5]>   0.571   median
# 2     5 <dbl [5]>   0.343   mean  
# 3     6 <dbl [6]>   0.220   median
# 4     6 <dbl [6]>   0.0419  mean  
# 5     7 <dbl [7]>  -0.193   median
# 6     7 <dbl [7]>  -0.132   mean  
# 7     8 <dbl [8]>  -0.171   median
# 8     8 <dbl [8]>   0.00583 mean  
# 9     9 <dbl [9]>   0.952   median
#10     9 <dbl [9]>   0.471   mean  
#11    10 <dbl [10]>  0.684   median
#12    10 <dbl [10]>  0.250   mean  

Upvotes: 5

Related Questions