Матрунич Консалтинг

Анализ данных, визуализация, маркетинговые исследования, язык R

Tag: wordcloud

Сравнение речей президентов России, Белоруссии и Украины с помощью облака слов

Сравнение поздравительных речей Путина, Лукашенко и Турчинова на День Победы

Сравним речи, которые произнесли руководители России, Белоруссии и Украины 9 мая 2014 года.
Сделаем это с помощью визуализации текстов в виде облака слов.

Облако слов со сравнением частот слов в речах Путина, Лукашенко и Турчинова

Облако слов со сравнением частот слов в речах Путина, Лукашенко и Турчинова

Как интерпретировать график? Размер шрифта, которым изображено слово, показывает относительную частоту, с которой данное слово встречалось в речи. Цвет показывает, в речи какого из президентов данное слово занимает наибольшую долю. Например, слово «победа» (и его формы) встречалось у Лукашенко 5 раз, у Путина – 3, у Турчинова – 5. Понятно, что слово должно попасть или к Лукашенко, или к Турчинову. Но у Лукашенко слово «победа» занимает 0,89% от массива учтённых слов, а у Турчинова – 2,8%, соответственно оно попадает в украинскую часть графика.

У Лукашенко много слов с маленьким размеров шрифта – это значит, что он говорил больше, чем остальные спикеры, размер словаря его выступления больше, и на каждое слово у белорусского президента приходится меньшая относительная частота. Действительно, у Лукашенко зафиксировано 562 слова, у Путина – 229, у Турчинова – 177.

Чем схожи выступления спикеров? Это можно проверить с помощью облака сходства. Здесь приведены слова, которые встречались одновременно у всех трёх выступающих. Чем больше размер шрифта, тем выше средняя доля встречаемости слова.

Облако сходства текстов выступлений Лукашенко, Путина и Турчинова на День Победы

Облако сходства текстов выступлений Лукашенко, Путина и Турчинова на День Победы

При подготовке из текстов выступлений были удалены стоп-слова (предлоги и пр.), а оставшиеся слова были приведены к исходной словоформе с помощью программы MyStem от компании Яндекс. Поскольку определение первичной словоформы происходит в автоматическом режиме без учёта контекста, некоторые слова преобразованы некорректно. Например, слово «дуг» в речи Путина в оригинале звучало как «Дуге». Обработка данных произведена в среде статистической обработки данных R. Более подробно о создании графиков в виде облака слов можно ознакомиться в публикации Обработка естественно-языковых текстов в R: облако слов.

Источники информации

Обработка естественно-языковых текстов в R: облако слов

Обработка естественно-языковых текстов (Natural language processing) – это одна из областей, в которых применяется R.

В этой публикации вы познакомитесь с базовыми инструментами анализа, основанного на данных о частоте встречаемости слов. В частности, мы рассмотрим функции из расширений tm и wordcloud: подготовим текстовые документы для частотного анализа и сделаем на их основе облако слов.

Материалом для работы у нас послужит выборка новостных сообщений о работе Псковского городского молодёжного центра, опубликованных в течение 2013 года в различных Интернет-СМИ. Поиск публикаций и сбор отобранных текстов произвела Екатерина Банщикова в рамках работы над своей дипломным проектом. Всего из более чем сотни новостных сообщений случайным образом было выбрано 20 текстов.

Текстовый корпус

Источники формирования корпуса

Первым шагом при обработке текстов является подготовка простого корпуса текстов. В библиотеке tm поддерживается несколько источников формирования корпуса. Например, на моём компьютере после установки этой библиотеки мне стали доступны функции по созданию корпуса из массива данных (DataFrameSource), из каталога файлов, каждый из которых содержит отдельный документ (DirSource), из вектора (VectorSource), из текстов, доступных по URI-ссылке (URISource) и из набора новостных сообщений агентства Reuters, входящих в состав расширения tm (ReutersSource). Помимо типов, которые входят в библиотеку tm, пользователь может оперативно установить расширения из CRAN, позволяющие импортировать тексты из веб-источников в форматах XML, JSON, HTML (tm.plugin.webmining), из почтовых файлов eml и mbox (tm.plugin.mail), из файлов французской программы текстового анализа Alceste (tm.plugin.alceste), из файлов, полученных от новостных агрегаторов Europresse (tm.plugin.europresse), LexisNexis (tm.plugin.lexisnexis), Dow Jones Factiva (tm.plugin.factiva).

Поскольку наши новостные сообщения хранятся в отдельных текстовых файлах, мы воспользуемся источником типа DirSource.

Чтение источника

Документы могут храниться в источнике в разных форматах. Библиотека tm поддерживает чтение документов в форматах простого текста (readPlain), PDF (readPDF), Microsoft Word (readDOC), XML (readXML), табличной структуры (readTabular – для источников DataFrameSource и VectorSource) и несколько Reuters-форматов. Наш вариант – простой текст.

library(tm)
# Файлы хранятся в каталоге articles
dirpath <- "articles"
articles <- Corpus(DirSource(dirpath),
                   readerControl = list(reader = readPlain,
                                      language = "ru",
                                      load = T))

