2015-05-25 3 views
6

Hier ist mein Problem:dplyr Fassen wir zusammen: Erstellen von Variablen aus dem Namen Vektor

ich eine Funktion verwende, die einen benannten Vektor zurückgibt. Hier ist ein Spielzeug Beispiel:

toy_fn <- function(x) { 
    y <- c(mean(x), sum(x), median(x), sd(x)) 
    names(y) <- c("Right", "Wrong", "Unanswered", "Invalid") 
    y 
} 

Ich verwende group_by in dplyr diese Funktion gelten für jede Gruppe (typische Split-apply-kombinieren). So, hier ist mein Spielzeug data.frame:

set.seed(1234567) 
toy_df <- data.frame(id = 1:1000, 
        group = sample(letters, 1000, replace = TRUE), 
        value = runif(1000)) 

Und hier ist das Ergebnis, das ich für bin mit dem Ziel:

toy_summary <- 
    toy_df %>% 
    group_by(group) %>% 
    summarize(Right = toy_fn(value)["Right"], 
       Wrong = toy_fn(value)["Wrong"], 
       Unanswered = toy_fn(value)["Unanswered"], 
       Invalid = toy_fn(value)["Invalid"]) 

> toy_summary 
Source: local data frame [26 x 5] 

    group  Right Wrong Unanswered Invalid 
1  a 0.5038394 20.15358 0.5905526 0.2846468 
2  b 0.5048040 15.64892 0.5163702 0.2994544 
3  c 0.5029442 21.62660 0.5072733 0.2465612 
4  d 0.5124601 14.86134 0.5382463 0.2681955 
5  e 0.4649483 17.66804 0.4426197 0.3075080 
6  f 0.5622644 12.36982 0.6330269 0.2850609 
7  g 0.4675324 14.96104 0.4692404 0.2746589 

Es funktioniert! Aber es ist einfach nicht cool, die gleiche Funktion viermal aufzurufen. Ich möchte lieber dplyr, um den benannten Vektor zu erhalten und eine neue Variable für jedes Element im Vektor zu erstellen. So etwas wie das:

Dies funktioniert leider nicht, weil "Fehler: einen einzelnen Wert erwarten".

Ich dachte, ok, lassen Sie uns einfach den Vektor in eine data.frame mit data.frame(as.list(x)) konvertieren. Aber das funktioniert auch nicht. Ich habe viele Dinge ausprobiert, aber ich konnte dplyr nicht glauben, dass es tatsächlich einen einzigen Wert (Beobachtung) für 4 verschiedene Variablen erhält. Gibt es eine Möglichkeit, dies zu erkennen?

Antwort

2

Sie können auch versuchen, diese mit do():

toy_df %>% 
    group_by(group) %>% 
    do(res = toy_fn(.$value)) 
+1

es scheint nicht zu funktionieren, obwohl. –

+0

Ich testete es auf meinem Computer - es funktioniert, der resultierende Datenrahmen braucht jedoch einige Parsing. –

+0

was für ein Parsing ?, ..., ich konnte es mir nicht genau ansehen, weil ich es in meinem Handy überprüft habe. –

3

Dies ist keine dplyr Lösung, aber wenn Sie Rohre mögen:

library(magrittr) 

toy_summary <- 
    toy_df %>% 
    split(.$group) %>% 
    lapply(function(x) toy_fn(x$value)) %>% 
    do.call(rbind, .) 

# > head(toy_summary) 
#   Right Wrong Unanswered Invalid 
# a 0.5038394 20.15358 0.5905526 0.2846468 
# b 0.5048040 15.64892 0.5163702 0.2994544 
# c 0.5029442 21.62660 0.5072733 0.2465612 
# d 0.5124601 14.86134 0.5382463 0.2681955 
# e 0.4649483 17.66804 0.4426197 0.3075080 
# f 0.5622644 12.36982 0.6330269 0.2850609  
+0

Vielen Dank. Ich mag es sehr. Ich war auf der Suche nach einer dplyr-basierten Lösung, weil ich diese Funktion nach einer Reihe von 'full_join', Filterung und anderen Datenkonflikten mit dplyr aufrufen muss. So scheint es natürlich, dplyr auch zu verwenden. Aber das ist cool und funktioniert perfekt. –

+0

@HernandoCasas Sie können dplyr-Funktionen vor oder nach dieser Kette kombinieren (weil die Eingabe und Ausgabe ein data.frame ist). Aber Sie können es nicht zwischen der Sequenz verwenden. – bergant

5

Eine mögliche Lösung ist dplyrSE Fähigkeiten zu nutzen. Beispielsweise legen Sie fungieren als Dann

dots <- setNames(list( ~ mean(value), 
         ~ sum(value), 
         ~ median(value), 
         ~ sd(value)), 
       c("Right", "Wrong", "Unanswered", "Invalid")) 

folgt, können Sie summarize_ verwenden (mit einem _) als

toy_df %>% 
    group_by(group) %>% 
    summarize_(.dots = dots) 
# Source: local data table [26 x 5] 
# 
# group  Right Wrong Unanswered Invalid 
# 1  o 0.4490776 17.51403 0.4012057 0.2749956 
# 2  s 0.5079569 15.23871 0.4663852 0.2555774 
# 3  x 0.4620649 14.78608 0.4475117 0.2894502 
# 4  a 0.5038394 20.15358 0.5905526 0.2846468 
# 5  t 0.5041168 24.19761 0.5330790 0.3171022 
# 6  m 0.4806628 21.14917 0.4805273 0.2825026 
# 7  c 0.5029442 21.62660 0.5072733 0.2465612 
# 8  w 0.4932484 17.75694 0.4891746 0.3309680 
# 9  q 0.5350707 22.47297 0.5608505 0.2749941 
# 10  g 0.4675324 14.96104 0.4692404 0.2746589 
# .. ...  ...  ...  ...  ... 

