2013-01-23 2 views
5

Ho bisogno di calcolare medie ponderate per riga (6M + righe), ma ci vuole molto tempo. La colonna con pesi è un campo carattere, quindi non può essere utilizzato direttamente. Datidata.table funzione per riga troppo lenta

Background:

library(data.table) 
library(stringr) 
values <- c(1,2,3,4) 
grp <- c("a", "a", "b", "b") 
weights <- c("{10,0,0,0}", "{0,10,0,0}", "{10,10,0,0}", "{0,0,10,0}") 
DF <- data.frame(cbind(grp, weights)) 
DT <- data.table(DF) 

string.weighted.mean <- function(weights.x) { 
    tmp.1 <- na.omit(as.numeric(unlist(str_split(string=weights.x, pattern="[^0-9]+")))) 
    tmp.2 <- weighted.mean(x=values, w=tmp.1) 
} 

Ecco come si può fare (troppo lento) con data.frames:

DF$wm <- mapply(string.weighted.mean, DF$weights) 

Questo fa il lavoro, ma è troppo lento (ore):

DT[, wm:=mapply(string.weighted.mean, weights)] 

Come può essere riformulata l'ultima riga per accelerare le cose?

+2

Hai un'ottima risposta. Solo per aggiungere: faccio fatica a pensare a un formato di input peggiore. Se possibile, utilizza le colonne dell'elenco per memorizzare i pesi come vettori numerici e per l'efficienza mai _ever_ iterate per riga, sempre per colonna. E una matrice potrebbe essere migliore in compiti come questo di data.table. –

risposta

6
DT[, rowid := 1:nrow(DT)] 
setkey(DT, rowid) 
DT[, wm :={ 
    weighted.mean(x=values, w=na.omit(as.numeric(unlist(str_split(string=weights, pattern="[^0-9]+")))))  
}, by=rowid] 
+1

Un bel modo per rendere 'rowid' è usare' rowid: = .I' –

2

Poiché non sembra tale gruppo abbia a che fare con il calcolo della media ponderata, ho cercato di semplificare il problema un po '.

 values <- seq(4) 

# A function to compute a string of length 4 with random weights 0 or 10 
    tstwts <- function() 
    { 
     w <- sample(c(0, 10), 4, replace = TRUE) 
     paste0("{", paste(w, collapse = ","), "}") 
    } 

# Generate 100K strings and put them into a vector 
    u <- replicate(1e5, tstwts()) 
    head(u) # Check 
    table(u) 

# Function to compute a weighted mean from a string using values 
# as an assumed external numeric vector 'values' of the same length as 
# the weights 
    f <- function(x) 
     { 
      valstr <- gsub("[\\{\\}]", "", x) 
      wts <- as.numeric(unlist(strsplit(valstr, ","))) 
      sum(wts * values)/sum(wts) 
     } 

# Execute the function f recursively on the vector of weights u 
    v <- sapply(u, f) 

# Some checks: 
    head(v) 
    table(v) 

Sul mio sistema, per 100K ripetizioni,

> system.time(sapply(u, f)) 
    user system elapsed 
    3.79 0.00 3.83 

Una versione tabella di dati di questo (gruppi sans) sarebbe

DT <- data.table(weights = u) 
DT[, wt.mean := lapply(weights, f)]) 
head(DT) 
dim(DT) 

Sul mio sistema, questo prende

system.time (DT [, wt.mean: = lapply (pesi, f)]) sistema utente intercorso 3,62 0,03 3,69

quindi aspettare circa 35-40 s per milione osservazioni su un sistema paragonabile al mio (Win7, 2,8GHz Chip dual core, 8GB RAM). YMMV.