Вот призыв к лучшему способу сделать то, что я уже могу делать неэффективно: отфильтровать серию n-граммовых токенов с помощью «стоп-слов», чтобы вхождение любого термина стоп-слова в n- грамм триггеров удаления.
Я бы очень хотел иметь одно решение, которое работает как с униграммами, так и с n-граммами, хотя было бы нормально иметь две версии, одну с «фиксированным» флагом и одну с флагом «регулярное выражение». Я объединяю два аспекта вопроса, поскольку у кого-то может быть решение, которое пробует другой подход, который касается как фиксированных, так и регулярных шаблонов стоп-слов.
Форматы:
токены - это список векторов символов, которые могут быть униграммами или n-граммами, объединенными символом
_
(подчеркивание).стоп-слова - это вектор символов. Прямо сейчас я доволен тем, что это будет фиксированная строка, но было бы неплохим бонусом, если бы я мог реализовать это, используя также стоп-слова, отформатированные с помощью регулярных выражений.
Желаемый результат: список символов, соответствующих входным токенам, но при этом удаляется любой компонентный токен, соответствующий стоп-слову. (Это означает совпадение униграммы или совпадение с одним из терминов, входящих в n-грамм.)
Примеры, тестовые данные, рабочий код и тесты, на которых можно опираться:
tokens1 <- list(text1 = c("this", "is", "a", "test", "text", "with", "a", "few", "words"),
text2 = c("some", "more", "words", "in", "this", "test", "text"))
tokens2 <- list(text1 = c("this_is", "is_a", "a_test", "test_text", "text_with", "with_a", "a_few", "few_words"),
text2 = c("some_more", "more_words", "words_in", "in_this", "this_text", "text_text"))
tokens3 <- list(text1 = c("this_is_a", "is_a_test", "a_test_text", "test_text_with", "text_with_a", "with_a_few", "a_few_words"),
text2 = c("some_more_words", "more_words_in", "words_in_this", "in_this_text", "this_text_text"))
stopwords <- c("is", "a", "in", "this")
# remove any single token that matches a stopword
removeTokensOP1 <- function(w, stopwords) {
lapply(w, function(x) x[-which(x %in% stopwords)])
}
# remove any word pair where a single word contains a stopword
removeTokensOP2 <- function(w, stopwords) {
matchPattern <- paste0("(^|_)", paste(stopwords, collapse = "(_|$)|(^|_)"), "(_|$)")
lapply(w, function(x) x[-grep(matchPattern, x)])
}
removeTokensOP1(tokens1, stopwords)
## $text1
## [1] "test" "text" "with" "few" "words"
##
## $text2
## [1] "some" "more" "words" "test" "text"
removeTokensOP2(tokens1, stopwords)
## $text1
## [1] "test" "text" "with" "few" "words"
##
## $text2
## [1] "some" "more" "words" "test" "text"
removeTokensOP2(tokens2, stopwords)
## $text1
## [1] "test_text" "text_with" "few_words"
##
## $text2
## [1] "some_more" "more_words" "text_text"
removeTokensOP2(tokens3, stopwords)
## $text1
## [1] "test_text_with"
##
## $text2
## [1] "some_more_words"
# performance benchmarks for answers to build on
require(microbenchmark)
microbenchmark(OP1_1 = removeTokensOP1(tokens1, stopwords),
OP2_1 = removeTokensOP2(tokens1, stopwords),
OP2_2 = removeTokensOP2(tokens2, stopwords),
OP2_3 = removeTokensOP2(tokens3, stopwords),
unit = "relative")
## Unit: relative
## expr min lq mean median uq max neval
## OP1_1 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 100
## OP2_1 5.119066 3.812845 3.438076 3.714492 3.547187 2.838351 100
## OP2_2 5.230429 3.903135 3.509935 3.790143 3.631305 2.510629 100
## OP2_3 5.204924 3.884746 3.578178 3.753979 3.553729 8.240244 100
grepl
для длинных векторов, написанных на c. да, я надеялся, что это тоже напишет:} @Rcore - person rawr   schedule 18.10.2015grepl
уже векторизован и написан на c.stringi::stri_detect_regex
иstringi::stri_detect_fixed
работают быстрее, и их стоит проверить. - person Zach   schedule 20.10.2015x <- rep('a', 1e7); system.time(grepl('a', x, fixed = TRUE)); system.time(stri_detect_fixed('a', x))
), но реальная тяжелая работа проходит через все комбинации, которые кажутся экспоненциально медленнее, когда у вас есть много шаблонов для сопоставления (добавление целей почти тривиально) - person rawr   schedule 20.10.2015