Teilen Sie eine String-Spalte in mehrere Dummy-Variablen auf

Als relativ unerfahrener Benutzer des data.table-Pakets in R habe ich versucht, eine Textspalte in eine große Anzahl von Anzeigespalten (Dummy-Variablen) zu verarbeiten, wobei eine 1 in jeder Spalte angibt, dass eine bestimmte Unterzeichenfolge vorhanden ist in der String-Spalte gefunden. Ich möchte zum Beispiel Folgendes verarbeiten:

ID     String  
1       a$b  
2       b$c  
3       c  

das sehr gut finden:

ID     String     a     b     c  
1       a$b       1     1     0  
2       b$c       0     1     1  
3        c        0     0     1  

Ich habe herausgefunden, wie die Verarbeitung durchgeführt wird, aber die Ausführung dauert länger als gewünscht, und ich vermute, dass mein Code ineffizient ist. Eine reproduzierbare Version meines Codes mit Dummy-Daten finden Sie weiter unten. Beachten Sie, dass in den realen Daten über 2000 Teilzeichenfolgen gesucht werden müssen, jede Teilzeichenfolge ungefähr 30 Zeichen lang ist und möglicherweise bis zu einige Millionen Zeilen vorhanden sind. Wenn nötig, kann ich viele Ressourcen parallelisieren und auf das Problem werfen, aber ich möchte den Code so weit wie möglich optimieren. Ich habe versucht, Rprof auszuführen, was keine offensichtlichen (für mich) Verbesserungen nahe legte.

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 

BEARBEITEN

Vielen Dank für die tollen Antworten, die meiner Methode weit überlegen sind. Die Ergebnisse einiger Geschwindigkeitstests unten, mit geringfügigen Änderungen an den einzelnen Funktionen, um 0L und 1L in meinem eigenen Code zu verwenden, die Ergebnisse nach Methode in separaten Tabellen zu speichern und die Reihenfolge zu standardisieren. Dies sind die abgelaufenen Zeiten von Einzelgeschwindigkeitstests (anstatt der Mediane von vielen Tests), aber die größeren Läufe dauern jeweils lange.

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

Ganz klar, die modifizierte Version des GeekTrader-Ansatzes ist am besten. Ich bin immer noch ein bisschen vage, was jeder Schritt tut, aber ich kann das nach Belieben durchgehen. Auch wenn dies etwas außerhalb der ursprünglichen Frage liegt, würde es mich und wahrscheinlich jeden, der diese Seite in Zukunft besucht, begrüßen, wenn jemand erklären möchte, wie GeekTrader und Ricardo Saporta effizienter vorgehen. Ich bin besonders daran interessiert zu verstehen, warum einige Methoden besser skalieren als andere.

***** BEARBEITEN # 2 *****

Ich habe versucht, die Antwort von GeekTrader mit diesem Kommentar zu bearbeiten, aber das scheint nicht zu funktionieren. Ich habe zwei geringfügige Änderungen an der GT3-Funktion vorgenommen: a) Ordnen Sie die Spalten, wodurch ein wenig Zeit hinzugefügt wird, und b) Ersetzen Sie 0 und 1 durch 0L und 1L, was die Dinge etwas beschleunigt. Rufen Sie die resultierende Funktion GT4 auf. Tabelle oben bearbeitet, um Zeiten für GT4 bei verschiedenen Tischgrößen hinzuzufügen. Eindeutig der Gewinner für eine Meile, und es hat den zusätzlichen Vorteil, intuitiv zu sein.

Antworten auf die Frage(6)

Ihre Antwort auf die Frage