2015-02-26 15 views
12

ich eine data.frame wie diese haben (die reale Datensatz hat viel mehr Zeilen und Spalten)Performing dplyr auf Teilmenge von Spalten mutieren

set.seed(15) 
dd <- data.frame(id=letters[1:4], matrix(runif(5*4), nrow=4)) 

# id  X1  X2  X3  X4  X5 
# 1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437 
# 2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670 
# 3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871 
# 4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125 

Ich mag wäre in der Lage sein, eine dplyr Erklärung zu schreiben, in dem Ich kann eine Teilmenge von Spalten auswählen und mutieren. (Ich versuche etwas Ähnliches wie die Verwendung von .SDcols in data.table).

Für ein vereinfachtes Beispiel, hier ist die Funktion Ich möchte in der Lage sein zu schreiben, Spalten für die Summen und Mittel der geraden "X" Spalten hinzufügen, während alle anderen Spalten erhalten. Die gewünschte Ausgabe unter Verwendung der Basis R ist

(cols<-paste0("X", c(2,4))) 
# [1] "X2" "X4" 
cbind(dd,evensum=rowSums(dd[,cols]),evenmean=rowMeans(dd[,cols])) 

# id  X1  X2  X3  X4  X5 evensum evenmean 
# 1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437 0.8761623 0.4380811 
# 2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670 1.6954878 0.8477439 
# 3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871 1.6775071 0.8387535 
# 4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125 1.0957535 0.5478768 

aber ich wollte eine dplyr-ähnliche Kette verwenden, um das Gleiche zu tun. Im allgemeinen Fall würde ich gerne eine der select() Hilfsfunktionen wie starts_with, ends_with, usw. und jede Funktion verwenden können. Hier ist, was habe ich versucht,

library(dplyr) 
partial_mutate1 <- function(x, colspec, ...) { 
    select_(x, .dots=list(lazyeval::lazy(colspec))) %>% 
    transmute_(.dots=lazyeval::lazy_dots(...)) %>% 
    cbind(x,.) 
} 

dd %>% partial_mutate1(num_range("X", c(2,4)), 
    evensum=rowSums(.), evenmean=rowMeans(.)) 

jedoch Dies wirft einen Fehler, der

Error in rowSums(.) : 'x' must be numeric 

sagt Welche zu sein scheint, weil . auf die gesamte date.frame zu beziehen scheint eher als die ausgewählte Teilmenge. (gleicher Fehler wie rowSums(dd)). Beachten Sie jedoch, dass dies die gewünschte Ausgabe erzeugt

partial_mutate2 <- function(x, colspec) { 
    select_(x, .dots=list(lazyeval::lazy(colspec))) %>% 
    transmute(evensum=rowSums(.), evenmean=rowMeans(.)) %>% 
    cbind(x,.) 
} 
dd %>% partial_mutate2(seq(2,ncol(dd),2)) 

Ich vermute, das ist eine Art von Umweltproblem? Irgendwelche Vorschläge, wie Sie die Argumente an übergeben, so dass die . Werte aus dem Datensatz "select() - ed" korrekt übernimmt?

+0

Eine hässliche Art und Weise wäre: 'dd%>% wählen (X2, X4)%>% mutieren (evensum = rowSums(), evenmean = rowMeans()..)%>% wählen (- X2, -X4)%>% cbind (., Dd) ' –

+0

Ich vermute das Problem ist, dass der Versuch, die SE ist messing mit '%>%'. Mit anderen Worten, mit 'rowMeans (.)', Das in '.dots' eingeschlossen ist, kann '%>%' nicht wissen, dass es die Daten dort auch ersetzen sollte. Dies ist nur eine Vermutung. – BrodieG

+0

