2017-01-11 6 views
2

У меня есть большое текстовое тело, где я хочу эффективно заменить слова своими синонимами (например, заменить все вхождения «автомобиля» синонимом «автомобиль»). Но я изо всех сил стараюсь найти правильный (эффективный способ) для этого.Заменить слова в text2vec эффективно

Для последующего анализа я использую библиотеку text2vec и хотел бы использовать эту библиотеку для этой задачи (избегая tm, чтобы уменьшить зависимости).

An (неэффективный) способ будет выглядеть следующим образом:

# 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")) 
) 

Мое решение перебором, чтобы иметь что-то вроде этого, и использовать цикл для поиска слов и заменить их

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" 

Я использовал это с tm, используя этот подход by MrFlick (используя tm::content_transformer и tm::tm_map), но я хочу уменьшить зависимости проекта, заменив tm тем быстрее text2vec.

Я думаю, оптимальным решением было бы как-то использовать text2vec s itoken, но я не уверен, как это сделать. Есть идеи?

ответ

3

Довольно поздно, но все-таки я хочу добавить мои 2 цента. Я вижу 2 решения

  1. небольшое улучшение над вашим str_replace_all. Поскольку он внутренне векторизован, вы можете сделать все замены без цикла. Я думаю, что это будет быстрее, но я не сделал никаких тестов.

    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. Естественно, эта задача предназначена для поиска хеш-таблиц. Насколько я знаю, самая быстрая реализация в пакете fastmatch. Таким образом, вы можете написать пользовательские токенизатор, что-то вроде:

    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 
        }) 
    } 
    

я бы пари, что второе решение будет работать быстрее, но нужно сделать несколько тестов.

+1

Это похоже на очень интересную концепцию! Я сделал несколько быстрых тестов на них. В то время как на небольших синонимах-образцах for-loop работает быстрее, подход 'fastmatch' намного быстрее в больших списках! Кроме того, поскольку я все еще работаю над проектом, ваши 2 цента очень ценны здесь! – David

+1

Также обратите внимание, что 'text2vec :: word_tokenizer' довольно медленный по сравнению с' stringr :: str_split (TEXT_HERE, pattern = stringr :: border ("word")) '. Единственная причина, по которой я не использую 'stringr' /' stringi'/'tokenizers', - это то, что я хочу, чтобы количество зависимостей' text2vec' было как можно меньше. –

2

С базой R это должно работать:

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

Разве это не похоже на цикл, который я разместил? Просто замените 'stringr :: str_replace_all' на' gsub'? – David

+0

Это так, но поскольку вы хотели уменьшить зависимости, я решил, что вы хотите базовое решение R. Вы проверили [это] (http://stackoverflow.com/questions/29273928/faster-approach-than-gsub-in-r)? – count

+0

Я знал о 'perl = T' (который действительно добавляет скорость), и он кажется быстрее, чем' stringr'. Но все же, мне интересно, предлагает ли 'text2vec' более быструю альтернативу (используя токены ...) – David

1

Первая часть решения Дмитрия Селиванова требует небольшого изменения.

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"