2014-09-23 22 views
61

Chcę użyć dplyr'smutate(), aby utworzyć wiele nowych kolumn w ramce danych. Nazwy kolumn i ich zawartość powinny być generowane dynamicznie.dplyr - mutate: użyj nazw zmiennych dynamicznych

Przykładowe dane z tęczówki:

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

I utworzeniu funkcji mutować moje nowe kolumny z Petal.Width zmiennej:

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

Teraz tworzę pętlę budować moje kolumny:

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

Jednak ponieważ mutate uważa, że ​​nazwa_zmiennej jest literalną nazwą zmiennej, pętla tworzy tylko jedną nową zmienną (nazywaną nazwą varname) zamiast czterech (zwaną płatkiem.2 - płatek.5).

Jak uzyskać mutate(), aby użyć mojej nazwy dynamicznej jako nazwy zmiennej?

+1

nie będę nalegał na mutacie, pytam, czy to możliwe. Może to tylko mała sztuczka, której nie znam. Jeśli jest inny sposób, posłuchajmy tego. –

+0

wierzę, że jest [miejsce do oglądania] (https://github.com/hadley/dplyr/issues/352#issuecomment-53829862) w pakiecie [lazyeval] (https://github.com/hadley/lazyeval) – baptiste

+0

W tym momencie 'dplyr' ma [całą winietę na nietypową ocenę] (https://cran.r-project.org/web/packages/dplyr/vignettes/nse.html) – Gregor

Odpowiedz

73

Ponieważ dramatycznie budujesz nazwę zmiennej jako wartość znaku, bardziej sensowne jest przypisywanie przy użyciu standardowego indeksowania danych.frame, które pozwala na wartości znaków dla nazw kolumn. Na przykład:

Funkcja mutate sprawia, że ​​bardzo łatwo wymienić nowe kolumny za pośrednictwem nazwanych parametrów. Ale to zakłada, że ​​znasz nazwę podczas wpisywania polecenia. Jeśli chcesz dynamicznie określać nazwę kolumny, musisz również zbudować nazwany argument.

Najnowsza wersja dplyr (0.7) robi to, używając nazwy := do dynamicznego przypisywania nazw parametrów. Możesz napisać swoją funkcję jako:

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

Aby uzyskać więcej informacji, zapoznaj się z dokumentacją dostępną formą vignette("programming", "dplyr").

Nieco wcześniejsza wersja dplyr (> = 0.3 < 0.7), zachęciła do stosowania "standardowych ocen" alternatyw dla wielu funkcji. Zobacz winietę niestandardowej oceny, aby uzyskać więcej informacji (vignette("nse")).

Więc odpowiedź jest użycie mutate_() zamiast mutate() i zrobić:

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

Starsze wersje dplyr

Uwaga Jest to również możliwe w starszych wersjach dplyr które istniały, gdy pytanie zostało pierwotnie postawione. Wymaga to starannego wykorzystania quote i 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

Dziękuję, to jest pomocne. btw, zawsze tworzę naprawdę dramatyczne zmienne. –

+18

Hehe. to prawdopodobnie jedna z moich ulubionych literówek, którą stworzyłem od jakiegoś czasu. Myślę, że go opuszczę. – MrFlick

+1

'do.call()' prawdopodobnie nie robi tego, co myślisz: http://rpubs.com/hadley/do-call2. Zobacz także nse winietę w wersji dev dplyr. – hadley

4

Ja również dodanie odpowiedź, że zwiększa to trochę, bo doszedłem do tego wpisu, szukając odpowiedzi, a miało to prawie to, co potrzebne, ale Potrzebowałem trochę więcej, co otrzymałem za pośrednictwem odpowiedzi @MrFlika i R lazyeval winiet.

Chciałem utworzyć funkcję, która mogłaby pobrać ramkę danych i wektor nazw kolumn (jako ciągi), które chcę przekształcić z ciągu znaków w obiekt Date. Nie mogłem wymyślić, jak uczynić as.Date() przyjąć argument, który jest ciągiem znaków i przekonwertować go do kolumny, więc zrobiłem to, jak pokazano poniżej.

Oto jak to zrobiłem za pomocą mutacji SE (mutate_()) i argumentu .dots. Krytyka, która czyni to lepszym, jest mile widziana.

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

Oto kolejna wersja i prawdopodobnie jest nieco prostsza.

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

Chociaż lubię użyciu dplyr do interaktywnego wykorzystania uważam, że to niezwykle trudne to zrobić przy użyciu dplyr bo trzeba przejść przez obręcze, aby korzystać lazyeval :: interp() setNames itd obejścia.

Oto prostsza wersja z wykorzystaniem bazy R, w której wydaje mi się bardziej intuicyjna, przynajmniej dla umieszczenia pętli wewnątrz funkcji, i która rozszerza rozwiązanie @ 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, chociaż nadal używam 'dplyr' dużo w nieinteraktywnych ustawieniach, używając go z wejście zmienne wewnątrz funkcji używa bardzo nieporęcznej składni. –

17

W nowym wydaniu dplyr (0.6.0 czeka w kwietniu 2017), możemy również wykonać zadanie (:=) i przekazać zmienne jak nazwy kolumn przez unquoting (!!), aby nie oceniać to

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

Sprawdzanie wyjście na podstawie @ MrFlick na multipetal stosowane na „iris1”

identical(iris1, iris2) 
#[1] TRUE 
Powiązane problemy