2017-01-11 2 views
2

Ich habe einen großen Textkörper, wo ich Wörter mit ihren entsprechenden Synonyme effizient ersetzen möchte (zum Beispiel ersetzen alle Vorkommen von "Automobil" mit dem Synonym "Auto"). Aber ich habe Mühe, einen geeigneten (effizienten) Weg zu finden, dies zu tun.Ersetzen Sie Wörter in text2vec effizient

Für die spätere Analyse verwende ich die text2vec Bibliothek und möchte diese Bibliothek auch für diese Aufgabe verwenden (unter Vermeidung von tm Abhängigkeiten zu reduzieren).

An (ineffizient) Art und Weise würde wie folgt aussehen:

# setup data 
text <- c("my automobile is quite nice", "I like my car") 

syns <- list(
    list(term = "happy_emotion", syns = c("nice", "like")), 
    list(term = "car", syns = c("automobile")) 
) 

My Brute-Force-Lösung ist, so etwas zu haben und eine Schleife verwenden, um die Worte zu suchen und sie

library(stringr) 
# works but is probably not the best... 
text_res <- text 
for (syn in syns) { 
    regex <- paste(syn$syns, collapse = "|") 
    text_res <- str_replace_all(text_res, pattern = regex, replacement = syn$term) 
} 
# which gives me what I want 
text_res 
# [1] "my car is quite happy_emotion" "I happy_emotion my car" 
ersetzen

Ich habe es mit tm mit diesem Ansatz by MrFlick (mit tm::content_transformer und tm::tm_map) gemacht, aber ich möchte die Abhängigkeiten des Projekts durch Ersetzen von tm durch die schnellere reduzieren text2vec.

Ich denke, die optimale Lösung wäre, irgendwie zu verwenden text2vec s itoken, aber ich bin mir nicht sicher, wie. Irgendwelche Ideen?

Antwort

3

ziemlich spät, aber noch will ich meine 2 Cent hinzuzufügen. Ich sehe zwei Lösungen

  1. Kleine Verbesserung gegenüber Ihren str_replace_all. Da es intern vektorisiert wird, können Sie alle Ersetzungen ohne Schleife durchführen. Ich denke, es wird schneller, aber ich habe keine Benchmarks gemacht.

    regex_batch = sapply(syns, function(syn) paste(syn$syns, collapse = "|")) 
    names(regex_batch) = sapply(syns, function(x) x$term) 
    str_replace_all(text, regex_batch) 
    
  2. Natürlich ist diese Aufgabe für Hash-Table-Lookup. Die schnellste Implementierung, soweit ich weiß, ist in fastmatch Paket. So können Sie schreiben, benutzerdefinierte tokenizer, so etwas wie:

    library(fastmatch) 
    
    syn_1 = c("nice", "like") 
    names(syn_1) = rep('happy_emotion', length(syn_1)) 
    syn_2 = c("automobile") 
    names(syn_2) = rep('car', length(syn_2)) 
    
    syn_replace_table = c(syn_1, syn_2) 
    
    custom_tokenizer = function(text) { 
        word_tokenizer(text) %>% lapply(function(x) { 
        i = fmatch(x, syn_replace_table) 
        ind = !is.na(i) 
        i = na.omit(i) 
        x[ind] = names(syn_replace_table)[i] 
        x 
        }) 
    } 
    

ich, dass die zweite Lösung funktioniert schneller wetten würde, aber müssen einige Benchmarks machen.

+1

Das sieht nach einem sehr interessanten Konzept aus! Ich habe ein paar schnelle Benchmarks auf ihnen gemacht. Während bei kleinen Synonym-Samples die For-Schleife schneller ist, ist der 'Fastmatch'-Ansatz auf größeren Listen viel schneller! Da ich noch am Projekt arbeite, sind deine 2 Cent hier sehr wertvoll! – David

+1

Beachten Sie auch, dass 'text2vec :: word_tokenizer' ziemlich langsam ist im Vergleich zu 'stringr :: str_split (TEXT_HERE, pattern = stringr :: boundary (" word "))'. Der einzige Grund, warum ich nicht 'striplr' /' stringi'/'tokenizers' verwende, ist, dass ich die Anzahl der' text2vec' Abhängigkeiten so klein wie möglich halten möchte. –

2

Mit Base R sollte diese Arbeit:

mgsub <- function(pattern,replacement,x) { 
if (length(pattern) != length(replacement)){ 
    stop("Pattern not equal to Replacment") 
} 
    for (v in 1:length(pattern)) { 
     x <- gsub(pattern[v],replacement[v],x) 
    } 
return(x) 
} 

mgsub(c("nice","like","automobile"),c(rep("happy_emotion",2),"car"),text) 
+0

Ist das nicht sehr ähnlich der Schleife, die ich gepostet habe? Ersetzen Sie einfach 'stringr :: str_replace_all' durch' gsub'? – David

+0

Es ist, aber da Sie Abhängigkeiten reduzieren wollten, dachte ich, dass Sie eine Basis-R-Lösung wollten. Haben Sie [diese] (http://stackoverflow.com/questions/29273928/faster-approach-than-gsub-in-r) ausgecheckt? – count

+0

Mir war das 'perl = T' bekannt (welches tatsächlich Geschwindigkeit hinzufügt), und es scheint schneller zu sein als' stringr'. Aber ich frage mich, ob 'text2vec' eine schnellere Alternative bietet (mit den Tokens ...) – David

1

Der erste Teil der Lösung von Dmitriy Selivanov erfordert eine kleine Änderung.

library(stringr)  

text <- c("my automobile is quite nice", "I like my car") 

syns <- list(
      list(term = "happy_emotion", syns = c("nice", "like")), 
      list(term = "car", syns = c("automobile")) 
      ) 

regex_batch <- sapply(syns, function(syn) syn$term) 
names(regex_batch) <- sapply(syns, function(x) paste(x$syns, collapse = "|")) 
text_res <- str_replace_all(text, regex_batch) 

text_res 
[1] "my car is quite happy_emotion" "I happy_emotion my car" 
Verwandte Themen