#r #text-mining #gsub #large-data
#r #интеллектуальный анализ текста #gsub #большие данные
Вопрос:
У меня есть большой корпус текста в виде вектора строк (около 700.000 строк). Я пытаюсь заменить определенные слова / фразы в корпусе. То есть у меня есть вектор из приложения 40.000 фраз и соответствующий вектор замен.
Я ищу эффективный способ решения проблемы
Я могу сделать это в цикле for, перебирая каждый шаблон замена. Но он плохо масштабируется (3 дня или около того!)
Я также пробовал qdap::mgsub(), но, похоже, он тоже плохо масштабируется
txt <- c("this is a random sentence containing bca sk",
"another senctence with bc a but also with zqx tt",
"this sentence contains non of the patterns",
"this sentence contains only bc a")
patterns <- c("abc sk", "bc a", "zqx tt")
replacements <- c("@a-specfic-tag-@abc sk",
"@a-specfic-tag-@bc a",
"@a-specfic-tag-@zqx tt")
#either
txt2 <- qdap::mgsub(patterns, replacements, txt)
#or
for(i in 1:length(patterns)){
txt <- gsub(patterns[i], replacements[i], txt)
}
Оба решения плохо масштабируются для моих данных с приложением 40.000 шаблонов / замен и 700.000 текстовых строк
Я полагаю, должен быть более эффективный способ сделать это?
Ответ №1:
Если вы можете сначала маркировать тексты, то векторизованная замена происходит намного быстрее. Это также быстрее, если а) вы можете использовать многопоточное решение и б) вы используете фиксированное сопоставление вместо регулярного выражения.
Вот как все это сделать в пакете quanteda. Последняя строка вставляет токены обратно в один «документ» в виде символьного вектора, если это то, что вы хотите.
library("quanteda")
## Package version: 1.4.3
## Parallel computing: 2 of 12 threads used.
## See https://quanteda.io for tutorials and examples.
##
## Attaching package: 'quanteda'
## The following object is masked from 'package:utils':
##
## View
quanteda_options(threads = 4)
txt <- c(
"this is a random sentence containing bca sk",
"another sentence with bc a but also with zqx tt",
"this sentence contains none of the patterns",
"this sentence contains only bc a"
)
patterns <- c("abc sk", "bc a", "zqx tt")
replacements <- c(
"@a-specfic-tag-@abc sk",
"@a-specfic-tag-@bc a",
"@a-specfic-tag-@zqx tt"
)
Это приведет к маркировке текстов, а затем к быстрой замене хэшированных типов с использованием фиксированного соответствия шаблону (но вы могли бы использовать valuetype = "regex"
для сопоставления с регулярным выражением). Перенося patterns
внутрь phrases()
функции, вы указываете tokens_replace()
искать последовательности токенов, а не отдельные совпадения, так что это решает проблему с несколькими словами.
toks <- tokens(txt) %>%
tokens_replace(phrase(patterns), replacements, valuetype = "fixed")
toks
## tokens from 4 documents.
## text1 :
## [1] "this" "is" "a" "random" "sentence"
## [6] "containing" "bca" "sk"
##
## text2 :
## [1] "another" "sentence"
## [3] "with" "@a-specfic-tag-@bc a"
## [5] "but" "also"
## [7] "with" "@a-specfic-tag-@zqx tt"
##
## text3 :
## [1] "this" "sentence" "contains" "none" "of" "the"
## [7] "patterns"
##
## text4 :
## [1] "this" "sentence" "contains"
## [4] "only" "@a-specfic-tag-@bc a"
Наконец, если вы действительно хотите перевести это обратно в символьный формат, преобразуйте в список типов символов, а затем вставьте их вместе.
sapply(as.list(toks), paste, collapse = " ")
## text1
## "this is a random sentence containing bca sk"
## text2
## "another sentence with @a-specfic-tag-@bc a but also with @a-specfic-tag-@zqx tt"
## text3
## "this sentence contains none of the patterns"
## text4
## "this sentence contains only @a-specfic-tag-@bc a"
Вам придется протестировать это на вашем большом корпусе, но 700 тысяч строк не кажутся слишком большой задачей. Пожалуйста, попробуйте это и сообщите, как это получилось!
Комментарии:
1. Привет, извините за поздний ответ. Мне нужно было выполнить срочную задачу. Я получаю следующую ошибку
Error in qatd_cpp_tokens_replace(x, type, ids_pat, ids_repl) : Not compatible with requested type: [type=NULL; target=double].
2. Об ошибках, пожалуйста, сообщайте по адресу github.com/quanteda/quanteda/issues .
Ответ №2:
Создайте вектор всех слов в каждой фразе
txt1 = strsplit(txt, " ")
words = unlist(txt1)
Используйте match()
, чтобы найти индекс слов для замены и заменить их
idx <- match(words, patterns)
words[!is.na(idx)] = replacements[idx[!is.na(idx)]]
Переформируйте фразы и вставьте вместе
phrases = relist(words, txt1)
updt = sapply(phrases, paste, collapse = " ")
Я думаю, это не сработает, если шаблоны могут содержать более одного слова…
Комментарии:
1. Извините, что не уточнил, шаблоны часто содержат больше слов.
Ответ №3:
Создайте сопоставление между старым и новым значениями
map <- setNames(replacements, patterns)
Создайте шаблон, который содержит все шаблоны в одном регулярном выражении
pattern = paste0("(", paste0(patterns, collapse="|"), ")")
Найдите все совпадения и извлеките их
ridx <- gregexpr(pattern, txt)
m <- regmatches(txt, ridx)
Отмените список, сопоставьте и повторно внесите совпадения в их заменяющие значения и обновите исходный вектор
regmatches(txt, ridx) <- relist(map[unlist(m)], m)
Комментарии:
1. Резервуары… Однако я получаю ошибку, в
r ridx <- gregexpr(pattern, txt)
сообщении об ошибкеr Error in gregexpr(pattern, txt) : assertion 'tree->num_tags == num_tags' failed in executing regexp: file 'tre-compile.c', line 634
2. Я бы предположил, что проблема слишком велика; вы могли бы попытаться рассмотреть подмножество шаблонов, чтобы увидеть, насколько осуществим подход; если это «работает», используя, скажем, 1000 шаблонов и быстрее, чем ваш текущий итерационный подход, тогда примените решение в «кусках» из 1000 шаблонов