2015-06-10 12 views
21

In Bezug auf this question habe ich versucht, die einfachste Möglichkeit zu finden, eine Liste von Funktionen auf eine Liste von Werten anzuwenden. Grundsätzlich ein verschachteltes lapply. Zum Beispiel, hier wenden wir sd und mean setzen trees in Daten gebaut:Liste der Funktionen auf die Liste der Werte anwenden

funs <- list(sd=sd, mean=mean) 
sapply(funs, function(x) sapply(trees, x)) 

zu erhalten:

   sd  mean 
Girth 3.138139 13.24839 
Height 6.371813 76.00000 
Volume 16.437846 30.17097 

Aber ich hatte gehofft, dass die innere function zu vermeiden und haben so etwas wie:

sapply(funs, sapply, X=trees) 

was nicht funktioniert, weil X die erste sapply statt der zweiten entspricht. Wir können es mit functional::Curry:

sapply(funs, Curry(sapply, X=trees)) 

aber ich hatte gehofft, vielleicht gibt es eine kluge Art und Weise war dies mit Lage- und Namensabgleich zu tun, dass ich fehle.

+5

Hadley schrieb ein ganzes Kapitel zu diesem Thema: http://adv-r.had.co.nz/Funktions-programmieren.html#listen-von-Funktionen, da ich nicht schlauer bin als er, weiß ich kein besserer Weg, dies zu tun – grrgrrbla

+0

Nicht einfacher, aber nett, wenn Sie ein sauberes data.frame am Ende wünschen: 'Bibliothek (purrr); map_df (Spaß, ~ map_df (Bäume, .x), .id = 'Statistik') ' – alistaire

Antwort

18

Seit mapply Verwendung Auslassungs ... Vektoren (atomics oder Listen) und kein benanntes Argument (X) passieren, wie in sapply, lapply, etc ... Sie den Parameter nicht nennen müssen X = trees wenn Sie mapply stattdessen verwenden von sapply:

funs <- list(sd = sd, mean = mean) 

x <- sapply(funs, function(x) sapply(trees, x)) 

y <- sapply(funs, mapply, trees) 

> y 
       sd  mean 
Girth 3.138139 13.24839 
Height 6.371813 76.00000 
Volume 16.437846 30.17097 
> identical(x, y) 
[1] TRUE 

Sie waren ein Buchstabe nah, um zu bekommen, was Sie suchten! :)

Beachten Sie, dass ich eine Liste für funs verwendet, weil ich einen Datenrahmen von Funktionen nicht erstellen kann, habe ich einen Fehler.

> R.version.string 
[1] "R version 3.1.3 (2015-03-09)" 
+3

Sehr schlau, werde das definitiv in Zukunft verwenden; Ich denke, das Hauptmerkmal ist, dass 'mapply' das Funktionsargument als erstes Argument akzeptiert, also funktioniert das alles. – BrodieG

13

Sie werden im Grunde eine anonyme Funktion irgendeiner Art benötigen, da es keine andere Möglichkeit geben würde, benannte Parameter zu den zwei verschiedenen sapply Aufrufen zu unterscheiden. Sie haben bereits eine explizite anonyme Funktion und die Methode Curry angezeigt. Sie könnten auch magrittr

library(magrittr) 
sapply(funs, . %>% sapply(trees, .)) 
# or .. funs %>% sapply(. %>% sapply(trees, .)) 

verwenden, aber der Punkt ist, Sie brauchen etwas dort die Spaltung zu tun. Das "Problem" ist, dass an lapply sendet, das ist ein internal function, der bestimmt scheint, die sich ändernden Werte als den Anfang des Funktionsaufrufs zu platzieren. Sie benötigen etwas, um die Parameter neu zu ordnen, und aufgrund der identischen Mengen von Parameternamen ist es nicht möglich, diese ohne eine Hilfsfunktion auseinander zu halten, um sich um die Disambiguierung zu kümmern.

