How to create a one-row data frame from a vector in R?

Viewed 148

What is the most concise and/or elegant way to turn a vector into a single-row data frame in R? I'm trying to use a function that returns a 5-element vector and apply it across groups in my data frame, like so:

library(tidyverse)
mtcars %>% 
  group_by(cyl) %>% 
  group_map(~boxplot.stats(.$wt)$stats)

I'd like to have these results as rows in a new data frame, rather than a list of vectors, so I can plot with them in ggplot. But the best I've come up with is to add do.call(rbind, .) %>% as.data.frame to the end of my pipeline, which seems ungainly and inelegant. Replacing the group_map with a group_modify seems like the answer, but the function complains that my results aren't a data frame. And wrapping the anonymous function in a call to tibble_row doesn't work because tibble_row won't take vectors, just individual elements or explicit list columns.

Ideally, output would look like:

  cyl    V1     V2    V3     V4   V5
1   4 1.513 1.8850 2.200 2.6225 3.19
2   6 2.620 2.8225 3.215 3.4400 3.46
3   8 3.170 3.5200 3.755 4.0700 4.07

Surely there's a more elegant way to do what I'm trying to do, ideally within the tidyverse framework?

2 Answers

We could use unnest_wider after returning the output in a list in summarise

library(dplyr)
library(tidyr)
mtcars %>%
    group_by(cyl) %>%
    summarise(out = list(boxplot.stats(wt)$stats)) %>% 
    unnest_wider(out) %>% 
    rename_at(-1, ~ str_replace(., '\\.+', 'x'))
# A tibble: 3 x 6
#    cyl    x1    x2    x3    x4    x5
#   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#1     4  1.51  1.88  2.2   2.62  3.19
#2     6  2.62  2.82  3.22  3.44  3.46
#3     8  3.17  3.52  3.76  4.07  4.07

Or if we want to use the OP's method, then set the names for that vector and use as_tibble_row

library(purrr)
library(stringr)
mtcars %>% 
  group_by(cyl) %>% 
  group_map(~ tibble(cyl = first(.x$cyl), 
          setNames(boxplot.stats(.$wt)$stats, str_c('x', 1:5)) %>% 
             as_tibble_row) , .keep = TRUE) %>%
  bind_rows
# A tibble: 3 x 6
#    cyl    x1    x2    x3    x4    x5
#  <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#1     4  1.51  1.88  2.2   2.62  3.19
#2     6  2.62  2.82  3.22  3.44  3.46
#3     8  3.17  3.52  3.76  4.07  4.07

As the output of group_map is always a list, it may be better to use group_modify to return a tbl thus avoiding the last map_dfr/bind_rows

mtcars %>% 
  group_by(cyl) %>% 
  group_modify(~ setNames(boxplot.stats(.$wt)$stats, str_c('x', 1:5)) %>%
         as_tibble_row , .keep = TRUE) %>% 
  ungroup
# A tibble: 3 x 6
#    cyl    x1    x2    x3    x4    x5
#  <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#1     4  1.51  1.88  2.2   2.62  3.19
#2     6  2.62  2.82  3.22  3.44  3.46
#3     8  3.17  3.52  3.76  4.07  4.07

Base R method with aggregate :

tmp <- aggregate(wt~cyl, mtcars, function(x) boxplot.stats(x)$stats)
tmp

#  cyl   wt.1   wt.2   wt.3   wt.4   wt.5
#1   4 1.5130 1.8850 2.2000 2.6225 3.1900
#2   6 2.6200 2.8225 3.2150 3.4400 3.4600
#3   8 3.1700 3.5200 3.7550 4.0700 4.0700

The wt.1, wt.2 etc columns are part of matrix, to get the output as individual columns you can do

data.frame(cyl = tmp$cyl, tmp$wt)
Related