Designação rápida de data.table de várias colunas por grupo a partir da pesquisa

Procurei a maneira canônica de fazer o que estou tentando, mas parece que tenho pouca sorte em conseguir algo rápido e elegante. Em resumo, eu tenho uma tabela grande com várias colunas de valor e quero multiplicar cada uma por um fator correspondente de uma tabela de pesquisa. Não consigo descobrir como passar dinamicamente em quais colunas eu quero multiplicado pelos valores de pesquisa ou como me referir aos valores de pesquisa em geral fora das expressões básicas.

Aqui está o meu exemplo: eu tenho ele configurado com 3 milhões de linhas com 10 colunas de valor, isso não leva muito tempo e é um pouco representativo do tamanho dos dados (isso será implementado como parte de um loop muito maior, daí a ênfase no desempenho). Há também uma tabela de pesquisa com 6 níveis e alguns multiplicadores variados para nossas colunas value_1: value_10.

library(data.table)

setsize <- 3000000
value_num <- 10
factors <- c("factor_a", "factor_b", "factor_c", "factor_d", "factor_e", "factor_f")
random <- data.table(replicate(10, sample(factors, size = setsize,  replace = T))
                     , replicate(10, rnorm(setsize, mean = 700, sd = 50)))
lookup <- data.table("V1" = factors, replicate(10, seq(.90, 1.5, length.out = length(factors))))
wps <- paste("value", c(1:10), sep = "_")
names(random)[11:20] <- wps
names(lookup)[2:11] <- wps
setkeyv(random, "V1")
setkeyv(lookup, "V1")

Solução 1: É bastante rápido, mas não consigo descobrir como se referir genericamente às colunas i, comoi.value_1 para que eu possa passá-los em um loop ou, melhor ainda, aplicá-los todos de uma vez.

f <- function() {
  random[lookup, value_1 := value_1 * i.value_1, by = .EACHI]
  random[lookup, value_2 := value_2 * i.value_2, by = .EACHI]
  random[lookup, value_3 := value_3 * i.value_3, by = .EACHI]
  random[lookup, value_4 := value_4 * i.value_4, by = .EACHI]
  random[lookup, value_5 := value_5 * i.value_5, by = .EACHI]
  random[lookup, value_6 := value_6 * i.value_6, by = .EACHI]
  random[lookup, value_7 := value_7 * i.value_7, by = .EACHI]
  random[lookup, value_8 := value_8 * i.value_8, by = .EACHI]
  random[lookup, value_9 := value_9 * i.value_9, by = .EACHI]
  random[lookup, value_10 := value_10 * i.value_10, by = .EACHI]
}

system.time(f())

   user  system elapsed 
  0.184   0.000   0.181 

Solução 2: depois que não consegui obter a solução 1 genérica, tentei umset() abordagem baseada. No entanto, apesar de me permitir especificar as colunas de valor alvo no vetor de caractereswps, é realmente muito mais lento que o acima. Eu sei que estou usando errado, mas não sei como melhorá-lo para remover toda a sobrecarga [.data.table.

idx_groups <- random[,.(rowstart = min(.I), rowend = max(.I)), by = key(random)][lookup]
system.time(
for (i in 1:nrow(idx_groups)){
  rows <- idx_groups[["rowstart"]][i]:idx_groups[["rowend"]][i]
  for (j in wps) {
    set(random, i=rows, j=j, value= random[rows][[j]] * idx_groups[[j]][i])
  }  
})

   user  system elapsed 
  3.940   0.024   3.967 

Qualquer conselho sobre como melhor estruturar essas operações seria apreciado.

Edit: Estou muito frustrado comigo mesmo por não tentar esta solução óbvia antes de postar esta pergunta:

system.time(
for (col in wps){
  random[lookup, (col) := list(get(col) * get(paste0("i.", col))), by = .EACHI, with = F]
})

   user  system elapsed 
  1.600   0.048   1.652 

o que parece fazer o que eu quero com velocidade relativa. No entanto, ainda é 10 vezes mais lento que a primeira solução acima (tenho certeza, devido às repetidasget()) então ainda estou aberto a conselhos.

Editar 2: Substituindoget() comeval(parse(text=col)) parece ter feito o truque.

system.time(
for (col in wps){
  random[lookup, (col) := list(eval(parse(text=col)) * eval(parse(text=paste0("i.", col)))), by = .EACHI, with = F]
})
   user  system elapsed 
  0.184   0.000   0.185 

Edit 3: Várias boas respostas de trabalho foram fornecidas. A solução de Rafael é provavelmente a melhor no caso geral, embora eu note que poderia extrair mais alguns milissegundos da construção de chamadas recomendada por Jangorecki em troca de uma função auxiliar de aparência bastante intimidadora. Marquei como resposta, obrigado pela ajuda a todos.

questionAnswers(3)

yourAnswerToTheQuestion