2012-07-22 26 views
5

Vorrei aggregare un frame di dati in base all'intervallo di tempo, applicando una funzione diversa a ciascuna colonna. Penso di avere quasi il aggregate in basso e ho diviso i miei dati in intervalli con il pacchetto chron, che era abbastanza facile.R: aggregato con funzione specifica della colonna

Ma non sono sicuro di come elaborare i sottoinsiemi. Tutte le funzioni di mappatura, *apply, *ply, svolgono una funzione (speravo in qualcosa che richiedesse un vettore di funzioni da applicare per colonna o variabile, ma non ne abbia trovato uno), quindi sto scrivendo una funzione che richiede i miei sottoinsiemi di frame di dati e mi forniscono la media per tutte le variabili, tranne "time", che è l'indice e "Runoff" che dovrebbe essere la somma.

ho provato questo:

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)))) 

che sarebbe brutto abbastanza, anche se non mi ha dato questo errore:

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

che mi dice che sto davvero facendo qualcosa di sbagliato. Da quello che ho visto di R penso che ci debba essere un modo elegante per farlo, ma che cos'è?

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") 

risposta

8

Ci sono molte cose che non vanno nel vostro approccio. Un consiglio generale non è quello di andare dritto per quello che pensi che dovrebbe essere la dichiarazione finale, ma lavorare le cose in incrementi, altrimenti rende difficile il debug (comprensione e correzione degli errori).

Ad esempio, si potrebbe avere iniziato con:

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

a notare che c'è qualcosa di sbagliato con la variabile di distinzione. Apparentemente a aggregate non piace lavorare con questa classe di dati. È possibile risolvere questo problema mediante la conversione Time a numerico:

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

allora si può provare

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

dove apply.fun è la vostra funzione definita dall'utente. Ciò non è riuscito con un messaggio piuttosto criptico, ma in esecuzione

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

aiuta a realizzare che la funzione FUN all'interno aggregate non viene chiamato una volta per ogni pezzo di dati (e approvato una data.frame), ma viene chiamato una volta per ogni colonna dei tuoi dati (e passato un vettore senza nome), quindi non c'è modo di ottenere il risultato desiderato usando aggregate.

Invece, è possibile utilizzare la funzione ddply dal pacchetto plyr. Lì, la funzione applicata a ciascun pezzo riceve un dato.fotogramma in modo da poter fare qualcosa di simile:

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 sulla questione @roysc nel primo commento qui sotto, si può fare:

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

ok, penso di capirlo un po 'meglio. Sono abituato a lingue fortemente tipizzate e trovo che lo schema di coercizione di classe di R sia confuso. Quello che vorrei anche sapere è se c'è un modo semplice per prendere la media della maggior parte delle colonne, ma trattarne uno appositamente, senza farlo esplicitamente. Dovrò separare il df e poi ricombinare le colonne? – scry

1

ne dici di questo?

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

questo manca il fatto che una delle colonne non dovrebbe avere la media, ma piuttosto la somma – Chase

+0

ho notato che ma le risposte più utili sono venute in giro. Aggiornerò questo – Maiasaura

5

Uso by invece di aggregate.

Se f è il samee come funzione anonima se non che list all'interno di esso viene sostituito con data.frame in modo che f <- function(dat) with(dat, data.frame(...whatever...)) poi:

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.) 

Potremmo rimuovere l'ultima dichiarazione che assegna i nomi delle colonne se f aggiunti i nomi si piuttosto che solo Time.

Problemi correlati