2012-07-22 10 views
5

Ich möchte einen Datenrahmen nach Zeitintervall aggregieren, indem ich für jede Spalte eine andere Funktion anwende. Ich glaube ich habe fast aggregate runter, und habe meine Daten in Intervalle mit dem chron Paket aufgeteilt, was einfach genug war.R: Aggregat mit spaltenspezifischer Funktion

Aber ich bin mir nicht sicher, wie die Teilmengen zu verarbeiten sind. Alle Zuordnungsfunktionen, *apply, *ply, nehmen eine Funktion an (ich hatte auf etwas gehofft, das einen Vektor von Funktionen pro Spalte oder -variable anwendete, aber keinen gefunden hatte), also schreibe ich eine Funktion, die benötigt meine Datenrahmen-Teilmengen, und gibt mir den Mittelwert für alle Variablen, außer "Zeit", die der Index ist, und "Runoff", die die Summe sein sollte.

Ich versuchte dies:

aggregate(d., list(Time=trunc(d.$time, "00:10:00")), function (dat) with(dat, 
list(Time=time[1], mean(Port.1), mean(Port.1.1), mean(Port.2), mean(Port.2.1), 
mean(Port.3), mean(Port.3.1), mean(Port.4), mean(Port.4.1), Runoff=sum(Port.5)))) 

, die genug, um selbst hässlich wäre, wenn es nicht mir diesen Fehler gegeben hat:

Error in eval(substitute(expr), data, enclos = parent.frame()) : 
    not that many frames on the stack 

die mich tun erzählt Ich bin wirklich etwas falsch gemacht. Von dem, was ich von R gesehen habe, denke ich, dass es einen eleganten Weg dafür geben muss, aber was ist es?

dput:

d. <- structure(list(time = structure(c(15030.5520833333, 15030.5555555556, 
15030.5590277778, 15030.5625, 15030.5659722222), format = structure(c("m/d/y", 
"h:m:s"), .Names = c("dates", "times")), origin = structure(c(1, 
1, 1970), .Names = c("month", "day", "year")), class = c("chron", 
"dates", "times")), Port.1 = c(0.359747, 0.418139, 0.417459, 
0.418139, 0.417459), Port.1.1 = c(1.3, 11.8, 11.9, 12, 12.1), 
    Port.2 = c(0.288837, 0.335544, 0.335544, 0.335544, 0.335544 
    ), Port.2.1 = c(2.3, 13, 13.2, 13.3, 13.4), Port.3 = c(0.253942, 
    0.358257, 0.358257, 0.358257, 0.359002), Port.3.1 = c(2, 
    12.6, 12.7, 12.9, 13.1), Port.4 = c(0.352269, 0.410609, 0.410609, 
    0.410609, 0.410609), Port.4.1 = c(5.9, 17.5, 17.6, 17.7, 
    17.9), Port.5 = c(0L, 0L, 0L, 0L, 0L)), .Names = c("time", 
"Port.1", "Port.1.1", "Port.2", "Port.2.1", "Port.3", "Port.3.1", 
"Port.4", "Port.4.1", "Port.5"), row.names = c(NA, 5L), class = "data.frame") 

Antwort

8

Mit Ihrem Ansatz sind viele Dinge nicht in Ordnung. Ein allgemeiner Ratschlag besteht darin, nicht direkt auf das zuzugehen, von dem Sie denken, dass die abschließende Aussage aussehen sollte, sondern Dinge in Inkrementen zu bearbeiten, andernfalls macht es das Debuggen (das Verstehen und Beheben von Fehlern) ziemlich schwierig.

Zum Beispiel könnten Sie mit begonnen haben:

aggregate(d., list(Time=trunc(d.$time, "00:10:00")), identity) 

zu bemerken, dass es etwas falsch mit Ihrem Split variabel ist. Anscheinend arbeitet aggregate nicht gerne mit dieser Datenklasse. Sie können dieses Problem beheben, indem Time in numerische Umwandlung:

aggregate(d., list(Time=as.numeric(trunc(d.$time, "00:10:00"))), identity) 

Dann können Sie

aggregate(d., list(Time=as.numeric(trunc(d.$time, "00:10:00"))), apply.fun) 

versuchen, wo apply.fun Ihre benutzerdefinierte Funktion ist. Dies scheitert mit einer eher CRIPTIC Nachricht, aber

aggregate(d., list(Time=as.numeric(trunc(d.$time, "00:10:00"))), print) 

hilft erkennen, läuft, dass die FUN Funktion innerhalb aggregate nicht einmal für jedes Datenstück genannt (und verabschiedete einen data.frame), aber es wird einmal für jede Spalte aufgerufen Ihrer Datenstücke (und übergeben einen unbenannten Vektor), so gibt es keine Möglichkeit, das gewünschte Ergebnis mit aggregate zu erhalten.

Stattdessen können Sie die ddply Funktion aus dem plyr Paket verwenden. Dort empfängt die Funktion, die auf jedes Stück angewendet wird, Daten.Rahmen, so dass Sie etwas tun können:

apply.fun <- function(dat) with(dat, data.frame(Time=time[1], 
               mean(Port.1), 
               mean(Port.1.1), 
               mean(Port.2), 
               mean(Port.2.1), 
               mean(Port.3), 
               mean(Port.3.1), 
               mean(Port.4), 
               mean(Port.4.1), 
               Runoff=sum(Port.5))) 

d.$Time <- as.numeric(trunc(d.$time, "00:10:00")) 
library(plyr) 
ddply(d., "Time", apply.fun) 

#   Time mean.Port.1. mean.Port.1.1. mean.Port.2. mean.Port.2.1. 
# 1 15030.5520833 0.4061886   9.82 0.3262026   11.04 
# mean.Port.3. mean.Port.3.1. mean.Port.4. mean.Port.4.1. Runoff 
# 1  0.337543   10.66  0.398941   15.32  0 

Edit: Follow-up auf @roysc Frage in dem ersten Kommentar unten, können Sie tun:

apply.fun <- function(dat) { 
    out <- as.data.frame(lapply(dat, mean)) 
    out$Time <- dat$time[1] 
    out$Runoff <- sum(dat$Port.5) 
    return(out) 
} 
+0

Okay, ich denke ich verstehe das ein bisschen besser. Ich bin an stark typisierte Sprachen gewöhnt und finde das Klassenzwangsschema von R verwirrend. Ich würde auch gerne wissen, ob es einen einfachen Weg gibt, den Mittelwert der meisten Spalten zu nehmen, sondern einen speziell zu behandeln, ohne es explizit zu machen. Muss ich das df trennen und dann die Spalten neu kombinieren? – scry

1

Wie wäre das?

library(plyr) 
ddply(d., .(time), colMeans) 
+0

das vermisst die Tatsache, dass eine der Spalte nicht die mittlere haben soll, sondern eher die Summe – Chase

+0

Ich bemerkte, dass aber mehr nützliche Antworten kamen. Ich werde das aktualisieren. – Maiasaura

5

Verwenden by statt aggregate.

Wenn f die samee als anonyme Funktion ist, außer dass list innerhalb sie mit data.frame ersetzt wird, so dass f <- function(dat) with(dat, data.frame(...whatever...)) dann:

d.by <- by(d., list(Time = trunc(d.$time, "00:10:00")), f) 
d.rbind <- do.call("rbind", d.by) # bind rows together 

# fix up row and column names 
rownames(d.rbind) <- NULL 
colnames(d.rbind) <- colnames(d.) 

Wir konnten die letzte Anweisung entfernen, die Spaltennamen zuordnet, wenn f die Namen hinzugefügt selbst anstatt nur Time.