#----------------------------------# # ORIGINAL # Sample DAta require (data.table) require (plyr) dtb <- data.table (expand.grid (month = rep (month.abb[1:3], each = 3), fac = letters[1:3]), value = rnorm (27)) # create two copies, since we will be altering the original dtb1 <- copy(dtb) dtb2 <- copy(dtb) # OP's original method (agg1 <- unique(dtb1[, value := mean (value), by = list (month, fac)])) # @Aruns suggestion (agg2 <- dtb2[, list(value = mean(value), newCol), by = list (month, fac)]) #----------------------------------# The issue (and reasoning) becomes more apparent if you look at a data.table with rows in excess of the ones being used in the computation. # Let's add a new column dtb[, newCol := LETTERS[seq(length(value))] Note that if we just want to output the computed value, then expression on the `RHS` as you have it is just fine. # This gives the expected results dtb[, mean (value), by = list (month, fac)] # This on the other hand assigns the respective values to *each* row dtb[, value := mean (value), by = list (month, fac)] In other words, the data is being subsetted to only return unique values. However, if you want to save this value back into the _SAME_ data table (which is what happens when using `:=`) then all rows that are identified in `i` (all rows by defualt) will be assigned a value. (which, when you look at the output with additional columns, makes sense) Then copying this data.table to agg still sends through all the rows. Therefore, if you want to copy to a new table, only those rows from your original table that are unique, you can a. wrap the table inside `unique()` before assigning it b. assign the table, above, that is returned when you are not assigning the RHS output (which is what @Arun suggested) #----------------------------------# The following example might help illustrate. _(You would need to copy + paste this, as the output is ommitted)_ # SAMPLE DATA, as above library(data.table) dtb.bak <- data.table (expand.grid (month = rep (month.abb[1:3], each = 3), fac = letters[1:3]), value = rnorm (27)) # METHOD 1 # #------------# dtb <- copy(dtb.bak) # restore, from sample data. dtb[, value := mean (value), by = list (month, fac)] dtb # this is what you would like to assign unique(dtb) # METHOD 2 # #------------# dtb <- copy(dtb.bak) # restore, from sample data. # this is what you would like to assign # next two lines are the same, only differnce is column name dtb[, mean (value), by = list (month, fac)] dtb[, list("mean" = mean (value)), by = list (month, fac)] # quote marks added for clarity # dtb is unchanged. dtb # NOW COMPARE THE SAME TWO METHODS, BUT IF THERE IS AN ADDITIOANL COLUMN dtb.bak[, newCol := rep(c("A", "B", "A"), length(value)/3)] dtb1 <- copy(dtb.bak) # restore, from sample data. dtb2 <- copy(dtb.bak) # restore, from sample data. # Method 1 dtb1[, value := mean (value), by = list (month, fac)] dtb1 unique(dtb1) # METHOD 2 # dtb2[, list("mean" = mean (value)), by = list (month, fac)] # quote marks added for clarity dtb2 # METHOD 2, WITH ADDED COLUMNS IN list() in `j` dtb2[, list("mean" = mean (value), newCol), by = list (month, fac)] # quote marks added for clarity # notice this has more columns thatn unique(dtb1)