【问题标题】:Include column names as function input with dplyr使用 dplyr 将列名作为函数输入包含在内
【发布时间】:2020-09-11 11:37:36
【问题描述】:

我经常需要将长而整齐的数据帧转换为宽格式。为此,我使用以下标准程序:

# Example data frame
df <- data.frame("ID" = rep(1:5, each = 4), "score" = runif(20, 0, 100), "location" = rep(c("a", "b", "c", "d"), 5))

# Transform into wide format
df_wide <- df %>%
  group_by_at(vars(-score)) %>%  # group by everything other than the value column. 
  mutate(row_id=1:n()) %>% ungroup() %>%  # build group index
  spread(key=location, value=score) %>%    # spread
  dplyr::select(-row_id)

我不想一遍又一遍地输入这个小脚本,而是想定义一个函数来自动完成。我发现了许多关于如何将列名包含为函数输入的有用帖子,但不知何故它不起作用或我收到错误消息。我做错了什么?

根据thesethis 的建议,以下是我的一些尝试(它们都不起作用):

wide_fun <- function(dat, key_name, value_name) {
  group_by_at(vars(- !! sym(value_name))) %>%  # group by everything other than the value column. 
    mutate(row_id=1:n()) %>% ungroup() %>%  # build group index
    spread(key=!! sym(key_name), value=!! sym(value_name)) %>%    # spread
    dplyr::select(-row_id)
}

wide_fun2 <- function(dat,  key_name, value_name) {
  key_col <- enquo(key_name)
  value_col <- enquo(value_name)
  group_by_at(vars(- !!value_col)) %>%  # group by everything other than the value column. 
    mutate(row_id=1:n()) %>% ungroup() %>%  # build group index
    spread(key= !!key_col, value= !!value_col) %>%    # spread
    dplyr::select(-row_id)
}

wide_fun3 <- function(dat, key_name, value_name) {
  group_by_at(vars(- value_name)) %>%  # group by everything other than the value column. 
    mutate(row_id=1:n()) %>% ungroup() %>%  # build group index
    spread(key=key_name, value=value_name) %>%    # spread
    dplyr::select(-row_id)
}

wide_fun3(df, quote(location), quote(score))

感谢您的帮助!

【问题讨论】:

  • 对于分组使用group_by(across(all_of(value_name))),而不是分散使用pivot_wider(names_from=key_name, values_from=value_name)
  • 嗨!感谢您的建议,但我并不想改变我转换数据框的方式,我只是想知道为什么我不能像现在这样将列名包含到函数中。
  • 另外across()似乎是 dplyr 的新成员?我更新了它,但仍然收到找不到它的错误消息...
  • 该代码允许您使用字符向量作为输入。快速修改我原来的答案。 group_by(across(!all_of(value_name))) 允许您为 value_name 指定一个或多个值,并将根据这些值以外的任何值进行分组。

标签: r function dplyr


【解决方案1】:

我已将您的代码稍微更新为dplyr 1.0.0tidyr。然后您可以使用new dplyr programming feature {{}} 来指定作为函数参数的变量。

# Example data frame
df <- data.frame("ID" = rep(1:5, each = 4), "score" = runif(20, 0, 100), "location" = rep(c("a", "b", "c", "d"), 5))
library(dplyr)
wide_fun <- function(.data, key_name, value_name) {
  .data %>% 
  group_by(across(-{{value_name}})) %>%  # group by everything other than the value column. 
    mutate(row_id = 1:n()) %>% ungroup() %>%  # build group index
    tidyr::pivot_wider(
      names_from = {{key_name}},
      values_from = {{value_name}}) %>%    # spread
    select(-row_id)
}

wide_fun(df, location, score)
#> # A tibble: 5 x 5
#>      ID     a     b     c     d
#>   <int> <dbl> <dbl> <dbl> <dbl>
#> 1     1  90.8  38.9  28.7  39.0
#> 2     2  94.5  24.9  84.6  54.6
#> 3     3  61.1  97.2  12.2  57.7
#> 4     4  52.7  85.6  41.4 100. 
#> 5     5  17.8  86.1  92.3  33.7

reprex package (v0.3.0) 于 2020 年 9 月 11 日创建

编辑

这个函数也应该适用于旧版本的dplyr

library(dplyr)
wide_fun_2 <- function(.data, key_name, value_name) {
  .data %>% 
    group_by_at(vars(-!!ensym(value_name))) %>%  # group by everything other than the value column. 
    mutate(row_id = 1:n()) %>% ungroup() %>%  # build group index
    tidyr::pivot_wider(
      names_from = !!ensym(key_name),
      values_from = !!ensym(value_name)) %>%    # spread
    select(-row_id)
}

df %>% 
  wide_fun_2(location, score)
 A tibble: 5 x 5
     ID      a     b     c     d
  <int>  <dbl> <dbl> <dbl> <dbl>
1     1 72.2    81.4  52.5  48.8
2     2 36.1    27.5  82.2  73.0
3     3 83.9    68.2  80.9  15.7
4     4  0.451  70.0  18.5  43.2
5     5 82.6    68.2  22.8  63.0

如果你只提供指定列的参数,你只需要处理符号而不是quosures,因此你需要使用ensym

【讨论】:

  • 这看起来很不错,谢谢!我在更新 dplyr 时遇到问题,但我会尝试一下。
  • 好吧,我明白了——我不知道。我以前从未使用过此类函数,非常感谢!
  • 这里是元编程的一个很好的介绍:adv-r.hadley.nz/meta-big-picture.html
猜你喜欢
  • 2021-06-24
  • 1970-01-01
  • 2018-05-09
  • 2014-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-23
相关资源
最近更新 更多