ast*_*rch 0 r dataframe dplyr nse tidyverse
一年多以来,我一直在尝试为此找到解决方案,并决定写一篇关于它的文章。任何帮助,将不胜感激。这是我可以在 Stata 和 SAS 中轻松完成的伪代码,但我不知道如何在 R 中完成。{} 是今年引入 dplyr 的类似胶水的运算符,因此我将其用作占位符对于使伪代码工作的运算符。
library(tidyverse)
var <- "mpg"
df_name <- "mtcars"
{df_name} %>% count({var})
{df_name}_1 <- {df_name} %>% mutate(., {var}_1={var}/2)
length({df_name}_1)
Run Code Online (Sandbox Code Playgroud)
应该导致
library(tidyverse)
var <- "mpg"
df_name <- "mtcars"
mtcars %>% count(mpg)
mtcars_1 <- mtcars %>% mutate(., mpg_1=mpg/2)
length(mtcars_1)
Run Code Online (Sandbox Code Playgroud)
在 Stata 中,我可以轻松地使用本地或全局宏,如下所示:
local df_name "mtcars"
Run Code Online (Sandbox Code Playgroud)
然后将其引用为“df_name”
在 SAS 中,我可以使用这样的全局宏来做到这一点:
%LET df_name=mtcars;
Run Code Online (Sandbox Code Playgroud)
然后像&df_name一样引用它。
请注意引用这些值在视觉上是多么容易——没有赋值、获取、括号、mget 等。
这两种方法都允许在数据集名称、函数、变量等中使用它们。极大地简化了我的代码并为我节省了大量时间。如何在 R 中以视觉简单的方式做到这一点?我的代码对于熟悉 Stata/SAS 的人来说应该是可读的(dplyr 在这方面很棒!)而且太多的 eval,将所有东西都包装在函数中,用括号赋值只会让他们放弃项目或迫使我改回SAS/Stata。
我尝试了 {{}}、!!、enquo、sym 和 NSE 的所有组合,但仍然不知道如何以视觉上简单的方式使其工作。在 dplyr 管道中,变量名称终于有了一些解决方法,但对于数据帧和基础 R 没有任何解决方法。
我真的很感激在这件事上的任何帮助!我在 2009 年用 R 遇到了这个问题,然后放弃了 R,直到我不得不在 2019 年回来,但仍然找不到解决这个问题的简单方法。
R 是一种函数式编程语言。您将使用元编程来创建代码,而不是使用像 SAS 这样的宏样式文本替换来创建新功能。R 中的变量名是符号,而不是字符串。试图强制这两种不同的编程范式看起来相同通常不是一个好主意,并且会导致两种语言中的一种非常不习惯。
使用 R 最好跟踪数据本身而不是数据的名称并跟踪要与符号一起使用的列。
library(tidyverse)
df <- mtcars
var <- rlang::sym("mpg")
Run Code Online (Sandbox Code Playgroud)
然后你可以做
df %>% count(!!var)
Run Code Online (Sandbox Code Playgroud)
或者创建一个函数
get_counts <- function(data, x) {
data %>% count({{x}})
}
get_counts(df, mpg) #use actual column name
get_counts(df, !!var) #or use name from variable with !!
Run Code Online (Sandbox Code Playgroud)
您可以使用dplyr:=和一些glue样式 sytnax命名新变量
df %>% mutate(., "{var}_1" := !!var/2)
Run Code Online (Sandbox Code Playgroud)
您也可以将其放入函数中
create_new_var <- function(data, x) {
data %>% mutate(., "{{x}}_1":={{x}}/2)
}
create_new_var(df, mpg)
create_new_var(df, !!var)
Run Code Online (Sandbox Code Playgroud)
然后,而不是创建名称中带有数字索引的变量,您只需使用管道
df %>%
create_new_var(!!var) %>%
length()
Run Code Online (Sandbox Code Playgroud)
或者,如果您有多个要使用的值,则将值保存在命名列表中。然后你可以在列表上映射函数。例如
df_name <- "mtcars"
data <- mget(df_name, inherits = TRUE)
fixed <- map(data, ~create_new_var(., !!var))
lens <- map(fixed, ~length(.))
lens$mtcars
# [1] 12
lens[[df_name]]
# [1] 12
Run Code Online (Sandbox Code Playgroud)
如果您在data列表中存储了多个 data.frames,这也将起作用
我强烈建议你不要使用这样的东西,但你可以定义一种新的赋值类型,允许字符串行变量名。例如
`%<-%` <- function(x, value) {
varname <- glue::glue(x, .envir = parent.frame())
invisible(assign(varname, value, envir = parent.frame()))
}
df_name <- "mtcars"
"{df_name}_1" %<-% { get(df_name) %>% create_new_var(!!var) }
Run Code Online (Sandbox Code Playgroud)
在这里,我们定义了%<-%而不是<-将采用类似字符串的名称并扩展它们并将它们转换为新变量。我们使用get()字符串来获取变量的值。请注意,我们需要{}围绕要分配给新值的表达式,因为我们无法控制 new 运算符的优先级。它与管道运算符具有相同的优先级,因此默认情况下一切都会从左到右。不过,这不是适当的 R 编程可能会做的事情。