Ich denke, du hast Recht @ BrodieG.Nach ein bisschen mehr Graben ist das eher ein magrtrit Problem als ein dplyr Problem. Zum Beispiel: 'muate (dd [, - 1], Summen = rowSums (.))' Funktioniert nicht ("object '.' Not found"). Das '.' Symbol ist also nicht speziell für' dplyr'. Der Versuch, eine Funktion über mehrere Spalten hinweg zu verwenden, scheint die falsche Idee zu sein. Ich denke, ich sollte die Daten zuerst in ein "ordentliches" Format umformen. – MrFlick

Antwort

7

Fehle ich etwas oder würde wie diese Arbeit zu erwarten:

cols <- paste0("X", c(2,4)) 
dd %>% mutate(evensum = rowSums(.[cols]), evenmean = rowMeans(.[cols])) 
# id  X1  X2  X3  X4  X5 evensum evenmean 
#1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437 0.8761623 0.4380811 
#2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670 1.6954878 0.8477439 
#3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871 1.6775071 0.8387535 
#4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125 1.0957535 0.5478768 

Oder suchen Sie speziell für eine benutzerdefinierte Funktion, dies zu tun?


nicht genau das, was Sie suchen, aber wenn Sie wollen, dass es in einem Rohr tun Sie select explizit innerhalb mutate wie diese verwenden:

dd %>% mutate(xy = select(., num_range("X", c(2,4))) %>% rowSums) 
# id  X1  X2  X3  X4  X5  xy 
#1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437 0.8761623 
#2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670 1.6954878 
#3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871 1.6775071 
#4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125 1.0957535 

Allerdings ist es ein wenig komplizierter, wenn Sie möchten mehrere Funktionen anwenden. Sie könnten eine Hilfsfunktion entlang der Linien verwenden (..not gründlich getestet ..):

f <- function(x, ...) { 
    n <- nrow(x) 
    x <- lapply(list(...), function(y) if (length(y) == 1L) rep(y, n) else y) 
    matrix(unlist(x), nrow = n, byrow = FALSE) 
} 

Und dann gilt es wie folgt aus:

dd %>% mutate(xy = select(., num_range("X", c(2,4))) %>% f(., rowSums(.), max(.))) 
# id  X1  X2  X3  X4  X5  xy.1  xy.2 
#1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437 0.8761623 0.9888592 
#2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670 1.6954878 0.9888592 
#3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871 1.6775071 0.9888592 
#4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125 1.0957535 0.9888592 
+0

Der Teil, der fehlt, ist, dass ich in der Lage wäre, die Spaltenauswahlfunktionen zu verwenden, die '? Select' anbietet, wie zum Beispiel starts_with, ends_with usw. Diese funktionieren nicht" schön "außerhalb von' select() '. – MrFlick

+0

@MrFlick, ah okay .. –

+1

Natürlich könnte ich verwenden 'cols <- dplyr ::: num_range (Namen (dd)," X ", c (2,4))'. Die Sache ist, es wäre kein Teil der Kette und es würde auch brechen, wenn Spaltenreihenfolgen in der Kette geändert werden, nachdem die Spaltenindizes berechnet wurden. Es wäre besser, es "on demand" wenn möglich zu tun – MrFlick

2

A number-of-Spalten Agnostiker Ansatz dplyr:

dd %>% 
    select(-id) %>% 
    mutate(evensum = rowSums(.[,1:length(.[1,])%%2==0]), 
     evenmean = rowMeans(.[,1:length(.[1,])%%2==0])) %>% 
    cbind(id=dd[,1],.) 

    id  X1  X2  X3  X4  X5 evensum evenmean 
1 a 0.6021140 0.3670719 0.6872308 0.5090904 0.4474437 0.8761623 0.4380812 
2 b 0.1950439 0.9888592 0.8314290 0.7066286 0.9646670 1.6954878 0.8477439 
3 c 0.9664587 0.8151934 0.1046694 0.8623137 0.1411871 1.6775071 0.8387535 
4 d 0.6509055 0.2539684 0.6461509 0.8417851 0.7767125 1.0957535 0.5478767 
Verwandte Themen