I have a data set that I need to analyze in R. A simplified version of it would be like this

SessionNo.  Objects       OtherColumns
    A          2               .
    A          3
    B          4
    C          1
    D          2
    D          1
    D          2
    D          3
    E          5

here each sessionno. represents one session of a broswer but due to the relation with other columns in the data it is aggregated like shown. So, Session 1 is now fragmented into two rows etc. What I need to find is the avg. number of objects downloaded per session (or any other statistics for no. of rows in each session). So, how do I count the no of objects for each session in R. Here, 5 objects in session A, 4 in session B, 8 in session D etc.

I guess one way would be to sum the whole Objects column and count the no. of unique session numbers in SessionNo. But I guess it would be more of a general solution if I could group the unique session number with the total number of objects aggregated in it? Any suggestions on how to accomplish that in R?


5 Answers 5


Perhaps this might help:

tapply(df$Objects, df$SessionNo., sum)
  • 1
    $\begingroup$ +1 since aggregate is an overshoot here and plyr is slow. $\endgroup$
    – user88
    Nov 24, 2010 at 18:15
  • $\begingroup$ -1 That can't possibly work unless you make the components of the data frame visible (using say with(), or refer to the explicitly via, e.g., df$Objects where df is the op's data frame. $\endgroup$ Nov 25, 2010 at 7:30
  • $\begingroup$ @mbq: why is aggregate() an "overshoot here"? It seems very natural as it returns for you a DF with the unique SessionNo. plus the sum for each. tapply() just gives the sums. $\endgroup$ Nov 25, 2010 at 7:32
  • $\begingroup$ @Gavin: You´re right, i´ve edited my post. $\endgroup$
    – EDi
    Nov 25, 2010 at 10:36
  • $\begingroup$ @EDi +1 following edit $\endgroup$ Nov 25, 2010 at 11:31

The function that is specifically designed for this task is ave(). By default it returned the mean within a group and returns a vector of the same length as the two input arguments. It is designed to fill in columns of either means or deviations from means. If this is in a dataframe with name "tst":

> tst$tmn <- with(tst, ave(Objects, SessionNo.))
> tst$devmn <- tst$Objects- with(tst, ave(Objects, SessionNo.))
> tst
  SessionNo. Objects tmn devmn
1          A       2 2.5  -0.5
2          A       3 2.5   0.5
3          B       4 4.0   0.0
4          C       1 1.0   0.0
5          D       2 2.0   0.0
6          D       1 2.0  -1.0
7          D       2 2.0   0.0
8          D       3 2.0   1.0
9          E       5 5.0   0.0
  • $\begingroup$ if you are using with(), then go the whole hog and use within() here --- gets rid of more $. Your first line could be, tst <- within(tst, tmn <- ave(Objects, SessionNo.)) whilst your second line could be tst <- within(tst, devmn <- Objects - ave(Objects, SessionNo.)). Note that the second line contains two modifications as the whole thing can be inside with() or within(). $\endgroup$ Nov 25, 2010 at 7:39

I personally like using the plyr and or reshape packages for tasks like this. If you're just starting with R, I would highly recommend getting to know them well. They've solved nearly all of my data manipulation tasks.

ddply(df, .(sessionNo.) function(x) data.frame(

OR, cast

colnames(df[1:2]) <- c("variable","value")
cast(df[1:2], variable ~ value, sum)
  • $\begingroup$ I think the colnames line above needs to be rewritten as colnames(df)[1:2] <- c("variable","value")? $\endgroup$
    – Chase
    Nov 30, 2010 at 22:53

If I am reading your question correctly, something like the following should do it:


where x is the data frame containing your data. This will give you, for each unique session number, the sum of the object counts.

You can of course substitute other functions (including your own) on place of the sum.


I often use by, which is a wrapper around tapply. Result is a list with a weee different print method compared to tapply.

df <- data.frame(SessionNo. = sample(x = c("A", "B", "C", "D", "E"),
                size = 20, replace = TRUE,
                prob = c(0.05, 0.1, 0.4, 0.4, 0.05)),
        Objects = sample(x = 1:5, size = 20, replace = TRUE))

df.out <- by(data = df$Objects, INDICES = df$SessionNo.,
        FUN = mean, simplify = FALSE)
[1] 3

[1] 1

[1] 3.222222

[1] 3.833333

[1] 1
  • $\begingroup$ Is there a difference if i give the argument "simplify = FALSE" to tapply()? $\endgroup$
    – EDi
    Nov 25, 2010 at 15:22
  • $\begingroup$ Try it, I've provided a full working example. :) You will see the difference if you use str(df.out) on both instances. $\endgroup$ Nov 26, 2010 at 8:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.