Предполётная обработка документов корпуса

Все наши тексты сохранены в "дружеском к читателю" формате: с использованием падежей и склонений, пунктуации, строчных и прописных букв, с массой предлогов и прочих частей речи. Все эти элементы текста несут смысл в контексте, но будут мешать при выполнении нашей задачи - построения облака слов.

Библиотека tm содержит инструменты по удалению лишних пробелов, приведению всех букв к строчному виду, удалению цифр, знаков пунктуации и стоп-слов. Приятно то, что присутствует в библиотеке и список стоп-слов для русского языка (проверить его содержание можно с помощью команды stopwords("russian")).

Команда tm_map позволяет применить заданную функцию к каждому документу в корпусе.

# Текстовые файлы были сохранены в кодировке Windows. Конвертируем.
articles <- tm_map(articles, iconv, 'cp1251', 'UTF-8')
articles <- tm_map(articles, stripWhitespace)
articles <- tm_map(articles, tolower)
articles <- tm_map(articles, removeNumbers)
articles <- tm_map(articles, removeWords, stopwords("russian"))
articles <- tm_map(articles, removePunctuation)

Облако слов: первый заход

library(wordcloud)
wordcloud(articles, random.order=F, max.words=80, 
          colors=brewer.pal(6,"Oranges"))

Облако слов без стемматизации

Облако слов без стемматизации

Всё бы хорошо, но на графике встречаются одни и те же слова, но в разных формах. Например: молодёжном - молодёжного - молодёжный, году - года, участники - участников. Поскольку в случае с облаком слов нам не важно, в какой форме слово использовалось, а гораздо полезнее сам факт появления слова и его частота, то нам следует привести все слова к исходной форме.

Стемматизация текстов

Стемматизация по-русски, или плач о национальной свободе

Любую околонаучно-исследовательскую работу крайне желательно проводить с помощью таких компьютерных программ, чей исходный код открыт и доступен для проверки публикой. Иначе, если вы, например, производите расчёты в Excel или SPSS, выводы и воспроизводимость вашего исследования можно поставить под сомнение, т.к. алгоритмы, по которым эти расчёты производились, недоступны ни вам, ни сообществу, а только разработчику программы.

Подозреваю, что алгоритмы стемматизации, в частности, для русского языка - это вещь крайне наукоёмкая, требующая для её создания больших умственных трудозатрат. В России, где отношение к нарушению прав на интеллектуальную собственность достаточно лояльное, а уровень понимания того, что такое лицензионный договор, открытый исходный код и свободные лицензии, у нашего научного сообщества низкий. Поэтому неудивительно, что эффективной программы для стемминга русскоязычных текстов нет, а для создания, например, «Национального корпуса русского языка» с участием РАН использовалась программа с закрытым исходным кодом MyStem от Яндекса. Это при том, что на разработку корпуса выделялось несколько грантов из государственного бюджета. Выглядит это грустно-позорно. Благо, обнаружился Открытый корпус русского языка, все тексты и связанное с ним программное обеспечение распространяются под свободными лицензиями - сразу жить стало приятнее.

Что же есть доступного в R для стемминга русского языка? Библиотека SnowballC реализует стеммер Портера. Например, на наши экземпляры дублирующих слов выше мы получем следующее:

wordStem(c('молодёжный', "молодёжного", "молодёжном",
           "года", "году", "участники", "участников"), language='ru')

[1] "молодёжн" "молодёжн" "молодёжн" "год" "год" "участник" "участник"

Вроде бы ничего, но не каждый поймёт, что такое «молодёжн». Ещё один пример стеммера с открытым исходным кодом - это отечественная разработка Андрея Коваленко Stemka. Написан на C++, может быть использован в качестве библиотеки, так что энтузиасты могут сделать библиотеку для R. На аналогичную строку русских слов Stemka выдаст следующий результат:

молодёжн|ый
молодёжн|ог|о
молодёжн|ом|
год|а
год|у
участник|ам|
участник|и

Удалив в каждой строке вертикальную черту и символы справа от неё мы получим результат, аналогичный портеровскому. Кстати, слово «кровать» он превращает в «крова», как и Портер.

Существует открытый проект АОТ, согласно описаниям предлагающий «COM-объекты, которые можно скомпилировать под Visual Studio и использовать под Delphi». Судя по активности в их svn-репозитории, проект жив. Спасибо разработчикам, что помимо доступных COM-объектов, для компиляции которых нужно купить Visual Studio, Delphi и операционную систему Windows, они предоставили демо-версию своих продуктов онлайн. Мы можем воспользоваться примером морфологического анализа http://aot.ru/demo/morph.html. Данный анализатор успешно справляется и со словом «кровать», и «молодёжного» он превращает в «молодёжный». Для немассированной обработки текстов в R можно отправлять автоматические запросы через демо-версию и «вытаскивать» нужное слово из получаемых в ответ страниц с помощью XPath. Но здесь нужно подумать, насколько такие действия сочетаются с условиями использования демо-версии.

Исходя из вышесказанного, придётся грусто-позорно пользоваться программой с закрытым исходным кодом.

Стемматизация с помощью MyStem от Яндекса

