2016-09-12 8 views
1

Angenommen, ich habe einige data.framedf:Anwendung data.frame raubend Funktionen über Gruppen von Zeilen

df <- read.table(text = " 
P Q R 
c 1 10 
a 1 0 
a 2 0 
b 2 0 
b 1 10 
c 2 10 
b 1 0 
a 2 10 
", 
stringsAsFactors = FALSE, 
header=T) 

... und einige Funktion foo, die eine data.frame als Argument verwendet.

Eine Aufspaltung in kleinere dfdata.frame vorstellen kann, ist entsprechend dem Wert in einem ihrer Spalten, sagen P und foo auf jede dieser kleineren data.frame Anwendung ist.

Im Folgenden zeige ich, was ich am besten kann, um dieses Problem zu lösen, aber ich vermute, dass bereits mehr rationalisierte Lösungen existieren, um eine solche natürliche Operation durchzuführen. Wenn ja, meine Frage ist: Was sind sie?

NB: Ich zeige unten zwei Anwendungsfälle; Das erste von beiden ist das, von dem ich erwarte, dass es deutlich verbessert werden kann. Was die zweite betrifft, denke ich, dass meine Lösung dafür schon so gut ist, wie sie wird; Ich füge diesen Anwendungsfall nur für den Fall ein, dass meine Vermutung falsch ist.


Meine Lösung hängt davon ab, ob foo ist eine Funktion, die ich für den Rückgabewert nennen, oder eine, die ich nur für seine Nebenwirkungen nennen.

Für den ersten Fall (foo für ihr Wert genannt) an, dass foo dies:

set.seed(0) 
sapply(unique(df$P), function (value) foo(df[df$P == value, ]), 
     simplify = FALSE) 
## $c 
## P Q R 
## 6 c 2 10 
## 
## $a 
## P Q R 
## 2 a 1 0 
## 
## $b 
## P Q R 
## 5 b 1 10 

Für den letzteren Fall:

## returns a one-row data.frame corresponding to a random row of 
## dataframe 
## NB: this is *just an example* for the sake of this question 
foo <- function (dataframe) { 
    dataframe[sample(nrow(dataframe), 1), ] 
} 

... dann wäre meine Lösung dieses sein (foo genannt für seine Nebenwirkung), angenommen, dass foo ist dies:

## prints to stdout a one-row data.frame corresponding to a random 
## row of dataframe 
## NB: this is *just an example* for the sake of this question 
foo <- function (dataframe) { 
    cat(str(dataframe[sample(nrow(dataframe), 1), ])) 
} 

... dann wäre meine Lösung sein:

set.seed(0) 
for (value in unique(df$P)) foo(df[df$P == value, ]) 
## 'data.frame': 1 obs. of 3 variables: 
## $ P: chr "c" 
## $ Q: int 2 
## $ R: int 10 
## 'data.frame': 1 obs. of 3 variables: 
## $ P: chr "a" 
## $ Q: int 1 
## $ R: int 0 
## 'data.frame': 1 obs. of 3 variables: 
## $ P: chr "b" 
## Q: int 1 
## R: int 10 
+1

Haben Sie sich die Funktion 'by' angeschaut? – aichao

+0

@aichao: 'by' sieht aus wie ich suche. Möchten Sie Ihren Kommentar als Antwort posten? – kjo

+1

Ich schlage auch vor, das dplyr-Paket und die Funktion 'group_by' zu suchen. – Dave2e

Antwort

1

Sie by Ihre beiden Anwendungsfällen mit der Funktion erreichen können. Um Ihre Ergebnisse zu replizieren, ändern wir jedoch Ihre Funktionen so, dass die letzte Zeile der Gruppe anstelle einer zufällig ausgewählten Zeile zurückgegeben oder ausgegeben wird. Dies ist erforderlich, da die Reihenfolge der Zeilen in einer Gruppe von by geändert wird. In einem echten Anwendungsfall sollte diese Reihenfolge keine Rolle spielen. Diese nur ist wichtig, weil Ihre Ergebnisse von einem Zufallszahlengenerator abhängig sind, um die gruppierten Zeilen auszuwählen.

