Привет, Хабр!

В этой статье я расскажу вам о том, как можно анализировать текстовые данные в 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». Подробнее

Комментарии (0)