R: aggregate with column-specific function

Viewed 3837

I would like to aggregate a data frame by time interval, applying a different function to each column. I think I almost have aggregate down, and have divided my data into intervals with the chron package, which was easy enough.

But I'm not sure how to process the subsets. All of the mapping functions, *apply, *ply, take one function (I was hoping for something that took a vector of functions to apply per-column or -variable, but haven't found one) so I'm writing a function that takes my data frame subsets, and gives me the mean for all variables, except "time", which is the index, and "Runoff" which should be the sum.

I tried this:

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

which would be ugly enough even if it didn't give me this error:

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

which tells me I'm really doing something wrong. From what I've seen of R I think there must be an elegant way to do this, but what is it?

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

Another option is to use a sequence of steps that will accomplish the same task in base R by alternately running aggregate() then using merge() as in:

agMeans_df <- aggregate(cbind(Port.1,Port1.1,Port.2,Port.2.2,Port.3,Port.3.1,Port.4,Port4.1)~timevar,data=d,mean)
agSum_df <- aggregate(Port.5~timevar,data=d,sum)
ag_all_df <- merge(agMeans_df,agSum_df,by="timevar")

I glossed over the issues raised in other responses that the group vector needs to be of the right class (here "timevar"), and that ordering of the columns may be changed. Some renaming prior to the merge() will also be required if you want to run several different functions on the same column to avoid confusing two aggregated columns that have the same names.

Related