In Ihrem ersten Anwendungsfall:

foo <- function (dataframe) { 
    dataframe[nrow(dataframe), ] 
} 

out1 <- sapply(unique(df$P), function (value) foo(df[df$P == value, ]), 
       simplify = FALSE) 

Das Ergebnis out1 ist ein list:

str(out1) ## this displays the structure of the out1 object 
##List of 3 
## $ c:'data.frame': 1 obs. of 3 variables: 
## ..$ P: chr "c" 
## ..$ Q: int 2 
## ..$ R: int 10 
## $ a:'data.frame': 1 obs. of 3 variables: 
## ..$ P: chr "a" 
## ..$ Q: int 2 
## ..$ R: int 10 
## $ b:'data.frame': 1 obs. of 3 variables: 
## ..$ P: chr "b" 
## ..$ Q: int 1 
## ..$ R: int 0 

Wir können das gleiche Ergebnis mit by, erreichen, die ein Objekt der Klasse zurückgibt by, das ist a list:

by.out1 <- with(df, by(df, P, foo)) 
str(by.out1) 
##List of 3 
## $ a:'data.frame': 1 obs. of 3 variables: 
## ..$ P: chr "a" 
## ..$ Q: int 2 
## ..$ R: int 10 
## $ b:'data.frame': 1 obs. of 3 variables: 
## ..$ P: chr "b" 
## ..$ Q: int 1 
## ..$ R: int 0 
## $ c:'data.frame': 1 obs. of 3 variables: 
## ..$ P: chr "c" 
## ..$ Q: int 2 
## ..$ R: int 10 
## - attr(*, "dim")= int 3 
## - attr(*, "dimnames")=List of 1 
## ..$ P: chr [1:3] "a" "b" "c" 
## - attr(*, "call")= language by.data.frame(data = df, INDICES = P, FUN = foo) 
## - attr(*, "class")= chr "by" 

Hier verwenden wir by mit with, um die by innerhalb der aus df konstruierten Umgebung auszuführen. Dies ermöglicht es uns, die Spalten df (wie P) namentlich ohne Anführungszeichen anzugeben.

Für Ihren zweiten Anwendungsfall (die über cat zu trösten zeigen):

with(df, by(df, P, foo)) 
##'data.frame': 1 obs. of 3 variables: 
## $ P: chr "a" 
## $ Q: int 2 
## $ R: int 10 
##'data.frame': 1 obs. of 3 variables: 
## $ P: chr "b" 
## $ Q: int 1 
## $ R: int 0 
##'data.frame': 1 obs. of 3 variables: 
## $ P: chr "c" 
## $ Q: int 2 
## $ R: int 10 

Die Funktion by ist in den base:

foo <- function (dataframe) { 
    cat(str(dataframe[nrow(dataframe), ])) 
} 

for (value in unique(df$P)) foo(df[df$P == value, ]) 
##'data.frame': 1 obs. of 3 variables: 
## $ P: chr "c" 
## $ Q: int 2 
## $ R: int 10 
##'data.frame': 1 obs. of 3 variables: 
## $ P: chr "a" 
## $ Q: int 2 
## $ R: int 10 
##'data.frame': 1 obs. of 3 variables: 
## $ P: chr "b" 
## $ Q: int 1 
## $ R: int 0 

Auch hier können wir das gleiche Ergebnis mit by erreichen R-Paket. Wie von Dave2e erwähnt, gibt es viele andere Pakete, die ähnliche Datenmanipulationsfähigkeiten haben. Einige von ihnen bieten mehr syntaktischen Zucker für die Benutzerfreundlichkeit und andere bieten eine bessere Optimierung oder beides. Einige davon sind: plyr, dplyr und data.table. Ich überlasse es Ihnen, diese zu erkunden.

Verwandte Themen