在`dplyr'中使用动态变量名`

在`dplyr'中使用动态变量名`,r,dplyr,r-faq,R,Dplyr,R Faq,我想使用dplyr::mutate()在数据帧中创建多个新列。应动态生成列名及其内容 来自iris的示例数据: library(dplyr) iris <- as_tibble(iris) 现在,我创建一个循环来构建我的列: for(i in 2:5) { iris <- multipetal(df=iris, n=i) } for(2:5中的i){ iris由于您正在动态地将变量名构建为字符值,因此使用标准data.frame索引进行赋值更为合理,该索引允许列名的字符值

我想使用
dplyr::mutate()
在数据帧中创建多个新列。应动态生成列名及其内容

来自iris的示例数据:

library(dplyr)
iris <- as_tibble(iris)
现在,我创建一个循环来构建我的列:

for(i in 2:5) {
    iris <- multipetal(df=iris, n=i)
}
for(2:5中的i){

iris由于您正在动态地将变量名构建为字符值,因此使用标准data.frame索引进行赋值更为合理,该索引允许列名的字符值。例如:

multipetal <- function(df, n) {
    varname <- paste("petal", n , sep=".")
    df[[varname]] <- with(df, Petal.Width * n)
    df
}

dplyr版本>=0.7
dplyr
从0.7版开始允许您使用
:=
动态分配参数名称。您可以将函数编写为:

# --- dplyr version 0.7+---
multipetal <- function(df, n) {
    varname <- paste("petal", n , sep=".")
    mutate(df, !!varname := Petal.Width * n)
}

dplyr<0.3 注意:这在最初提出问题时存在的较旧版本的
dplyr
中也是可能的。它需要仔细使用
quote
setName

# --- dplyr versions < 0.3 ---
multipetal <- function(df, n) {
    varname <- paste("petal", n , sep=".")
    pp <- c(quote(df), setNames(list(quote(Petal.Width * n)), varname))
    do.call("mutate", pp)
}
#--dplyr版本<0.3---

多金属我还添加了一个答案,这个答案稍微增加了一点,因为我在搜索答案时找到了这个条目,这个条目几乎满足了我的需要,但我需要更多,这是通过@MrFlik的答案和R lazyeval vignettes获得的

我想做一个函数,它可以接受一个数据帧和一个列名向量(作为字符串),我想将其从字符串转换为日期对象。我不知道如何使
成为.Date()
接受一个字符串参数并将其转换为列,所以我按照如下所示进行了操作

下面是我是如何通过SE mutate(
mutate_389;()
)和
.dots
参数来做到这一点的。欢迎对这一点提出批评

library(dplyr)

dat <- data.frame(a="leave alone",
                  dt="2015-08-03 00:00:00",
                  dt2="2015-01-20 00:00:00")

# This function takes a dataframe and list of column names
# that have strings that need to be
# converted to dates in the data frame
convertSelectDates <- function(df, dtnames=character(0)) {
    for (col in dtnames) {
        varval <- sprintf("as.Date(%s)", col)
        df <- df %>% mutate_(.dots= setNames(list(varval), col))
    }
    return(df)
}

dat <- convertSelectDates(dat, c("dt", "dt2"))
dat %>% str
库(dplyr)

dat这是另一个版本,可以说更简单一些

multipetal <- function(df, n) {
    varname <- paste("petal", n, sep=".")
    df<-mutate_(df, .dots=setNames(paste0("Petal.Width*",n), varname))
    df
}

for(i in 2:5) {
    iris <- multipetal(df=iris, n=i)
}

> head(iris)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species petal.2 petal.3 petal.4 petal.5
1          5.1         3.5          1.4         0.2  setosa     0.4     0.6     0.8       1
2          4.9         3.0          1.4         0.2  setosa     0.4     0.6     0.8       1
3          4.7         3.2          1.3         0.2  setosa     0.4     0.6     0.8       1
4          4.6         3.1          1.5         0.2  setosa     0.4     0.6     0.8       1
5          5.0         3.6          1.4         0.2  setosa     0.4     0.6     0.8       1
6          5.4         3.9          1.7         0.4  setosa     0.8     1.2     1.6       2

multipetal虽然我喜欢使用dplyr进行交互,但我发现使用dplyr进行交互非常困难,因为要使用lazyeval::interp()、setNames等变通方法,您必须经历很多困难

这是一个使用base R的简单版本,在这个版本中,将循环放在函数中似乎更直观,至少对我来说是这样,它扩展了@MrFlicks的解决方案

multipetal <- function(df, n) {
   for (i in 1:n){
      varname <- paste("petal", i , sep=".")
      df[[varname]] <- with(df, Petal.Width * i)
   }
   df
}
multipetal(iris, 3) 

multimetal在新发布的
dplyr
0.6.0
将于2017年4月发布)中,我们还可以执行赋值(
:=
)并通过取消引用(
!!
)将变量作为列名传递,以避免对其求值

 library(dplyr)
 multipetalN <- function(df, n){
      varname <- paste0("petal.", n)
      df %>%
         mutate(!!varname := Petal.Width * n)
 }

 data(iris)
 iris1 <- tbl_df(iris)
 iris2 <- tbl_df(iris)
 for(i in 2:5) {
     iris2 <- multipetalN(df=iris2, n=i)
 }   

经过大量的尝试和错误,我发现模式
UQ(rlang::sym(“此处的某些字符串”))
对于处理字符串和dplyr动词非常有用。它似乎在很多令人惊讶的情况下都能工作

下面是一个使用
mutate
的示例。我们希望创建一个将两列相加的函数,将两个列名作为字符串传递给函数。我们可以使用此模式,以及赋值运算符
:=
,来完成此操作

## Take column `name1`, add it to column `name2`, and call the result `new_name`
mutate_values <- function(new_name, name1, name2){
  mtcars %>% 
    mutate(UQ(rlang::sym(new_name)) :=  UQ(rlang::sym(name1)) +  UQ(rlang::sym(name2)))
}
mutate_values('test', 'mpg', 'cyl')
或排列

## transform a variable and then sort by it 
arrange_values <- function(name, transform){
  mtcars %>% 
    arrange(UQ(rlang::sym(name)) %>%  UQ(rlang::sym(transform)))
}
arrange_values('mpg', 'sin')
您可能会喜欢这个软件包,它为新用户/临时用户提供了简化的tidy eval API和文档

您正在创建希望
变异为列名的字符串。因此,使用
friendlyeval
可以编写:

multipetal <- function(df, n) {
  varname <- paste("petal", n , sep=".")
  df <- mutate(df, !!treat_string_as_col(varname) := Petal.Width * n)
  df
}

for(i in 2:5) {
  iris <- multipetal(df=iris, n=i)
}
multimetal我们有卷曲操作符(
{{}
),这使得这非常容易。当动态列名显示在赋值的左侧时,使用
:=

library(dplyr)
library(rlang)

iris1 <- tbl_df(iris)

multipetal <- function(df, n) {
   varname <- paste("petal", n , sep=".")
   mutate(df, {{varname}} := Petal.Width * n)
}

multipetal(iris1, 4)

# A tibble: 150 x 6
#   Sepal.Length Sepal.Width Petal.Length Petal.Width Species petal.4
#          <dbl>       <dbl>        <dbl>       <dbl> <fct>     <dbl>
# 1          5.1         3.5          1.4         0.2 setosa      0.8
# 2          4.9         3            1.4         0.2 setosa      0.8
# 3          4.7         3.2          1.3         0.2 setosa      0.8
# 4          4.6         3.1          1.5         0.2 setosa      0.8
# 5          5           3.6          1.4         0.2 setosa      0.8
# 6          5.4         3.9          1.7         0.4 setosa      1.6
# 7          4.6         3.4          1.4         0.3 setosa      1.2
# 8          5           3.4          1.5         0.2 setosa      0.8
# 9          4.4         2.9          1.4         0.2 setosa      0.8
#10          4.9         3.1          1.5         0.1 setosa      0.4
# … with 140 more rows

另一种选择是:在引号内使用
{}
来轻松创建动态名称。这与其他解决方案类似,但并不完全相同,我发现这更容易

library(dplyr)
library(tibble)

iris <- as_tibble(iris)

multipetal <- function(df, n) {
  df <- mutate(df, "petal.{n}" := Petal.Width * n)  ## problem arises here
  df
}

for(i in 2:5) {
  iris <- multipetal(df=iris, n=i)
}
iris
库(dplyr)
图书馆(tibble)

iris谢谢你,这很有帮助。顺便说一句,我总是创建非常戏剧性的变量。呵呵。这可能是我最近最喜欢的打字错误之一。我想我会留下它。
do.call()
可能没有你想象的那样做:。另请参阅dplyr开发版本中的nse小插曲。因此,如果我理解你的观点@hadley,我已经更新了上面的
do.call
以使用
do.call(“mutate”)
并在列表中引用
df
。这就是你的建议吗?当
lazyeval
版本的
dplyr
是发布的版本时,然后
mutate(df,.dots=setNames(list(~Petal.Width*n),varname))
会是一个更好的解决方案吗?如果我不仅需要在赋值的左侧,而且还需要在右侧使用变量列标题怎么办?例如
mutate(df,!!newVar:=(!!var1+!!var2)/2)
不起作用:(我相信在vignette中甚至没有提到
mutate\uu
,从其他函数中也看不出如何使用它。多年来,我一直在努力理解
quosure
等文档。虽然上面的vignette链接不再有效,但这条评论让我得出了这个总结,以进行评估:。我最终我明白了!谢谢。
dplyr
提供了这方面的内容。+1,虽然我在非交互式设置中仍然经常使用
dplyr
,但在函数中与variabel输入一起使用它会使用非常笨拙的语法。您的提示非常有效,但我有一个小问题。我将初始列
mycl
更改为url(例如),并用新名称复制数据帧
df
末尾的旧列
myclinicialvalue
发回MyClinicialValue的列。
我还没有写问题,因为我没有找到reprex。我的目标是
DT::datatable()
escape
参数。我使用
escape=FALSE
等待它。对于常量,它也不起作用,但似乎也得到了坏列。:)我的问题似乎不是动态变量的原因。(顺便说一句,reprex补充道)谢谢你的回答!这里有一个超级简单的例子说明我是如何使用它的:
varname=sym(“Petal.Width”);ggplot(iris,aes(x=!!varname))+geom_histogram()
这在公式中对我有效!
## filter a column by a value 
filter_values <- function(name, value){
  mtcars %>% 
    filter(UQ(rlang::sym(name)) != value)
}
filter_values('gear', 4)
## transform a variable and then sort by it 
arrange_values <- function(name, transform){
  mtcars %>% 
    arrange(UQ(rlang::sym(name)) %>%  UQ(rlang::sym(transform)))
}
arrange_values('mpg', 'sin')
## select a column 
select_name <- function(name){
  mtcars %>% 
    select(!!name)
}
select_name('mpg')
multipetal <- function(df, n) {
  varname <- paste("petal", n , sep=".")
  df <- mutate(df, !!treat_string_as_col(varname) := Petal.Width * n)
  df
}

for(i in 2:5) {
  iris <- multipetal(df=iris, n=i)
}
library(dplyr)
library(rlang)

iris1 <- tbl_df(iris)

multipetal <- function(df, n) {
   varname <- paste("petal", n , sep=".")
   mutate(df, {{varname}} := Petal.Width * n)
}

multipetal(iris1, 4)

# A tibble: 150 x 6
#   Sepal.Length Sepal.Width Petal.Length Petal.Width Species petal.4
#          <dbl>       <dbl>        <dbl>       <dbl> <fct>     <dbl>
# 1          5.1         3.5          1.4         0.2 setosa      0.8
# 2          4.9         3            1.4         0.2 setosa      0.8
# 3          4.7         3.2          1.3         0.2 setosa      0.8
# 4          4.6         3.1          1.5         0.2 setosa      0.8
# 5          5           3.6          1.4         0.2 setosa      0.8
# 6          5.4         3.9          1.7         0.4 setosa      1.6
# 7          4.6         3.4          1.4         0.3 setosa      1.2
# 8          5           3.4          1.5         0.2 setosa      0.8
# 9          4.4         2.9          1.4         0.2 setosa      0.8
#10          4.9         3.1          1.5         0.1 setosa      0.4
# … with 140 more rows
multipetal <- function(df, name, n) {
   mutate(df, {{name}} := Petal.Width * n)
}

multipetal(iris1, temp, 3)

# A tibble: 150 x 6
#   Sepal.Length Sepal.Width Petal.Length Petal.Width Species  temp
#          <dbl>       <dbl>        <dbl>       <dbl> <fct>   <dbl>
# 1          5.1         3.5          1.4         0.2 setosa  0.6  
# 2          4.9         3            1.4         0.2 setosa  0.6  
# 3          4.7         3.2          1.3         0.2 setosa  0.6  
# 4          4.6         3.1          1.5         0.2 setosa  0.6  
# 5          5           3.6          1.4         0.2 setosa  0.6  
# 6          5.4         3.9          1.7         0.4 setosa  1.2  
# 7          4.6         3.4          1.4         0.3 setosa  0.900
# 8          5           3.4          1.5         0.2 setosa  0.6  
# 9          4.4         2.9          1.4         0.2 setosa  0.6  
#10          4.9         3.1          1.5         0.1 setosa  0.3  
# … with 140 more rows
multipetal(iris1, "temp", 3)
library(dplyr)
library(tibble)

iris <- as_tibble(iris)

multipetal <- function(df, n) {
  df <- mutate(df, "petal.{n}" := Petal.Width * n)  ## problem arises here
  df
}

for(i in 2:5) {
  iris <- multipetal(df=iris, n=i)
}
iris