Podziel kolumnę łańcuchową na kilka zmiennych obojętnych

Jako stosunkowo niedoświadczony użytkownik pakietu data.table w R, próbowałem przetworzyć jedną kolumnę tekstową na dużą liczbę kolumn wskaźnikowych (zmiennych obojętnych), przy czym 1 w każdej kolumnie wskazuje, że dany podciąg był znalezione w kolumnie ciągu. Na przykład chcę to przetworzyć:

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

zaangażowany w to:

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

Zorientowałem się, jak wykonać przetwarzanie, ale uruchomienie zajmuje więcej czasu, niż bym chciał, i podejrzewam, że mój kod jest nieefektywny. Poniżej znajduje się odtwarzalna wersja mojego kodu z fałszywymi danymi. Zauważ, że w rzeczywistych danych istnieje ponad 2000 podciągów do wyszukania, każdy podciąg ma około 30 znaków, a może być nawet kilka milionów wierszy. Jeśli zajdzie taka potrzeba, mogę zsynchronizować i rzucić wiele zasobów na problem, ale chcę zoptymalizować kod w jak największym stopniu. Próbowałem uruchomić Rprof, co sugeruje brak oczywistych (dla mnie) ulepszeń.

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 

EDYTOWAĆ

Dziękuję za wspaniałe odpowiedzi - znacznie lepsze od mojej metody. Wyniki niektórych testów prędkości poniżej, z niewielkimi modyfikacjami każdej funkcji, aby użyć 0L i 1L w moim własnym kodzie, aby zapisać wyniki w oddzielnych tabelach według metody i ujednolicić zamawianie. Są to czasy, które upłynęły od testów pojedynczej prędkości (zamiast median z wielu testów), ale każdy z większych przebiegów zajmuje dużo czasu.

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

Dość wyraźnie, zmodyfikowana wersja podejścia GeekTradera jest najlepsza. Nadal jestem trochę niejasny, co robi każdy krok, ale mogę się nad tym zastanawiać. Chociaż jest to nieco poza zakresem pierwotnego pytania, jeśli ktoś chce wyjaśnić, co metody GeekTrader i Ricardo Saporta działają bardziej efektywnie, byłoby to docenione zarówno przeze mnie, jak i prawdopodobnie przez każdego, kto odwiedza tę stronę w przyszłości. Jestem szczególnie zainteresowany zrozumieniem, dlaczego niektóre metody są lepsze niż inne.

***** EDYCJA # 2 *****

Próbowałem edytować odpowiedź GeekTradera tym komentarzem, ale to nie działa. Dokonałem dwóch bardzo drobnych modyfikacji funkcji GT3, aby a) uporządkować kolumny, które dodają niewielką ilość czasu, oraz b) zastąpić 0 i 1 0L i 1L, co nieco przyspiesza. Wywołaj wynikową funkcję GT4. Tabela powyżej edytowana, aby dodać czasy dla GT4 przy różnych rozmiarach tabeli. Wyraźnie zwycięzca o milę i ma dodatkową zaletę, będąc intuicyjnym.

questionAnswers(6)

yourAnswerToTheQuestion