如何在dplyr中将动态列名传递到自定义函数中?

44

我有一个具有以下结构的数据集:

Classes ‘tbl_df’ and 'data.frame':  10 obs. of  7 variables:
 $ GdeName  : chr  "Aeugst am Albis" "Aeugst am Albis" "Aeugst am Albis" "Aeugst am Albis" ...
 $ Partei   : chr  "BDP" "CSP" "CVP" "EDU" ...
 $ Stand1971: num  NA NA 4.91 NA 3.21 ...
 $ Stand1975: num  NA NA 5.389 0.438 4.536 ...
 $ Stand1979: num  NA NA 6.2774 0.0195 3.4355 ...
 $ Stand1983: num  NA NA 4.66 1.41 3.76 ...
 $ Stand1987: num  NA NA 3.48 1.65 5.75 ...

我想提供一个函数,它可以计算任何值之间的差异,并且我想使用dplyrmutate函数来实现这一点:(假设参数fromto作为参数传递)

from <- "Stand1971"
to <- "Stand1987"

data %>%
  mutate(diff = from - to)

当然,这不起作用,因为dplyr使用非标准评估。我知道现在有一种优雅的解决方案可以使用mutate_来解决这个问题,并且我已经阅读了这篇文献,但是我仍然无法理解。

该怎么办?

这里是一个可重现示例的数据集的前几行:

structure(list(GdeName = c("Aeugst am Albis", "Aeugst am Albis", 
"Aeugst am Albis", "Aeugst am Albis", "Aeugst am Albis", "Aeugst am Albis", 
"Aeugst am Albis", "Aeugst am Albis", "Aeugst am Albis", "Aeugst am Albis"
), Partei = c("BDP", "CSP", "CVP", "EDU", "EVP", "FDP", "FGA", 
"FPS", "GLP", "GPS"), Stand1971 = c(NA, NA, 4.907306434, NA, 
3.2109535926, 18.272143463, NA, NA, NA, NA), Stand1975 = c(NA, 
NA, 5.389079711, 0.4382328556, 4.5363022622, 18.749259742, NA, 
NA, NA, NA), Stand1979 = c(NA, NA, 6.2773722628, 0.0194647202, 
3.4355231144, 25.294403893, NA, NA, NA, 2.7055961071), Stand1983 = c(NA, 
NA, 4.6609804428, 1.412940467, 3.7563539244, 26.277246489, 0.8529335746, 
NA, NA, 2.601878177), Stand1987 = c(NA, NA, 3.4767860929, 1.6535933856, 
5.7451770193, 22.146844746, NA, 3.7453183521, NA, 13.702211858
)), .Names = c("GdeName", "Partei", "Stand1971", "Stand1975", 
"Stand1979", "Stand1983", "Stand1987"), class = c("tbl_df", "data.frame"
), row.names = c(NA, -10L))

2
它并没有回答你的问题,但从上下文猜测,你最好使用一个整洁的数据集,这样你就可以使用 lead(x) - x 来一次性计算所有年份的相邻值之间的差异。 - hadley
2个回答

67

使用最新版本的dplyr(>=0.7),您可以使用rlang !!(叹号叹号)运算符。

library(tidyverse)
from <- "Stand1971"
to <- "Stand1987"

data %>%
  mutate(diff=(!!as.name(from))-(!!as.name(to)))

你只需要使用as.name将字符串转换为名称,然后将它们插入到表达式中即可。不幸的是,我似乎需要使用比我想要的更多的括号,但是!!运算符似乎按照一种奇怪的操作顺序执行。
原始答案,dplyr (0.3- <0.7):
从那个vignette (vignette("nse","dplyr")) 中使用lazyeval的interp()函数。
library(lazyeval)

from <- "Stand1971"
to <- "Stand1987"

data %>%
  mutate_(diff=interp(~from - to, from=as.name(from), to=as.name(to)))

为什么这种方法比使用“paste”更加“性感”(或首选)? - grssnbchr
1
interp()有助于捕获适当的环境,尤其是在具有更复杂的作用域或非基本函数时更为重要。 - MrFlick
8
无论变量的名称是什么,interp方法总是有效的,并且可以捕获环境。使用paste函数只会在你的代码中引入一个不稳定的元素。 - hadley
如果我想让我的新列名(例如,diff)也是动态的呢?同样的构造似乎在 mutate 赋值语句的左侧不起作用。 - DanTan
9
使用mutate(!!diff := (!!as.name(from)) - (!!as.name(to))):=允许您更改等号左侧的新列的名称。请参阅https://dev59.com/ul8e5IYBdhLWcg3wLX8-。 - MrFlick

19

现在您可以在 dplyr 链中使用 .data

library(dplyr)
from <- "Stand1971"
to <- "Stand1987"

data %>% mutate(diff = .data[[from]] - .data[[to]])

另一个选项是使用带有叹叹号的bang-bang(!!)的sym

data %>% mutate(diff = !!sym(from) - !!sym(to))

在基础 R 中,我们可以使用:

data$diff <- data[[from]] - data[[to]]

这个答案很完美,但是在代码的其他部分中,我使用胶水语法{var}来完成这个任务。然而,在这种情况下它不起作用。现在dplyr是否有一个等效于胶水语法的.data呢? - Keipi
3
你尝试过使用 {.data[var]} 吗?或许你可以针对你的具体情况提一个新的问题。 - Ronak Shah

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