Make column of input items with purrr::map_df using .id without duplicating inputs for named vector

青春壹個敷衍的年華 提交于 2021-01-28 18:48:26

问题


I often want to map over a vector of column names in a data frame, and keep track of the output using the .id argument. But to write the column names related to each map iteration into that .id column seems to require doubling up their name in the input vector - in other words, by naming each column name with its own name. If I don't name the column with its own name, then .id just stores the index of the iteration.

This is expected behavior, per the purrr::map docs:

.id
Either a string or NULL. If a string, the output will contain a variable with that name, storing either the name (if .x is named) or the index (if .x is unnamed) of the input.

But my approach feels a little clunky, so I imagine I'm missing something. Is there a better way to get a list of the columns I'm iterating over, that doesn't require writing each column name twice in the input vector? Any suggestions would be much appreciated!

Here's an example to work with:

library(rlang)
library(tidyverse)

tb <- tibble(foo = rnorm(10), bar = rnorm(10))

cols_once <- c("foo", "bar")
cols_once %>% map_dfr(~ tb %>% summarise(avg = mean(!!sym(.x))), .id="var")
# A tibble: 2 x 2
  var       avg   <-- var stores only the iteration index
  <chr>   <dbl>
1 1     -0.0519
2 2      0.204 

cols_twice <- c("foo" = "foo", "bar" = "bar")
cols_twice %>% map_dfr(~ tb %>% summarise(avg = mean(!!sym(.x))), .id="var")
# A tibble: 2 x 2
  var       avg   <-- var stores the column names
  <chr>   <dbl>
1 foo   -0.0519
2 bar    0.204 

回答1:


You could create your input vector easily with:

setNames(names(tb), names(tb))

So your code would be:

setNames(names(tb), names(tb)) %>%
  map_dfr(~ tb %>% summarise(avg = mean(!!sym(.x))), .id="var")

Edit following your comment:

Still not the solution you are hoping for, but when you don't use all the column names, you could still use setNames() and subset the ones you want (or subset out the ones you don't).

tb <- tibble(foo = rnorm(10), bar = rnorm(10), taz = rnorm(10))

setNames(names(tb), names(tb))[-3]



回答2:


Here's an alternative solution for your specific scenario using summarize_at and gather:

tb %>% summarize_at( cols_once, mean ) %>% gather( var, avg )
# # A tibble: 2 x 2
#   var      avg
#   <chr>  <dbl>
# 1 foo   0.374 
# 2 bar   0.0397

In a more general scenario, I don't think there's a way around naming your cols_once when working with map_dfr, because of the expected behavior you pointed out in your question. However, you can use the "snake case" wrapper for setNames() to do it more elegantly:

cols_once %>% set_names %>% 
  map_dfr(~ tb %>% summarise(avg = mean(!!sym(.x))), .id="var")
# # A tibble: 2 x 2
#   var      avg
#   <chr>  <dbl>
# 1 foo   0.374 
# 2 bar   0.0397


来源:https://stackoverflow.com/questions/53311543/make-column-of-input-items-with-purrrmap-df-using-id-without-duplicating-inpu

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!