Computación eficiente de una combinación lineal de columnas data.table

Tengo nc columnas en un data.table, y nc escalares en un vector. Quiero tomar una combinación lineal de las columnas, pero no sé de antemano qué columnas usaré. ¿Cuál es la forma más eficiente de hacer esto?

preparar

 require(data.table) set.seed(1) n <- 1e5 nc <- 5 cf <- setNames(rnorm(nc),LETTERS[1:nc]) DT <- setnames(data.table(replicate(nc,rnorm(n))),LETTERS[1:nc]) 

maneras de hacerlo

Supongamos que quiero usar las primeras cuatro columnas. Puedo escribir manualmente:

 DT[,list(cf['A']*A+cf['B']*B+cf['C']*C+cf['D']*D)] 

Puedo pensar en dos formas automáticas (que funcionan sin saber que todas las AE deberían usarse):

 mycols <- LETTERS[1:4] # the first four columns DT[,list(as.matrix(.SD)%*%cf[mycols]),.SDcols=mycols] DT[,list(Reduce(`+`,Map(`*`,cf[mycols],.SD))),.SDcols=mycols] 

benchmarking

Espero que la as.matrix haga lenta la segunda opción, y realmente no tenga intuición para la velocidad de MapReduce combinaciones.

 require(rbenchmark) options(datatable.verbose=FALSE) # in case you have it turned on benchmark( manual=DT[,list(cf['A']*A+cf['B']*B+cf['C']*C+cf['D']*D)], coerce=DT[,list(as.matrix(.SD)%*%cf[mycols]),.SDcols=mycols], maprdc=DT[,list(Reduce(`+`,Map(`*`,cf[mycols],.SD))),.SDcols=mycols] )[,1:6] test replications elapsed relative user.self sys.self 2 coerce 100 2.47 1.342 1.95 0.51 1 manual 100 1.84 1.000 1.53 0.31 3 maprdc 100 2.40 1.304 1.62 0.75 

Tengo un 5% a un 40% de desaceleración en relación con el enfoque manual cuando repito la llamada de benchmark .

mi aplicación

Las dimensiones aquí – n length(mycols) – están cerca de lo que estoy trabajando, pero ejecutaré estos cálculos muchas veces, alterando el vector de coeficientes, cf

Esto es casi 2 veces más rápido para mí que su versión manual:

 Reduce("+", lapply(names(DT), function(x) DT[[x]] * cf[x])) benchmark(manual = DT[, list(cf['A']*A+cf['B']*B+cf['C']*C+cf['D']*D)], reduce = Reduce('+', lapply(names(DT), function(x) DT[[x]] * cf[x]))) # test replications elapsed relative user.self sys.self user.child sys.child #1 manual 100 1.43 1.744 1.08 0.36 NA NA #2 reduce 100 0.82 1.000 0.58 0.24 NA NA 

Y para iterar solo sobre mycols , reemplace names(DT) con mycols en lapply .

Agregue esta opción a su llamada de referencia:

 ops = as.matrix(DT) %*% cf 

En mi dispositivo era un 30% más rápido que la multiplicación de matriz que intentaste.