folgt Obwohl es schön aussieht, da hier ein großer Fang ist. Sie müssen die Spalte wissen, die Sie a priori (value) bei der Einrichtung der Funktion arbeiten, so dass es auf einen anderen Spaltennamen nicht funktioniert, wenn Sie dots nicht ordnungsgemäß einrichten.


Als Bonus ist hier eine einfache Lösung data.table mit Ihrer ursprünglichen Funktion

library(data.table) 
setDT(toy_df)[, as.list(toy_fn(value)), by = group] 
#  group  Right Wrong Unanswered Invalid 
# 1:  o 0.4490776 17.51403 0.4012057 0.2749956 
# 2:  s 0.5079569 15.23871 0.4663852 0.2555774 
# 3:  x 0.4620649 14.78608 0.4475117 0.2894502 
# 4:  a 0.5038394 20.15358 0.5905526 0.2846468 
# 5:  t 0.5041168 24.19761 0.5330790 0.3171022 
# 6:  m 0.4806628 21.14917 0.4805273 0.2825026 
# 7:  c 0.5029442 21.62660 0.5072733 0.2465612 
# 8:  w 0.4932484 17.75694 0.4891746 0.3309680 
# 9:  q 0.5350707 22.47297 0.5608505 0.2749941 
# 10:  g 0.4675324 14.96104 0.4692404 0.2746589 
#... 
+0

Schön mit data.table. Die dplyr-basierte Lösung, die Sie vorschlagen, funktioniert nicht für mich, weil ich die Funktion nicht ändern kann. Ich mag es sehr viel Data.table Weise, obwohl ich nach einer dplyr-basierten Lösung suchte, weil ich diese Funktion nach einem Bündel von full_join, Filterung und anderen Datengerangel, die mit dplyr getan werden, aufrufen muss. So scheint es natürlich, dplyr auch zu verwenden. –

+0

Was meinen Sie mit "Ich kann die Funktion nicht ändern"? –

+0

Ich meinte, dass ich die Funktion nicht wie vorgeschlagen einstellen kann, weil Sie ein Objekt mit einer Formel für jeden der Rückgabewerte meiner Beispielfunktion (toy_fn) erstellen. Dies war jedoch nur ein Beispiel und meine reale Anwendung tut es nicht beinhalten die Berechnung der Mittelwert, Summe, Median und SD. Stattdessen ist es eine Funktion, die die Daten mit Referenzwerten in einer anderen Datenbank vergleicht (RODBC verwendet, um eine Verbindung zur anderen Datenbank herzustellen und aktualisierte Referenzwerte zu erhalten) und vier Werte (in einem benannten Vektor) zurückgibt, die das Ergebnis des Vergleichs angeben Rufen Sie eine einzelne Funktion, um jeden dieser Werte zu erhalten –

3
mit

Anscheinend gibt es ein Problem bei der Verwendung von median (nicht sicher, was dort vor sich geht), aber abgesehen davon, dass man normalerweise verwenden ein Ansatz wie der folgende mit summarise_each, um mehrere Funktionen anzuwenden.Beachten Sie, dass Sie die Namen der resultierenden Spalten unter Verwendung eines benannten Vektor als Eingabe für funs_() angeben:

x <- c(Right = "mean", Wrong = "sd", Unanswered = "sum") 

toy_df %>% 
    group_by(group) %>% 
    summarise_each(funs_(x), value) 

#Source: local data frame [26 x 4] 
# 
# group  Right  Wrong Unanswered 
#1  a 0.5038394 0.2846468 20.15358 
#2  b 0.5048040 0.2994544 15.64892 
#3  c 0.5029442 0.2465612 21.62660 
#4  d 0.5124601 0.2681955 14.86134 
#5  e 0.4649483 0.3075080 17.66804 
#6  f 0.5622644 0.2850609 12.36982 
#7  g 0.4675324 0.2746589 14.96104 
#8  h 0.4921506 0.2879830 21.16248 
#9  i 0.5443600 0.2945428 22.31876 
#10  j 0.5276048 0.3236814 20.57659 
#.. ...  ...  ...  ... 
+2

Ich glaube nicht, dass Sie 'funs_' hier brauchen. Ein "Zeichenvektor von Funktionsnamen" sollte ausreichen. Siehe z.B. das 'summarise_each (c (" min "," max ")) Beispiel. Seltsamerweise mit "Median". – Henrik

+0

Guter Punkt, @ Henrik –

+0

Danke. Für dieses spezielle Beispiel funktioniert es sehr gut. Aber in meiner realen Anwendung kann ich für jeden der Werte, die ich berechnen muss, keine andere Funktion aufrufen. Es ist sowieso meine Schuld. Ich war nicht klar genug, dass die Funktion, die ich in die Post setzte, nur ein reproduzierbares Beispiel war, aber die Funktion, die ich für jede Gruppe aufrufen muss, ist viel komplexer und nicht nur Anrufe zu Mittelwert, Median usw. Auch Es ist eine Funktion, die ich nicht ändern kann. –

1

unter Verwendung der Sequenz von list(as_tibble(as.list(...)) gefolgt von einem von tidyr macht den Trick

toy_summary2 <- toy_df %>% group_by(group) %>% 
summarize(Col = list(as_tibble(as.list(toy_fn(value))))) %>% unnest() 
Verwandte Themen