# FROM: 
# http://stackoverflow.com/questions/15593961/r-data-table-efficient-replication-by-group/15594262#15594262

library(data.table)

# Sample data
ob1 <- as.data.frame(cbind(c(1999),c("THE","BLACK","DOG","JUMPED","OVER","RED","FENCE"),c(4)),stringsAsFactors=FALSE)
ob2 <- as.data.frame(cbind(c(2000),c("I","WALKED","THE","BLACK","DOG"),c(3)),stringsAsFactors=FALSE)
ob3 <- as.data.frame(cbind(c(2001),c("SHE","PAINTED","THE","RED","FENCE"),c(1)),stringsAsFactors=FALSE)
ob4 <- as.data.frame(cbind(c(2002),c("THE","YELLOW","HOUSE","HAS","BLACK","DOG","AND","RED","FENCE"),c(2)),stringsAsFactors=FALSE)
sample_data <- rbind(ob1,ob2,ob3,ob4)
colnames(sample_data) <- c("yr","token","multiple")


      # make sure `sample_data$multiple` is an integer
      sample_data$multiple <- as.integer(sample_data$multiple)

      # create data.table
      S <- data.table(sample_data, key='yr')

      # optionally, drop original data.frame if not needed
      rm(sample_data)

      ## Allocate the memory first  (you can combine these next two lines into one. Harder to read, but slight pickup)
      newDT <- setkey(S[, rep(NA, list(rows=length(token) * unique(multiple))), by=yr][, list(yr)], 'yr')
      newDT[, tokenReps := as.character(NA)]

      # Add the rep'd tokens into newDT, using recycling
      for (y in unique(S[, yr]))
        newDT[.(y), tokenReps := S[.(y)][,token] ]



---

### Two notes: 

(1)  `sample_data$multiple` is currently a character and thus getting coerced when passed to `rep` (in your original example).  It might be worth double-checking your real data if that is also the case. 

(2)  I used the following to determine the number of rows needed per year 

    S[, list(rows=length(token) * unique(multiple)), by=yr] 

---

And now for the fun part: benchmarks! 

      # ensure proper data type
      sample_data$yr       <- as.integer(sample_data$yr)
      sample_data$multiple <- as.integer(sample_data$multiple)

      # concatenate several copies of the sample_data 
      N <- 1e5
      large_sample_data <- rbindlist(replicate(N, sample_data, simplify=FALSE))
      large_sample_data[, token := paste0(token, sample(LETTERS, length(token), TRUE))]

      # convert back to data.frame for proper starting point
      large_sample_data <- as.data.frame(large_sample_data)
      dim(large_sample_data)

---

      library(microbenchmark)

      prev_solution  <- quote(data.table(large_sample_data)[, list(tokenReps = rep(token,unique(multiple))), by = "yr"])


      new_solution <- quote({
                          S <- data.table(large_sample_data, key='yr')
                          newDT <- setkey(S[, rep(NA, list(rows=length(token) * unique(multiple))), by=yr][, list(yr, tokenReps=as.character(NA) )], 'yr')
                          newDT[, tokenRep := S[.(y)][, token], by=list(y=yr)]
                        })


      S <- data.table(large_sample_data, key='yr')
      new_solution_noS <- quote({
                          newDT <- setkey(S[, rep(NA, list(rows=length(token) * unique(multiple))), by=yr][, list(yr, tokenReps=as.character(NA) )], 'yr')
                          newDT[, tokenRep := S[.(y)][, token], by=list(y=yr)]
                        })



      microbenchmark(eval(prev_solution), eval(new_solution), eval(new_solution_noS), times=25L)

        #  Unit: milliseconds
        #                      expr       min       lq   median       uq      max
        #  1 eval(new_solution_noS) 1160.4343 1315.474 1382.222 1462.104 1629.484
        #  2     eval(new_solution) 1482.7898 1725.719 1780.461 1876.911 2328.388
        #  3    eval(prev_solution)  852.4272  975.719 1073.944 1186.434 1518.255