Стеммер MyStem разработан в 1998 году сооснователем Яндекса Ильёй Сегаловичем. Программа доступна для различных операционных систем, исходный код закрыт. И хотя сам сайт Яндекса уверяет, что программа доступна только для некоммерческого использования, внимательно ознакомившись с лицензионным договором, в пункте 3.2 вы обнаружите, что «Программа может использоваться в коммерческих целях для разработки/создания каких-либо сервисов или программ, включаться и использоваться по прямому функциональному назначению в составе таких сервисов или программ, а также использоваться иным образом в процессе оказания услуг/выполнения работ». Исключения составляют те случаи, когда пользователь хочет с помощью MyStem заниматься спамом, раскруткой сайтов или создать свой поисковый движок. Спасибо Яндексу за это!

Правда, помимо закрытого кода у MyStem есть ещё одна ложка дёгтя: в пункте 3.11 соглашения читаем, что «при установке на персональный компьютер каждой копии Программы присваивается индивидуальный номер, который автоматически сообщается Правообладателю». Понятно, что Яндекс хочет знать, сколько копий его программы используется (ведь часть пользователей MyStem точно приносит экономический вред Яндексу, когда пользуются им для раскрутки сайтов), но всё равно неприятно.

Для использования mystem в R создадим одноименную функцию. Для работы требуется, чтобы операционная система знала, где лежит исполняемый файл mystem (в Windows, видимо, mystem.exe).

mystem <- function(doc) {
  library(stringr)
  sdoc <- system('mystem -nl -e utf-8 ', intern=T, input=doc)
  # При получении нескольких вариантов mystem разделяет их
  # вертикальной чертой. Удалим черту и варианты.
  sdoc <- str_replace(sdoc, '\\|.*$', '')
  # Если mystem сомневается в результате, он добавляет знак вопроса. Удаляем.
  sdoc <- str_replace(sdoc, '\\?', '')
  sdoc <- paste(sdoc, collapse=" ")
  attributes(sdoc) <- attributes(doc)
  sdoc
}

Теперь мы обладаем инструментом для стемминга и можем возвращаться в облака.

Финальный аккорд

Дообработка текстов корпуса

Добавляем к обработке текстов стемматизацию. Также логично удалить стоп-слова уже после приведения слов к исходной форме.

articles <- tm_map(articles, iconv, 'cp1251', 'UTF-8')
articles <- tm_map(articles, stripWhitespace)
articles <- tm_map(articles, tolower)
articles <- tm_map(articles, removeNumbers)
articles <- tm_map(articles, removePunctuation)
articles <- tm_map(articles, mystem)
articles <- tm_map(articles, removeWords, c(stopwords("russian"),
                                            "это", "также",
                                            "быть", "мочь",
                                            "май", "апрель", "март",
                                            "ноябрь", "который",
                                            "псковский", "молодежный",
                                            "псков", "центр", 
                                            "городской"))

Рисуем облако слов

wordcloud(articles, random.order=F, max.words=80, 
          colors=brewer.pal(6,"Oranges"))
Облако слов после стемматизации. Слова с низкой частотой еле видны

Облако слов после стемматизации. Слова с низкой частотой еле видны

Теперь все существительные представлены в именительном падеже, глаголы - в инфинитиве. Но слова с низкой частотой, которые рисуются самым маленьким размером шрифта и с наименьшей интенсивностью цвета, практически не читаются. Что случилось, ведь мы не меняли никаких характеристик графика, кроме самих слов и их частот?

В частотах весь фокус: в предыдущей версии графика частоты одного и того же слова «размазывавались» по разным словоформам, а теперь частоты скопились в базовой словоформе. И, естественно, слова-чемпионы добрали гораздо больше, чем слова-аутсайдеры. В итоге, разрыв в частотах между лидерами и отстающими увеличился, а чтобы этот разрыв показать, алгоритм увеличил разницу в размере шрифта и интенсивности цвета. Что делать?

Как правильно использовать цвета в статистике или немного грамматики графики

С точки зрения грамматики графики цвет слова и размер шрифта слова передают одну и ту же информацию - относительную частоту встречаемости слова в корпусе. Поэтому самый простой и идеологически верный вариант - это отказаться от дублирования информации и закрасить все слова одним цветом.

Облако слов без использования цвета. Все слова хорошо читаются

Облако слов без использования цвета. Все слова хорошо читаются

В таком случае все слова хорошо читаются. А если заказчик всё-таки хочет цветную картинку? Надо подумать...

А на сегодня давайте сделаем последнюю доработку: уберём из графика несодержательные в случае с молодёжным центром слова:

articles <- tm_map(articles, removeWords, c(stopwords("russian"),
                                            "это", "также",
                                            "быть", "мочь", "октябрь",
                                            "май", "апрель", "март",
                                            "ноябрь", "который",
                                            "псковский", "молодежный",
                                            "псков", "центр", 
                                            "городской"))
wordcloud(articles, random.order=F, max.words=80)
Облако слов из новостей о Псковском городском молодёжном центре

Облако слов из новостей о Псковском городском молодёжном центре

Powered by WordPress & Theme by Anders Norén