Текстовый анализ в R через quanteda и tm
Привет, Хабр!
В этой статье я расскажу вам о том, как можно анализировать текстовые данные в R, используя библиотеки quanteda и tm.
Зачем вообще quanteda и tm, и почему не Python?
Справедливый вопрос: все вокруг любят spaCy
, nltk
, gensim
в Python, а тут мы про R. R отлично подходит для быстрых статистических вычислений, имеет удобный синтаксис для манипуляций с данными, а самое главное — пакеты quanteda
и tm
дают обширный функционал для лингвистического анализа. Если вы уже знакомы с tidyverse, тогда работа с текстовыми данными покажется довольно «нативной». Кроме того, quanteda — это универсал для текстов: токенизация, стоп‑слова, stemming, лемматизация (через доп. инструменты), анализ частот, биграмм, триграмм, построение документно‑терминных матриц, расчеты статистических метрик, и многое другое.
А вот tm
используется как классический инструмент для обработки текстовых корпусов, построения DTM, очистки и преобразования.
Начнем: установка и загрузка библиотек
Для начала устанавливаем пакеты:
install.packages("quanteda")
install.packages("tm")
install.packages("SnowballC") # Для стемминга
install.packages("wordcloud") # Почему бы и нет, для наглядности
install.packages("textstem") # Для лемматизации
install.packages("readtext") # Удобный импорт текстов
install.packages("dplyr") # Ну куда без него
Подтягиваем их в сессию:
library(quanteda)
library(tm)
library(SnowballC)
library(wordcloud)
library(textstem)
library(readtext)
library(dplyr)
Загрузка данных
Предположим, есть папка с текстовыми файлами новостных статей на русском языке. Для полноты картины назовем ее ./data/articles/
. Каждый файл — отдельная статья в формате .txt
. Можно использовать readtext
для удобного чтения сразу пачки файлов:
# Допустим, наши файлы в ./data/articles/*.txt
text_data <- readtext("./data/articles/*.txt", encoding = "UTF-8")
head(text_data)
Это создаст фрейм данных, где будет колонка text
. Если хочется максимально безопасный код, проверяем наличие файлов, кодировки и т. п. Но сейчас будем считать, что все данные корректны.
Создаем корпус и начинаем токенизировать
quanteda
позволяет легко создать корпус — удобную структуру для дальнейшего анализа:
corpus_data <- corpus(text_data, text_field = "text")
summary(corpus_data)
Сейчас у нас корпус текстов. Дальше — токенизация. Представим, что нужны только слова, все знаки препинания — прочь, цифры вычеркиваем, приведение к нижнему регистру — обязательно.
tokens_data <- tokens(corpus_data,
remove_punct = TRUE,
remove_numbers = TRUE)
tokens_data <- tokens_tolower(tokens_data)
Теперь есть токены — самая базовая структурная единица. Их можно просмотреть:
head(tokens_data[[1]], 50) # первые 50 токенов первого документа
Стоп-слова и стемминг/лемматизация
Русский язык богат, и иногда хочется выкинуть из анализа общеупотребительные слова. В quanteda есть список стоп‑слов для некоторых языков, но для русского он может быть неполон. Подгрузим собственный список стоп‑слов. Предположим, есть файл stopwords_ru.txt
со стоп‑словами по одному на строку:
my_stopwords <- readLines("stopwords_ru.txt", encoding = "UTF-8")
И удалим эти слова из токенов:
tokens_clean <- tokens_select(tokens_data, pattern = my_stopwords, selection = "remove")
Далее — стемминг или лемматизация. Стемминг приводит слова к их «обрубленной» форме (корню), а лемматизация пытается найти «словарную» форму слова. Для русского языка лемматизация более осмысленна, но требует дополнительных библиотек. textstem
в R может не идеально работать с русским, но можно попробовать:
lemma_fun <- function(x) lemmatize_words(x, language = "russian")
tokens_lemma <- tokens_apply(tokens_clean, lemma_fun)
Если лемматизация не устраивает, можно применить стемминг через SnowballC:
tokens_stemmed <- tokens_wordstem(tokens_clean, language = "russian")
В итоге, есть более нормализованные токены.
Переход к количественным характеристикам: Document-Term Matrix
Для статистического анализа нужна матрица документ‑термин. В quanteda это проще пареной репы:
dtm <- dfm(tokens_stemmed)
dtm
dfm
(document‑feature matrix) — внутренний тип quanteda, очень удобен. Можем посмотреть самые частотные слова:
topfeatures(dtm, 20)
А если хочется классический формат из tm
, то можно преобразовать:
dtm_tm <- convert(dtm, to = "tm")
dtm_tm
Очистка DTM: частотный фильтр, отсеивание редких слов
Далеко не всегда нужны все слова. Некоторые термины встречаются один раз на миллион документов и лишь засоряют модель. Отсеим редко встречающиеся слова:
dtm_trimmed <- dfm_trim(dtm, min_docfreq = 5, min_termfreq = 10)
Теперь меньше редкого шума. Можно также отфильтровать слишком частые слова (которые встречаются в 90% документов):
dtm_filtered <- dfm_trim(dtm_trimmed, max_docfreq = 0.9, docfreq_type = "prop")
Анализ: частоты, ассоциации, коллокации
Порой интересно посмотреть на коллокации — устойчивые словосочетания, биграммы, триграммы. В quanteda это очень просто:
collocations <- textstat_collocations(tokens_stemmed, size = 2:3, min_count = 5)
head(collocations, 20)
Это выведет топ коллокаций. Если хотим их использовать как единые токены (т. е. «machine learning» превращаем в «machine_learning»):
tokens_coll <- tokens_compound(tokens_stemmed, collocations, join = TRUE)
Визуализация частот
Можно сделать простой wordcloud, чтобы оценить популярные термины:
set.seed(123)
textplot_wordcloud(dtm_filtered, max_words = 100, color = RColorBrewer::brewer.pal(8, "Dark2"))
Да, wordcloud — это уже классика, но иногда он помогает быстро ухватить суть.
TF-IDF и прочие метрики
Частоты — хорошо, но можно сделать хитрее. Частотный показатель TF‑IDF помогает выделить важные слова для конкретного документа. В quanteda делается элементарно:
dtm_tfidf <- dfm_tfidf(dtm_filtered, scheme_tf = "count", scheme_df = "inverse")
topfeatures(dtm_tfidf, 20)
Это даст слова с максимальной уникальностью.
Топик-моделирование (LDA)
Стоит попробовать что‑то поглубже. Например, LDA — очень распространенный метод для выделения тем в текстах. В quanteda можно легко использовать встроенные функции или обратиться к пакету topicmodels
. Допустим, есть dfm, и нужно сделать простенькую LDA:
library(topicmodels)
# Пусть у нас 5 тем (k=5)
lda_model <- LDA(convert(dtm_filtered, to = "topicmodels"), k = 5, control = list(seed = 123))
lda_topics <- topics(lda_model)
lda_terms <- terms(lda_model, 10)
lda_terms
Это даст топ‑термины для каждой из 5 тем. Конечно, настройка параметров LDA — отдельное искусство. Но для старта пойдет.
Сентимент-анализ
Если хочется немного оценить тональность текстов (хотя с русским языком посложнее, чем с английским), можно попробовать подключить заранее подготовленные словари тональности. Предположим, есть список позитивных и негативных слов. Можно подсчитать, сколько позитивных/негативных встречается:
pos_words <- c("отличный", "замечательный", "хороший", "прекрасный")
neg_words <- c("ужасный", "плохой", "отвратительный")
sentiment_scores <- dfm_select(dtm_filtered, pattern = c(pos_words, neg_words))
Теперь можно подсчитать суммарную «тональность» каждого документа:
sentiment_df <- convert(sentiment_scores, to = "data.frame")
sentiment_df$sentiment_score <- rowSums(sentiment_df[, pos_words]) - rowSums(sentiment_df[, neg_words])
head(sentiment_df)
Это примитивный подход, но для демонстрации сойдет.
Производительность
При работе с большими корпусами может оказаться, что R начинает тормозить. Quanteda оптимизирован под C++ и может работать довольно шустро. Но все равно, если есть десятки миллионов документов, стоит подумать о параллелизации или блочном анализе. Многие функции quanteda можно распараллелить с помощью quanteda.textmodels
или других пакетов.
Например, при токенизации можно указать what = "word"
или verbose = TRUE
для профилирования, можно разбить большой корпус на батчи и их обрабатывать. В продакшене часто используют комбинацию R + Spark (через sparklyr
) или Python для распределенных вычислений. Но quanteda хорош для интерактивного анализа или средних по размеру наборов.
Заключение
Итак, tm
— более старый пакет, предлагает Corpus
, DocumentTermMatrix
, TermDocumentMatrix
объекты. quanteda
— быстрее, гибче, удобнее. Предлагает corpus
, tokens
, dfm
объекты. quanteda
имеет более современный API и часто обновляется.
Если вы только стартуете в текстовом анализе на R, я рекомендую перейти сразу к quanteda — он в большинстве случаев покрывает функционал tm.
Не стесняйтесь экспериментировать: меняйте параметры токенизации, добавляйте свои стоп‑слова, пытайтесь найти скрытые темы в своих данных. Смотрите, как меняется результат при разных метриках.
Больше актуальных навыков по аналитике вы можете получить в рамках практических онлайн-курсов от экспертов отрасли.
Также приходите на открытые уроки, которые совсем скоро пройдут в рамках набора учащихся:
27 декабря — «Визуализация данных. Основные «финансовые» графики, работа с mplfinance». Подробнее
13 января — «Визуализация данных на Python». Подробнее