#r #text-mining
#r #интеллектуальный анализ текста
Вопрос:
У меня есть набор текстовых данных, и я хочу выполнить поиск различных слов в нем, а затем отметить их, когда, когда я их найду. Вот пример данных:
df <- data.table("id" = c(1:3), "report" = c("Travel opens our eyes to art, history, and culture – but it also introduces us to culinary adventures we may have never imagined otherwise."
, "We quickly observed that no one in Sicily cooks with recipes (just with the heart), so we now do the same."
, "We quickly observed that no one in Sicily cooks with recipes so we now do the same."), "summary" = c("On our first trip to Sicily to discover our family roots,"
, "If you’re not a gardener, an Internet search for where to find zucchini flowers results."
, "add some fresh cream to make the mixture a bit more liquid,"))
До сих пор я использовал SQL для обработки этого, но это становится сложным, когда у вас есть большой список слов для поиска.
dfOne <- sqldf("select id
, case when lower(report) like '%opens%' then 1 else 0 end as opens
, case when lower(report) like '%cooks%' then 1 else 0 end as cooks
, case when lower(report) like '%internet%' then 1 else 0 end as internet
, case when lower(report) like '%zucchini%' then 1 else 0 end as zucchini
, case when lower(report) like '%fresh%' then 1 else 0 end as fresh
from df
")
Я ищу идеи, как сделать это более эффективным способом. Представьте, что если у вас есть длинный список целевых терминов, этот код может стать излишне длинным.
Спасибо,
SM.
Ответ №1:
1) sqldf
Определите вектор слов, а затем преобразуйте его в SQL. Обратите внимание, что это case when
не требуется, поскольку like
уже выдает результат 0/1. Предварение sqldf
с fn$
позволяет $like
заменить строку символов R like
в инструкции SQL. Используйте verbose=TRUE
аргумент to sqldf
, чтобы просмотреть сгенерированную инструкцию SQL. Это всего лишь две строки кода, независимо от того, какой длины words
.
words <- c("opens", "cooks", "internet", "zucchini", "fresh", "test me")
like <- toString(sprintf("nlower(report) like '%%%s%%' as '%s'", words, words))
fn$sqldf("select id, $like from df", verbose = TRUE)
предоставление:
id opens cooks internet zucchini fresh test me
1 1 1 0 0 0 0 0
2 2 0 1 0 0 0 0
3 3 0 1 0 0 0 0
2) внешний
Используя words
вышеизложенное, мы можем использовать outer
следующим образом. Обратите внимание, что функция (третий аргумент) во внешнем должна быть векторизована, и мы можем сделать grepl
векторизованную, как показано. Опустите check.names = FALSE
, если не возражаете, имена столбцов, связанные со словами, в которых пробелы или знаки препинания вставлены в синтаксические имена переменных R. Это приводит к тому же результату, что и (1).
with(df, data.frame(
id,
t(outer(setNames(words, words), report, Vectorize(grepl))),
check.names = FALSE
))
3) sapply
Используя sapply
, мы можем получить немного более короткое решение в тех же строках, что и (2). Результат такой же, как в (1) и (2).
with(df, data.frame(id, sapply(words, grepl, report), check.names = FALSE))
Комментарии:
1. Мне нравится, что ваш код состоит из нескольких строк. Я протестирую это и приму решение, если оно подтвердится. Большое вам спасибо за то, что согласились.
2. Спасибо Г. Гротендику. Решение 1 завершается ошибкой, когда что-то вроде этого «test-me» или «test me» используется в списке слов « слова <- c(«открывает», «готовит», «интернет», «цуккини», «свежий», «протестируй меня»)« . Два других работают нормально и создают столбцы с именами test.me . Я думаю, что первое связано с соглашениями об именовании имен столбцов в SQLLite?
3. В вопросе, похоже, не было определения слов как имеющих знаки препинания или пробелы, но если у вас есть такие, просто заключите имя столбца в кавычки в инструкции SQL. Я изменил это выше, чтобы сделать это. Также добавьте check.names=FALSE в (2) и (3), если вы не хотите, чтобы имя менялось — что мы также сделали выше.
4. Еще раз спасибо. Я изучу эти функции и пойму, что они делают под капотом.
Ответ №2:
Вот простой способ. Предполагается, что вы хотите выполнить поиск по двум отдельным столбцам.
library(tidyverse)
df <- tibble(id = c(1:3), report = c("Travel opens our eyes to art, history, and culture – but it also introduces us to culinary adventures we may have never imagined otherwise."
, "We quickly observed that no one in Sicily cooks with recipes (just with the heart), so we now do the same."
, "We quickly observed that no one in Sicily cooks with recipes so we now do the same."),
summary = c("On our first trip to Sicily to discover our family roots,"
, "If you’re not a gardener, an Internet search for where to find zucchini flowers results."
, "add some fresh cream to make the mixture a bit more liquid,"))
# Vector of words
vec <- c('eyes','art','gardener','mixture','trip')
df %>%
mutate(reportFlag = case_when(
str_detect(report,paste(vec,collapse = '|')) ~ T,
T ~ F)
) %>%
mutate(summaryFlag = case_when(
str_detect(report,paste(vec,collapse = '|')) ~ T,
T ~ F))
Комментарии:
1. хорошо, это выглядит как элегантное решение. Однако я пока не могу это протестировать, у меня возникли проблемы с установкой tidyverse. Предупреждение в install.packages : установка пакета ‘reprex’ имела ненулевую ошибку статуса завершения: зависимости ‘broom’, ‘rlang’, ‘tidyselect’, ‘vctrs’ недоступны для пакета ‘modelr’. Я решу эту проблему и оценю ваше решение. В то же время, что делают ~ T, T, F?
2. Да, это какой-то не совсем интуитивный
case_when
синтаксис. Первый аргументstr_detect...
ищет слова и помечает T, если они найдены. Если нет, то все остальные случаи отмечены знаком F. Это последний T.3. Создает ли ваше решение 5 новых столбцов, как это делает мой sql? Каждый столбец для каждого слова? Отображение 1 для строки или регистра с этим словом и 0 в противном случае?