Die Funktion ermöglicht es Ihnen, eine Liste an "MoreArgs" zu übergeben, die eine Möglichkeit bietet, den Konflikt um benannte Parameter zu umgehen. Dies soll zwischen den Elementen, über die Sie vektorisieren möchten, und denen, die fixiert sind, aufgeteilt werden. So können Sie

tun
mapply(sapply, funs, MoreArgs=list(X=trees)) 
#    sd  mean 
# Girth 3.138139 13.24839 
# Height 6.371813 76.00000 
# Volume 16.437846 30.17097 
+1

Schönes mit' MoreArgs'. Ich denke, 'magrtrtr' könnte' Spaß%>% sapply (.%>% Sapply (X = Bäume)) '' sein? Ich hatte definitiv ein bisschen eine Doppelpackung, da ich das '.' als erstes Element in der Pipe sah. – BrodieG

+1

Ja, ich habe das auch hinzugefügt, obwohl ich denke, dass die erste Version klarer ist. Um ehrlich zu sein, denke ich, dass der beste Weg ist, die explizite anonyme Funktion wie beim ersten Mal zu benutzen: 'sapply (Spaß, Funktion (x) sapply (Bäume, x))' – MrFlick

+0

vereinbart; Ich bearbeitete noch einmal, um ein zusätzliches '.' zu entfernen, aber nicht 100% sicher, dass ich meiner eigenen Logik folge ... – BrodieG

0

Obwohl nicht so erbaulich noch so elegant wie die Lösung von @ Floo0 präsentiert, hier noch ein anderer nehmen mit tidyr und dplyr:

library(dplyr) 
library(tidyr) 

fns <- funs(sd = sd, mean = mean) 
trees %>% 
    gather(property, value, everything()) %>% 
    group_by(property) %>% 
    summarise_all(fns) 

# A tibble: 3 x 3 
# property  sd  mean 
#  <chr>  <dbl> <dbl> 
# 1 Girth 3.138139 13.24839 
# 2 Height 6.371813 76.00000 
# 3 Volume 16.437846 30.17097 

Diese Folge von Operationen hat einen anständigen Job Absicht der Signalisierung , auf Kosten von extra Ausführlichkeit.

5

Ein weiterer Ansatz purrr Verwendung wäre:

require(purrr) 

funs <- list(sd=sd, mean=mean) 
trees %>% map_df(~invoke_map(funs, ,.), .id="id") 

Wichtige: Beachten Sie die leere zweite Argument von invoke_map von Position anzupassen. Siehe ?purrr::invoke_map Beispiele.

, die Sie gibt:

Source: local data frame [3 x 3] 

     id  sd  mean 
    <chr>  <dbl> <dbl> 
1 Girth 3.138139 13.24839 
2 Height 6.371813 76.00000 
3 Volume 16.437846 30.17097 

Statt rownames dieser Ansatz gibt Ihnen eine Spalte id enthält die ursprünglichen Spalten.

+0

Bei der Verwendung von purrr 0.2.2 (und möglicherweise auch früheren Versionen, die ich nicht überprüft habe) ist es erforderlich, 'invoke_map_df()' statt 'invoke_map()' zu verwenden, um das Ergebnis anzuzeigen. – egnha

+0

@egnha, das ist seltsam. Für mich funktioniert es gut mit 'purrr_0.2.2'. Die Verwendung von 'invoke_map_df' führt zu' Fehler: Objekt kann nicht in einen Datenrahmen konvertiert werden' ... Welche Version von R benutzen Sie? – Rentrop

+0

Das ist rätselhaft. Ich benutze R 3.3.0; ran Code in einer neuen Sitzung mit nur purrr (und keine Init-Dateien von R geladen). Moralisch ist 'invoke_map_df' die richtige' invoke_map * ', die angewendet wird (und auf meinem Rechner korrekt funktioniert), da' map_df' einen Datenrahmen durch Binden von Zeilen erzeugt (es sei denn, ich habe etwas falsch verstanden). – egnha

Verwandte Themen