2012-07-22 21 views
5

Me gustaría agregar un marco de datos por intervalo de tiempo, aplicando una función diferente a cada columna. Creo que casi tengo aggregate abajo, y he dividido mis datos en intervalos con el paquete chron, lo cual fue bastante fácil.R: agregado con función específica de columna

Pero no estoy seguro de cómo procesar los subconjuntos. Todas las funciones de mapeo, *apply, *ply, toman una función (yo esperaba algo que tomara un vector de funciones para aplicar por columna o -variable, pero no he encontrado ninguna) así que estoy escribiendo una función que toma mis subconjuntos de marcos de datos, y me da la media para todas las variables, excepto "tiempo", que es el índice, y "Escorrentía", que debería ser la suma.

yo probamos este:

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

que sería lo suficientemente feo incluso si no me dio este error:

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

que me dice que realmente estoy haciendo algo mal. Por lo que he visto de R, creo que debe haber una manera elegante de hacerlo, pero ¿qué 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") 

Respuesta

8

Hay muchas cosas malas con su enfoque. Un consejo general es no ir directamente a lo que piensas que debería ser la declaración final, sino trabajar en incrementos, de lo contrario hace que la depuración (comprensión y corrección de errores) sea bastante difícil.

Por ejemplo, podría haber comenzado con:

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

darse cuenta de que hay algo mal con su variable de segmentación. Aparentemente aggregate no le gusta trabajar con esta clase de datos. Puede solucionar este problema mediante la conversión de Time a numérico:

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

entonces puede intentar

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

donde apply.fun es su función definida por el usuario. Esta falla con un mensaje bastante críptica, pero el funcionamiento

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

ayuda a darse cuenta de que la función FUN dentro aggregate no se llama una vez para cada pieza de datos (y aprobó una hoja.de.datos), pero se llama una vez para cada columna de sus piezas de datos (y pasó un vector sin nombre), por lo que no hay forma de que pueda obtener el resultado que desea utilizando aggregate.

En su lugar, puede usar la función ddply del paquete plyr. Allí, la función aplicada a cada pieza recibe datos.marco de lo que se puede hacer algo como esto:

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 

Editar: de Seguimiento sobre la cuestión @roysc en el primer comentario más abajo, se puede hacer:

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

bien, creo que entiendo esto un poco mejor. Estoy acostumbrado a los lenguajes fuertemente tipados y encuentro que el esquema de coacción de clase de R es confuso. Lo que también me gustaría saber es si hay una manera simple de tomar la media de la mayoría de las columnas, pero trate una especialmente, sin hacerlo explícitamente. ¿Tendré que separar el df y luego recombinar las columnas? – scry

1

¿Qué tal esto?

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

esto se pierde el hecho de que una de las columnas no debe tener la media, sino más bien la suma – Chase

+0

Me di cuenta de eso pero surgieron más respuestas útiles. Voy a actualizar esto. – Maiasaura

5

Uso by en lugar de aggregate.

Si f es el samee como su función anónima excepto que list dentro de ella se reemplaza con data.frame modo que f <- function(dat) with(dat, data.frame(...whatever...)) a continuación:

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

Nos podría eliminar la última declaración que asigna nombres de columna si f ha añadido la misma de nombres en lugar de solo Time.

Cuestiones relacionadas