2014-09-23 22 views
61

voglio usaremutate() per creare più nuove colonne di dplyr in un frame di dati. I nomi delle colonne e i loro contenuti dovrebbero essere generati dinamicamente.dplyr - mutare: utilizzare nomi di variabili dinamiche

dati di esempio da Iris:

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

ho creato una funzione di mutare le mie nuove colonne dalla Petal.Width variabile:

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

Ora creo un ciclo di costruire le mie colonne:

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

Tuttavia, poiché mutate pensa che varname sia un nome di variabile letterale, il ciclo crea solo una nuova variabile (chiamata varname) anziché quattro (chiamata petal.2 - petal.5).

Come posso ottenere mutate() di usare il mio nome dinamico come nome della variabile?

+1

non sto insistendo su mutate, sto chiedendo se è possibile. Forse è solo un piccolo trucco che non conosco. Se c'è un altro modo, ascoltiamolo. –

+0

credo che ci sia [uno spazio da guardare] (https://github.com/hadley/dplyr/issues/352#issuecomment-53829862) nel [pacchetto lazyeval] (https://github.com/hadley/lazyeval) – baptiste

+0

a questo punto, 'dplyr' ha un [intera vignetta sulla valutazione non standard] (https://cran.r-project.org/web/packages/dplyr/vignettes/nse.html) – Gregor

risposta

73

Dal momento che si sta drammaticamente costruendo un nome di variabile come un valore di carattere, ha più senso fare assegnazione utilizzando l'indicizzazione data.frame standard che permette di valori dei caratteri per i nomi di colonna. Per esempio:

La funzione mutate rende molto facile per citarne nuove colonne tramite parametri denominati. Ma questo presuppone che tu conosca il nome quando digiti il ​​comando. Se si desidera specificare in modo dinamico il nome della colonna, è necessario creare anche l'argomento con nome.

L'ultima versione di dplyr (0.7) lo utilizza utilizzando := per assegnare dinamicamente i nomi dei parametri. È possibile scrivere la funzione come:

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

Per ulteriori informazioni, vedere la documentazione disponibile sotto forma vignette("programming", "dplyr").

Una versione leggermente precedente di dplyr (> = 0.3 < 0.7), ha incoraggiato l'uso di alternative di "valutazione standard" a molte delle funzioni. Vedere la vignetta di valutazione non standard per ulteriori informazioni (vignette("nse")).

Quindi, ecco, la risposta è quella di utilizzare mutate_() piuttosto che mutate() e fare:

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

versioni precedenti di dplyr

notare che questo è possibile anche nelle versioni precedenti di dplyr esistenti al momento dell'avvio la domanda era originariamente posta Si richiede un uso attento di quote e 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) 
} 
+15

Grazie, è utile. btw, creo sempre variabili davvero drammatiche. –

+18

Hehe. quello è probabilmente uno dei miei refusi preferiti che ho fatto in un istante. Penso che lo lascerò. – MrFlick

+1

'do.call()' probabilmente non fa quello che pensi lo fa: http://rpubs.com/hadley/do-call2. Vedi anche la nse vignette nella versione dev di dplyr. – hadley

4

sto aggiungendo anche una risposta che aumenta questo un po 'perché sono arrivato a questa voce durante la ricerca di una risposta, e questo ha avuto quasi quello che mi serviva, ma io avevo bisogno di un altro po ', che ho ottenuto tramite la risposta di @MrFlik e le vignette di Razy.

ho voluto fare una funzione che potrebbe assumere un dataframe e un vettore di nomi di colonne (come stringhe) che voglio essere convertito da una stringa in un oggetto Date. Non riuscivo a capire come fare as.Date() prendere un argomento che è una stringa e convertirlo in una colonna, quindi ho fatto come illustrato di seguito.

Di seguito è riportato il modo in cui ho eseguito questa operazione tramite muting SE (mutate_()) e l'argomento .dots. Le critiche che lo rendono migliore sono le benvenute.

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

Ecco un'altra versione, ed è probabilmente un po 'più semplice.

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

Mentre mi piace usare dplyr per l'uso interattivo, trovo straordinariamente difficile da farlo usando dplyr perché si deve passare attraverso i cerchi di utilizzare lazyeval :: interp(), setNames, ecc soluzioni alternative.

Ecco una versione più semplice che utilizza la base R, in cui sembra più intuitivo, almeno per me, inserire il ciclo all'interno della funzione e che estende la soluzione di @ 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, anche se uso ancora 'dplyr' molto in impostazioni non interattive, usandolo con l'input variabel all'interno di una funzione utilizza una sintassi molto clunky. –

17

Nella nuova release di dplyr (0.6.0 attesa nel mese di aprile 2017), possiamo anche fare un incarico (:=) e passare variabili come nomi di colonna si toglie la quotatura (!!) di non valutarla

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

Controllo l'uscita in base a @ MrFlick di multipetal applicato su 'col IRIS1'

identical(iris1, iris2) 
#[1] TRUE 
Problemi correlati