2014-09-23 131 views
61

我想使用dplyr的mutate()在數據框中創建多個新列。列名稱及其內容應該動態生成。從虹膜dplyr - mutate:使用動態變量名稱

示例數據:

require(dplyr) 
data(iris) 
iris <- tbl_df(iris) 

我創建了一個函數從Petal.Width變量變異我的新欄目:

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)而不是四個(稱爲petal.2 - petal.5)。

如何獲得mutate()以將我的動態名稱用作變量名?

+1

我沒有堅持在變異時,我問是否可能。也許這只是我不知道的一個小動作。如果還有其他方法,讓我們聽聽。 – 2014-09-23 20:11:20

+0

我相信在[lazyeval包](https://github.com/hadley/lazyeval)中有[可以觀看的空間](https://github.com/hadley/dplyr/issues/352#issuecomment-53829862) – baptiste 2014-09-23 20:59:06

+0

在這一點上,'dplyr'有一個[非標準評估整個小插曲](https://cran.r-project.org/web/packages/dplyr/vignettes/nse.html) – Gregor 2016-07-05 23:25:03

回答

73

由於您正在顯着地將變量名稱構建爲字符值,因此使用標準數據進行分配更有意義。框架索引允許列名的字符值。例如:

mutate功能使得它很容易通過名字命名參數的新列。但是,假設您輸入命令時知道名稱。如果要動態指定列名稱,則還需要構建命名參數。

最新版本的dplyr(0.7)通過使用:=來動態分配參數名稱。您可以將函數編寫爲:

# --- 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)鼓勵使用「標準評估」替代許多功能。有關更多信息,請參閱非標準評估小插圖(vignette("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)) 
} 

dplyr

注意這個的舊版本,也可以在已經存在舊版本dplyr的當問題最初提出。它需要謹慎使用quotesetName

# --- 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) 
} 
+15

謝謝,這很有幫助。順便說一句,我總是創造真正戲劇性的變數。 – 2014-09-23 20:18:47

+18

呵呵。這可能是我一段時間以來我最喜歡的拼寫錯誤之一。我想我會離開它。 – MrFlick 2014-09-23 20:19:56

+1

'do.call()'可能不會做你認爲它的做法:http://rpubs.com/hadley/do-call2。另請參閱dplyr開發版中的nse小插曲。 – hadley 2014-09-26 11:39:33

4

我也加入,因爲我來到這個條目的答案搜索時增加了此一點點的答案,這有我需要的幾乎什麼,但我需要多一點,我通過@MrFlik的答案和R lazyeval小插曲獲得了一些。

我想創建一個函數,它可以將一個數據幀和一個向量列名(字符串),我想從一個字符串轉換爲一個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 
8

這是另一個版本,它可以說更簡單一些。

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 
1

而我則喜歡使用dplyr交互使用,我覺得是因爲你必須去赴湯蹈火使用lazyeval ::口譯(它非常棘手做到這一點使用dplyr),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) 
+1

+1,儘管我仍然在非交互式設置中使用'dplyr',函數內部的variabel輸入使用非常笨重的語法。 – 2017-02-14 12:30:10

17

dplyr0.6.0在2017年四月等待),我們也可以做一個賦值(:=),並通過變量列名新版本通過unquoting(!!)無法評估它

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

檢查輸出基於施加@ MrFlick的multipetal「iris1」

identical(iris1, iris2) 
#[1] TRUE