Frage Effizientes Berechnen einer linearen Kombination von data.table-Spalten


ich habe nc Spalten in einer data.table und nc Skalare in einem Vektor. Ich möchte eine nehmen lineare Kombination von den Spalten, aber ich weiß nicht im Voraus, welche Spalten ich verwenden werde. Was ist der effizienteste Weg, dies zu tun?

Konfiguration

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])

Wege, es zu tun

Angenommen, ich möchte die ersten vier Spalten verwenden. Ich kann manuell schreiben:

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

Ich kann mir zwei automatische Wege vorstellen (die funktionieren, ohne zu wissen, dass A-E verwendet werden sollte):

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

Ich erwarte das as.matrix um die zweite Option langsam zu machen, und wirklich keine Intuition für die Geschwindigkeit von Map-Reduce Kombinationen.

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

Ich komme irgendwo von einer Verlangsamung von 5% auf 40% gegenüber dem manuellen Ansatz, wenn ich das wiederhole benchmark Anruf.

meine Bewerbung

Die Dimensionen hier - n und length(mycols) - sind nah an dem, was ich arbeite, aber ich werde diese Berechnungen viele Male ausführen, den Koeffizientenvektor ändern, cf.


7
2017-10-09 17:44


Ursprung


Antworten:


Das ist fast 2x schneller als deine manuelle Version:

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

Und nur darüber iterieren mycols, ersetzen names(DT) mit mycols im lapply.


7
2017-10-09 18:09



Fügen Sie diese Option zu Ihrem Benchmark-Aufruf hinzu:

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

Auf meinem Gerät war es 30% schneller als die Matrix-Multiplikation, die Sie ausprobiert haben.


1
2017-10-09 17:57