2014-09-23 35 views
61

Tôi muốn sử dụng dplyr'smutate() để tạo nhiều cột mới trong một khung dữ liệu. Tên cột và nội dung của chúng phải được tạo động.dplyr - mutate: sử dụng tên biến động

Ví dụ dữ liệu từ iris:

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

tôi đã tạo ra một chức năng để đột biến cột mới của tôi từ Petal.Width biến:

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

Bây giờ tôi tạo ra một vòng lặp để xây dựng các cột của tôi:

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

Tuy nhiên, vì biến thể nghĩ varname là tên biến chữ, vòng lặp chỉ tạo ra một biến mới (gọi là varname) thay vì bốn (gọi là petal.2 - petal.5).

Làm cách nào để có được mutate() để sử dụng tên động của tôi làm tên biến?

+1

tôi không khẳng định trên đột biến, tôi hỏi nếu nó có thể. Có lẽ đó chỉ là một mẹo nhỏ mà tôi không biết. Nếu có cách khác, hãy nghe nó. –

+0

tôi tin rằng có [không gian để xem] (https://github.com/hadley/dplyr/issues/352#issuecomment-53829862) trong [gói lười biếng] (https://github.com/hadley/lazyeval) – baptiste

+0

Tại thời điểm này, 'dplyr' có [toàn bộ họa tiết trên đánh giá phi tiêu chuẩn] (https://cran.r-project.org/web/packages/dplyr/vignettes/nse.html) – Gregor

Trả lời

73

Vì bạn đang xây dựng một cách đáng kể tên biến làm giá trị ký tự, nên có ý nghĩa hơn khi thực hiện gán bằng cách sử dụng chỉ mục data.frame chuẩn cho phép giá trị ký tự cho tên cột. Ví dụ:

Chức năng mutate giúp bạn đặt tên cột mới dễ dàng thông qua các thông số được đặt tên. Nhưng điều đó giả định bạn biết tên khi bạn gõ lệnh. Nếu bạn muốn chỉ định động tên cột, thì bạn cũng cần phải xây dựng đối số đã đặt tên.

Phiên bản mới nhất của dplyr (0.7) thực hiện việc này bằng cách sử dụng := để gán động tên thông số. Bạn có thể viết chức năng của mình là:

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

Để biết thêm thông tin, hãy xem tài liệu có sẵn biểu mẫu vignette("programming", "dplyr").

Phiên bản trước đó của dplyr (> = 0.3 < 0.7), khuyến khích sử dụng các lựa chọn "đánh giá tiêu chuẩn" cho nhiều chức năng. Xem phần đánh giá phi tiêu chuẩn để biết thêm thông tin (vignette("nse")).

Vì vậy, đây, câu trả lời là sử dụng mutate_() hơn mutate() và làm:

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

phiên bản cũ của dplyr

Chú giải này cũng có thể trong các phiên bản cũ của dplyr đã tồn tại khi các câu hỏi ban đầu được đặt ra. Nó đòi hỏi sử dụng cẩn thận của 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

Cảm ơn bạn, điều đó rất hữu ích. btw, tôi luôn tạo ra những biến thực sự ấn tượng. –

+18

Hehe. đó có lẽ là một trong những lỗi chính tả yêu thích của tôi mà tôi đã thực hiện trong một thời gian. Tôi nghĩ tôi sẽ bỏ nó. – MrFlick

+1

'do.call()' có thể không làm những gì bạn nghĩ: http://rpubs.com/hadley/do-call2. Xem thêm họa tiết nse trong phiên bản dev của dplyr. – hadley

4

Tôi cũng thêm một câu trả lời rằng augments này một chút vì tôi đến cụm từ này khi tìm kiếm một câu trả lời, và điều này đã gần như những gì tôi cần, nhưng tôi cần thêm một chút nữa, mà tôi đã nhận được qua câu trả lời của @MrFlik và các họa tiết R lazyeval.

Tôi muốn tạo một hàm có thể lấy một khung dữ liệu và một vectơ tên cột (dưới dạng chuỗi) mà tôi muốn được chuyển đổi từ chuỗi thành đối tượng Ngày tháng. Tôi không thể tìm ra cách làm cho as.Date() lấy một đối số là một chuỗi và chuyển đổi nó thành một cột, vì vậy tôi đã làm nó như hình dưới đây.

Dưới đây là cách tôi đã thực hiện điều này thông qua biến thể SE (mutate_()) và đối số .dots. Các chỉ trích làm cho điều này tốt hơn được hoan nghênh.

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

Đây là phiên bản khác và được cho là đơn giản hơn một chút.

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

Trong khi tôi thích sử dụng dplyr để sử dụng tương tác, tôi thấy nó vô cùng khó khăn để làm điều này bằng dplyr bởi vì bạn phải đi qua hoops để sử dụng lazyeval :: interp(), setNames vv cách giải quyết. Đây là một phiên bản đơn giản hơn bằng cách sử dụng cơ sở R, trong đó nó có vẻ trực quan hơn, với tôi ít nhất, để đặt vòng lặp bên trong hàm, và mở rộng giải pháp của @ 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, mặc dù tôi vẫn sử dụng 'dplyr' rất nhiều trong cài đặt không tương tác, sử dụng nó với đầu vào variabel bên trong một hàm sử dụng cú pháp rất clunky. –

17

Trong phiên bản mới của dplyr (0.6.0 chờ vào tháng Tư năm 2017), chúng tôi cũng có thể làm một bài tập (:=) và vượt qua các biến như tên cột bằng cách unquoting (!!) để không đánh giá nó

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

Kiểm tra đầu ra dựa trên @ MrFlick của multipetal áp dụng trên 'iris1'

identical(iris1, iris2) 
#[1] TRUE 
Các vấn đề liên quan