在dplyr::funs的命名参数中,我可以引用其他参数的名称吗?

8
考虑以下内容:
library(tidyverse)

df <- tibble(x = rnorm(100), y = rnorm(100, 10, 2), z = x * y)

df %>% 
mutate_all(funs(avg = mean(.), dev = sd(.), scaled = (. - mean(.)) / sd(.)))

有没有一种避免通过调用meansd两次来引用avgdev列的方法?我的想法是这样的:
df %>% 
mutate_all(funs(avg = mean(.), dev = sd(.), scaled = (. - avg) / dev))

很明显这样是行不通的,因为没有 avgdev 这些列,但有 x_avgx_devy_avgy_dev等列。

是否有一种好的方法,在 funs 中使用 rlang 工具以编程方式创建这些列参照,这样我就可以引用前面命名参数创建的列(当 .x 时,我将引用 x_meanx_dev 计算 x_scaled,依此类推)?

3个回答

5

我认为,如果您将数据转换为长格式,会更容易进行操作。

library(tidyverse)

set.seed(111)
df <- tibble(x = rnorm(100), y = rnorm(100, 10, 2), z = x * y)

df %>% 
  gather(key, value) %>% 
  group_by(key) %>% 
  mutate(avg    = mean(value),
         sd     = sd(value),
         scaled = (value - avg) / sd)
#> # A tibble: 300 x 5
#> # Groups:   key [3]
#>    key    value     avg    sd scaled
#>    <chr>  <dbl>   <dbl> <dbl>  <dbl>
#>  1 x      0.235 -0.0128  1.07  0.232
#>  2 x     -0.331 -0.0128  1.07 -0.297
#>  3 x     -0.312 -0.0128  1.07 -0.279
#>  4 x     -2.30  -0.0128  1.07 -2.14 
#>  5 x     -0.171 -0.0128  1.07 -0.148
#>  6 x      0.140 -0.0128  1.07  0.143
#>  7 x     -1.50  -0.0128  1.07 -1.39 
#>  8 x     -1.01  -0.0128  1.07 -0.931
#>  9 x     -0.948 -0.0128  1.07 -0.874
#> 10 x     -0.494 -0.0128  1.07 -0.449
#> # ... with 290 more rows

本文档由reprex包(v0.2.1.9000)于2018-11-04创建。


2
值得注意的是,如果需要,可以使用 spread 将结果转换回宽格式。 - Artem Sokolov

2
这可能适用于您:

这对您可能有用:

avg <- quo(mean(.))
dev <- quo(sd(.))
res <- df %>% 
  mutate_all(funs(avg = !!avg, dev = !!dev, scaled = (. - !!avg) / !!dev))

确认它能够正常工作:

res0 <- df %>% 
  mutate_all(funs(avg = mean(.), dev = sd(.), scaled = (. - mean(.)) / sd(.)))
identical(res, res0)
# [1] TRUE

3
从技术上讲,这仍然会计算mean(。)sd(。)两次,但这是表达式算术的巧妙示例。 - Artem Sokolov

1
这似乎有点复杂,但它能够正常工作:

scaled <- function(col_name, x, y) {
  col_name <- deparse(substitute(col_name))
  avg <- eval.parent(as.symbol(paste0(col_name, x)))
  dev <- eval.parent(as.symbol(paste0(col_name, y)))
  (eval.parent(as.symbol(col_name)) - avg) / dev
}

df %>%
  mutate_all(funs(avg = mean(.), dev = sd(.), scaled = scaled(., "_avg", "_dev"))) 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接