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

虹膜数据示例:

library(dplyr)
iris <- as_tibble(iris)

我创建了一个函数来改变花瓣中的新列。宽度变量:

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

现在我创建了一个循环来构建我的列:

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

然而,由于mutate认为varname是一个字面变量名,因此循环只创建了一个新变量(称为varname),而不是四个(称为花瓣)。2 -花瓣。5)。

我怎么能得到mutate()使用我的动态名称作为变量名?


当前回答

我还添加了一个答案,稍微加强了这一点,因为我在搜索答案时来到了这个条目,这几乎是我需要的,但我需要更多,这是我通过@MrFlik的答案和R lazyeval小插图得到的。

我想做一个函数,可以接受一个dataframe和列名向量(作为字符串),我想从字符串转换为Date对象。我不知道如何使as. date()接受一个字符串参数并将其转换为列,所以我如下所示。

下面是我如何通过SE mutate (mutate_())和.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用于交互用途,但我发现使用dplyr来实现这一点非常棘手,因为您必须通过圆环来使用lazyeval::interp()、setNames等变通方法。

下面是一个使用基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) 

在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)
 }   

根据应用在'iris1'上的@MrFlick的多瓣检查输出

identical(iris1, iris2)
#[1] TRUE

这是另一个版本,可以说更简单一点。

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

另一种替代方法:在引号内使用{}来轻松创建动态名称。这与其他解决方案相似,但并不完全相同,而且我发现它更简单。

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 1.0.0,但不确定(如果重要的话,我也有rlang 4.7.0)。

由于动态地将变量名构建为字符值,因此使用标准data.frame索引(允许为列名提供字符值)进行赋值更有意义。例如:

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

mutate函数使得通过命名参数命名新列变得非常容易。但这假设您在键入命令时知道名称。如果希望动态指定列名,则还需要构建named参数。


双版本>= 1.0

在最新的dplyr版本中,您可以在使用:=命名参数时使用glue包中的语法。因此,这里名称中的{}通过计算其中的表达式来获取值。

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

如果要向函数传递列名,可以在字符串中使用{{}},也可以在列名中使用{{}}

meanofcol <- function(df, col) {
  mutate(df, "Mean of {{col}}" := mean({{col}}))
}
meanofcol(iris, Petal.Width)


dpoyr版本

从0.7版开始,Dplyr允许您使用:=来动态分配参数名。你可以这样写你的函数:

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

有关更多信息,请参阅从vignette(“programming”,“dplyr”)中获得的文档。


dplyr (>=0.3 & <0.7)

稍早一些的dplyr版本(>=0.3 <0.7)鼓励使用“标准求值”替代许多函数。有关更多信息,请参阅非标准评估小插图(小插图(“nse”))。

所以在这里,答案是使用mutate_()而不是mutate(),并执行:

# --- dplyr version 0.3-0.5---
multipetal <- function(df, n) {
    varname <- paste("petal", n , sep=".")
    varval <- lazyeval::interp(~Petal.Width * n, n=n)
    mutate_(df, .dots= setNames(list(varval), varname))
}

双工< 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)
}