Come utente relativamente inesperto del pacchetto data.table in R, ho cercato di elaborare una colonna di testo in un gran numero di colonne di indicatori, con un 1 in ciascuna colonna che indica che una particolare sottostringa è stata trovata all'interno della colonna di stringhe. Per esempio, io voglio elaborare questo:Migliora la velocità di elaborazione del testo usando R e data.table
ID String
1 a$b
2 b$c
3 c
in questo:
ID String a b c
1 a$b 1 1 0
2 b$c 0 1 1
3 c 0 0 1
ho capito come fare il trattamento, ma ci vuole più tempo per l'esecuzione di quanto vorrei, e ho il sospetto che il mio codice è inefficiente. Di seguito è riportata una versione riproducibile del mio codice con dati fittizi. Si noti che nei dati reali sono presenti oltre 2000 sottostringhe da ricercare, ciascuna sottostringa ha una lunghezza di circa 30 caratteri e può contenere fino a qualche milione di righe. Se necessario, posso parallelizzare e lanciare molte risorse al problema, ma voglio ottimizzare il codice il più possibile. Ho provato a eseguire Rprof, che non suggeriva miglioramenti evidenti (per me).
set.seed(10)
elements_list <- c(outer(letters, letters, FUN = paste, sep = ""))
random_string <- function(min_length, max_length, separator) {
selection <- paste(sample(elements_list, ceiling(runif(1, min_length, max_length))), collapse = separator)
return(selection)
}
dt <- data.table(id = c(1:1000), messy_string = "")
dt[ , messy_string := random_string(2, 5, "$"), by = id]
create_indicators <- function(search_list, searched_string) {
y <- rep(0, length(search_list))
for(j in 1:length(search_list)) {
x <- regexpr(search_list[j], searched_string)
x <- x[1]
y[j] <- ifelse(x > 0, 1, 0)
}
return(y)
}
timer <- proc.time()
indicators <- matrix(0, nrow = nrow(dt), ncol = length(elements_list))
for(n in 1:nrow(dt)) {
indicators[n, ] <- dt[n, create_indicators(elements_list, messy_string)]
}
indicators <- data.table(indicators)
setnames(indicators, elements_list)
dt <- cbind(dt, indicators)
proc.time() - timer
user system elapsed
13.17 0.08 13.29
EDIT
Grazie per le grandi risposte - tutte molto superiori al mio metodo. I risultati di alcuni test di velocità di seguito, con lievi modifiche a ciascuna funzione per utilizzare 0L e 1L nel mio codice, per memorizzare i risultati in tabelle separate per metodo e per standardizzare l'ordine. Questi sono i tempi trascorsi dai test a velocità singola (piuttosto che le mediane da molti test), ma le corse più lunghe richiedono tempo.
Number of rows in dt 2K 10K 50K 250K 1M
OP 28.6 149.2 717.0
eddi 5.1 24.6 144.8 1950.3
RS 1.8 6.7 29.7 171.9 702.5
Original GT 1.4 7.4 57.5 809.4
Modified GT 0.7 3.9 18.1 115.2 473.9
GT4 0.1 0.4 2.26 16.9 86.9
Abbastanza chiaramente, la versione modificata dell'approccio di GeekTrader è la migliore. Sono ancora un po 'vago su cosa stia facendo ogni passo, ma posso esaminarlo a mio piacimento. Anche se un po 'fuori dai limiti della domanda originale, se qualcuno vuole spiegare quali metodi di GeekTrader e Ricardo Saporta stanno facendo in modo più efficiente, sarebbe apprezzato sia da me che probabilmente da chiunque visiti questa pagina in futuro. Sono particolarmente interessato a capire perché alcuni metodi scalano meglio di altri.
* EDIT # 2 ***
ho provato a modificare la risposta di GeekTrader con questo commento, ma che sembra non funzionare. Ho apportato due lievi modifiche alla funzione GT3, a) ordinare le colonne, che aggiungono una piccola quantità di tempo, e b) sostituire 0 e 1 con 0L e 1L, il che accelera un po 'le cose. Chiama la funzione risultante GT4. Tabella sopra modificata per aggiungere tempi per GT4 a diverse dimensioni di tabella. Chiaramente il vincitore di un miglio, e ha il vantaggio aggiunto di essere intuitivo.
Aggiornato con la versione 3 che è molto più veloce e molto più efficiente in termini di memoria –
Questa è una grande domanda con risposte fantastiche. Nei tuoi benchmark, è 'Modified GT' GT3? Se è così, non sono in grado di ottenere la velocità 10x quando implemento GT4 cambiando 0 e 1 a 0L e 1L. – mchangun