Аналіз текстів (text mining) в R реалізовується за допомогою пакету tm
Аналізувати можна різні типи текстів: html-сторінки, pdf-файли, документи в docx та інші. У цій статті розглянемо як можна аналізувати в R документи у форматах html, docx та txt. Це дозволить нам прослідкувати, як формати аналізованих документів впливають на результати самого аналізу. Це дозволить у майбутньому при аналізі враховувати ці особливості.
Для того, щоб продемонструвати можливості R в аналізі тестів, ми візьмемо для роботи три тексти з Хабру:
Ці тексти ми завчасно зберегли у форматах txt та docx на локальному диску.
Тексти для аналізу потрібно підготувати. Особливо, якщо ми працюємо не з документами на локальному диску, а із матеріалами сайтів. Їх потрібно позбавити зайвого коду і лишити тільки сам текст. Є багато різних способів, як це зробити. Одним із найпростіших є використання функції htmlToText, яка використовує команди з пакетів RCurl та XML
library(RCurl)
library(XML)
htmlToText <- function(input, ...) {
require(RCurl)
require(XML)
evaluate_input <- function(input) {
if(file.exists(input)) {
char.vec <- readLines(input, warn = FALSE)
return(paste(char.vec, collapse = ""))
}
if(grepl("</html>", input, fixed = TRUE)) return(input)
if(!grepl(" ", input)) {
if(!file.exists("cacert.perm")) download.file(url="http://curl.haxx.se/ca/cacert.pem", destfile="cacert.perm")
return(getURL(input, followlocation = TRUE, cainfo = "cacert.perm"))
}
return(NULL)
}
convert_html_to_text <- function(html) {
doc <- htmlParse(html, asText = TRUE)
text <- xpathSApply(doc, "//text()[not(ancestor::script)][not(ancestor::style)][not(ancestor::noscript)][not(ancestor::form)]", xmlValue)
return(text)
}
collapse_text <- function(txt) {
return(paste(txt, collapse = " "))
}
html.list <- lapply(input, evaluate_input)
text.list <- lapply(html.list, convert_html_to_text)
text.vector <- sapply(text.list, collapse_text)
return(text.vector)
}
Тепер застосуємо цю функцію до html-сторінок
input <- "https://habrahabr.ru/post/217545/"
input2 <- "https://habrahabr.ru/company/itinvest/blog/262155/"
input3 <- "https://habrahabr.ru/post/232515/"
Конвертуємо тепер це в текст за допомогою вище створеної функції:
txt <- htmlToText(input)
txt2 <- htmlToText(input2)
txt3 <- htmlToText(input3)
Усі тексти об’єднаємо в один список:
text <- c(txt,txt2,txt3)
Отже, три тексти з html-сторінок майже готові до аналізу. Тепер прочитаємо доківський файл. Він також засорений різного роду кодом, подібно до html-сторінки. Для його очищення від нього вже також є написані функції.
Для читання доківських файлів найкраще використовувати функцію read_docx з пакету qdapTools:
library(qdapTools)
docx <- read_docx("text.docx")
docx2 <- read_docx("text2.docx")
docx3 <- read_docx("text3.docx")
Також об’єднаємо в список:
word <- list(docx,docx2,docx3)
Документи у форматі txt найзручніші для роботи, адже не містять зайвого коду. Їх читання в пакеті tm трохи відмінне.
Усі тексти, які маємо намір проаналізувати спочатку треба зберегти в окрему директорію. Потім подібним чином вказаки адресу цієї папки:
cname <- file.path("~", "new_media", "text")
cname
## [1] "~/new_media/text"
dir(cname)
## [1] "text.txt" "text2.txt" "text3.txt"
Тепер запустимо сам пакет tm і сформуємо корпуси слів. Для документів word і html ця процедура буде однаковою, там на вхід ідуть списки. Для txt на вхід йде директорія, тому команада трохи інша:
library(tm)
corpus <- Corpus(VectorSource(text))
corpus2 <- Corpus(VectorSource(word))
corpus3 <- Corpus(DirSource(cname))
Перевіримо, чи подібні у нас вийшли файли:
summary(corpus)
## Length Class Mode
## 1 2 PlainTextDocument list
## 2 2 PlainTextDocument list
## 3 2 PlainTextDocument list
summary(corpus2)
## Length Class Mode
## 1 2 PlainTextDocument list
## 2 2 PlainTextDocument list
## 3 2 PlainTextDocument list
summary(corpus3)
## Length Class Mode
## text.txt 2 PlainTextDocument list
## text2.txt 2 PlainTextDocument list
## text3.txt 2 PlainTextDocument list
Усе добре, продовжуємо роботу.
Видалимо з масивів пунктуацію:
docs <- tm_map(corpus, removePunctuation)
docs2 <- tm_map(corpus2, removePunctuation)
docs3 <- tm_map(corpus3, removePunctuation)
Так само замінимо на пропуски різні зайві символи:
for(j in seq(docs))
{
docs[[j]] <- gsub("/", " ", docs[[j]])
docs[[j]] <- gsub("@", " ", docs[[j]])
docs[[j]] <- gsub("\\|", " ", docs[[j]])
}
for(j in seq(docs2))
{
docs2[[j]] <- gsub("/", " ", docs2[[j]])
docs2[[j]] <- gsub("@", " ", docs2[[j]])
docs2[[j]] <- gsub("\\|", " ", docs2[[j]])
}
for(j in seq(docs3))
{
docs3[[j]] <- gsub("/", " ", docs3[[j]])
docs3[[j]] <- gsub("@", " ", docs3[[j]])
docs3[[j]] <- gsub("\\|", " ", docs3[[j]])
}
Подібним чином можна заміняти різні слова, символи та скорочення на інші (наприклад txt <- gsub("анализировать", "анализ", txt) - ця команда замінить всі “анализировать” на “анализ” в документі txt)
Видалимо числа:
docs <- tm_map(docs, removeNumbers)
docs2 <- tm_map(docs2, removeNumbers)
docs3 <- tm_map(docs3, removeNumbers)
Зробимо всі слова написаними маленькими літерами:
docs <- tm_map(docs, tolower)
docs2 <- tm_map(docs2, tolower)
docs3 <- tm_map(docs3, tolower)
Тепер видалимо стоп-слова (сполучники, займенники і тп). У пакеті tm є готовий список російських стоп-слів, однак він не повний й інколи тексти місьтять специфічні для них стоп-слова (наприклад, назва сайту, де цей текст опублікований)
docs <- tm_map(docs, removeWords, stopwords("russian"))
docs2 <- tm_map(docs2, removeWords, stopwords("russian"))
docs3 <- tm_map(docs3, removeWords, stopwords("russian"))
Слова, яких нема в списку стоп-слів можна видалити подібним чином окремо. Забігаючи наперед скажу, що в списку стоп-слів немає слова “это”:
docs <- tm_map(docs, removeWords, c("хабр", "хабрахабр", "это"))
docs2 <- tm_map(docs2, removeWords, c("хабр", "хабрахабр", "это"))
docs3 <- tm_map(docs3, removeWords, c("хабр", "хабрахабр", "это"))
Пакет SnowballC допоможе нам ще краще підготувати тексти до аналізу.
Зокрема функція stemDocument дозволить уніфікувати однокореневі слова (для англійських слів, звісно, вона працює краще):
library(SnowballC)
docs <- tm_map(docs, stemDocument)
docs2 <- tm_map(docs2, stemDocument)
docs3 <- tm_map(docs3, stemDocument)
Також видалимо зайві пробіли і пропуски:
docs <- tm_map(docs, stripWhitespace)
docs2 <- tm_map(docs2, stripWhitespace)
docs3 <- tm_map(docs3, stripWhitespace)
І завершуємо підготовку даних переформатуванням у PlainTextDocument, з яким працює пакет tm:
docs <- tm_map(docs, PlainTextDocument)
docs2 <- tm_map(docs2, PlainTextDocument)
docs3 <- tm_map(docs3, PlainTextDocument)
Зробимо матрицю слів:
dtm <- DocumentTermMatrix(docs)
dtm2 <- DocumentTermMatrix(docs2)
dtm3 <- DocumentTermMatrix(docs3)
Порахуємо частоту, з якою зустрічаються слова:
freq <- colSums(as.matrix(dtm))
freq2 <- colSums(as.matrix(dtm2))
freq3 <- colSums(as.matrix(dtm3))
Упорядкуємо їх за частою:
ord <- order(freq)
ord2 <- order(freq2)
ord3 <- order(freq3)
І поглянемо на результат, вивівши 6 найпопулярніших термінів:
freq[head(ord)]
## данных данные августа алгоритм метод вероятность
## 62 61 56 50 39 29
freq2[head(ord2)]
## hyperlink данные метод решений pagerank knn
## 128 43 36 26 26 23
freq3[head(ord3)]
## данных данные метод решений pagerank обучения
## 59 43 36 26 26 23
Результати вийшли відмінні. Цьому є своє пояснення. У випадку html-документа анлізувалася не лише сама стаття, а і коментарі (“марта” - це від того, що на сайті ставилася дата коментаря (числа ми видалили, а місяць лишився)). Плюс коментарі додали частот багатьом словам, які зустрічалися в тексті. У випадку word-документа найбільшу популярність отримав термін hyperlink. Це зумовлено тим, що у збережених текстах було багато гіперпосилань, які при переформатуванні позначалися додатково цим терміном. Його у майбутньому потрібно буде відсівати на етапі відсіву стоп-слів. Аналіз txt документа прогнозовано дав найменш засорені дані. Це логічно, бо до нього не потрібно застосовувати очистку від коду.
Для виявлення “штучно” популярних слів можна ще подивитися на частоту частот:
head(table(freq), 100)
## freq
## 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
## 3113 658 355 158 112 63 38 42 20 30 16 14 12 10 3
## 16 17 18 19 20 21 23 24 25 26 29 39 47 50 56
## 8 5 1 3 4 2 3 1 1 3 2 1 1 1 1
## 61 62
## 1 1
Де частоти починають порушувати нормальний розподіл, там потрібно уважніше дивитися на терміни з такими частотами - швидше за все така ненормальна популярність зумовалена технічними особливостями тексту і їх потрібно прибрати з аналізу.
Можемо вивести найпопулярніші слова і таким чином:
freq <- sort(colSums(as.matrix(dtm)), decreasing=TRUE)
head(freq, 14)
## данных данные августа алгоритм марта
## 62 61 56 50 47
## метод вероятность которые например решений
## 39 29 29 26 26
## pagerank почему knn классификатор
## 26 25 24 23
freq2 <- sort(colSums(as.matrix(dtm2)), decreasing=TRUE)
head(freq2, 14)
## hyperlink данных алгоритм данные метод
## 128 59 45 43 36
## решений pagerank обучения knn которые
## 26 26 23 23 22
## вероятность используется классификатор набор
## 21 19 19 19
freq3 <- sort(colSums(as.matrix(dtm3)), decreasing=TRUE)
head(freq3, 14)
## данных алгоритм данные метод решений
## 59 45 43 36 26
## pagerank knn обучения которые вероятность
## 26 24 23 22 21
## используется классификатор набор параметры
## 19 19 19 19
Можна просто ці слова подати без вказівки на частоту:
findFreqTerms(dtm, lowfreq=15)
## [1] "августа" "алгоритм" "алгоритма" "апреля"
## [5] "вероятность" "всё" "давайте" "данные"
## [9] "данных" "делает" "дерева" "именно"
## [13] "использует" "используется" "классификатор" "компании"
## [17] "которые" "который" "ксредних" "марта"
## [21] "метод" "набор" "например" "нужно"
## [25] "обучения" "очень" "параметры" "поскольку"
## [29] "почему" "пример" "просто" "работы"
## [33] "реклама" "решений" "такое" "требует"
## [37] "является" "cart" "data" "knn"
## [41] "pagerank" "svm"
findFreqTerms(dtm2, lowfreq=15)
## [1] "алгоритм" "вероятность" "давайте" "данные"
## [5] "данных" "делает" "дерева" "использует"
## [9] "используется" "классификатор" "которые" "ксредних"
## [13] "метод" "набор" "например" "нужно"
## [17] "обучения" "параметры" "почему" "пример"
## [21] "решений" "требует" "является" "cart"
## [25] "hyperlink" "knn" "pagerank" "svm"
findFreqTerms(dtm3, lowfreq=15)
## [1] "алгоритм" "вероятность" "давайте" "данные"
## [5] "данных" "делает" "дерева" "использует"
## [9] "используется" "классификатор" "которые" "ксредних"
## [13] "метод" "набор" "например" "нужно"
## [17] "обучения" "параметры" "почему" "пример"
## [21] "решений" "требует" "является" "cart"
## [25] "knn" "pagerank" "svm"
А можна дати в табличному вигляді:
wf <- data.frame(word=names(freq), freq=freq)
wf2 <- data.frame(word=names(freq2), freq=freq2)
wf3 <- data.frame(word=names(freq3), freq=freq3)
head(wf)
## word freq
## данных данных 62
## данные данные 61
## августа августа 56
## алгоритм алгоритм 50
## марта марта 47
## метод метод 39
head(wf2)
## word freq
## hyperlink hyperlink 128
## данных данных 59
## алгоритм алгоритм 45
## данные данные 43
## метод метод 36
## решений решений 26
head(wf3)
## word freq
## данных данных 59
## алгоритм алгоритм 45
## данные данные 43
## метод метод 36
## решений решений 26
## pagerank pagerank 26
Ясна річ, що слів у документах може бути дуже багато, але нас цікавлять найбільш вживані. Маловживані часто є потреба видалити. Для цього застосовується функція removeSparseTerms. Вона видаляє зайві слова не за сумарною частотою вживаності, а більше за частотою появи в усіх документах. Тобто важливіше для слова бути хоча б раз в усіх документах, чим багато разів в одному.
dtms <- removeSparseTerms(dtm, 0.1)
dtms2 <- removeSparseTerms(dtm2, 0.1)
dtms3 <- removeSparseTerms(dtm3, 0.1)
Поглянемо на результати:
inspect(dtms)
## <<DocumentTermMatrix (documents: 3, terms: 285)>>
## Non-/sparse entries: 855/0
## Sparsity : 0%
## Maximal term length: 23
## Weighting : term frequency (tf)
##
## Terms
## Docs августа алгоритмы анализ архитектура балансе бесконечного
## character(0) 6 1 2 1 5 1
## character(0) 4 4 3 1 3 1
## character(0) 46 2 6 1 5 1
## Terms
## Docs большим большую будут быстро вакансии вашем версия вещей
## character(0) 3 1 1 2 1 1 1 1
## character(0) 4 1 5 2 2 1 1 1
## character(0) 1 1 2 2 1 1 1 1
## Terms
## Docs взять виде видим видите вместо внеземной войдите войти
## character(0) 2 3 4 1 1 1 1 1
## character(0) 2 1 2 4 4 1 1 1
## character(0) 1 1 2 1 1 1 1 1
## Terms
## Docs вопрос времени время всем всему встречается второй выше
## character(0) 6 4 5 5 1 2 2 3
## character(0) 5 1 1 1 1 1 2 3
## character(0) 1 8 4 1 1 1 3 4
## Terms
## Docs гиктаймс говоря года график давайте дайджест данные данных
## character(0) 1 2 3 8 4 2 10 4
## character(0) 1 1 1 1 11 2 43 57
## character(0) 1 2 2 3 1 2 8 1
## Terms
## Docs движения деанонимизируем делать дело добавить довольно
## character(0) 1 1 2 1 5 2
## character(0) 1 1 2 1 2 6
## character(0) 12 1 3 4 1 2
## Terms
## Docs должны достаточно доступна других жизни задача
## character(0) 2 3 1 1 2 1
## character(0) 2 4 1 3 1 2
## character(0) 1 3 1 1 3 2
## Terms
## Docs зарегистрированные зародилась знает значение значению
## character(0) 1 1 3 1 4
## character(0) 1 1 1 2 2
## character(0) 1 1 1 1 4
## Terms
## Docs значения значит идеи избранное изменён именно инженерия
## character(0) 1 5 2 2 8 7 2
## character(0) 5 1 1 1 1 8 1
## character(0) 1 2 1 2 5 1 2
## Terms
## Docs инструменты интеллекта интересно интересные инфо
## character(0) 4 2 3 5 1
## character(0) 2 2 2 3 1
## character(0) 4 2 2 5 1
## Terms
## Docs искусственного используется исследование исследования
## character(0) 2 1 2 1
## character(0) 2 17 1 1
## character(0) 2 2 1 1
## Terms
## Docs истории каждой каждый каламбуры карт картинка качестве
## character(0) 1 1 1 2 5 7 1
## character(0) 3 5 10 2 3 1 1
## character(0) 1 1 1 2 5 2 1
## Terms
## Docs клиентов количество комментарии комментарий компании
## character(0) 5 1 3 8 7
## character(0) 4 10 2 1 6
## character(0) 5 1 3 5 2
## Terms
## Docs контент которая которую которые который круг купил лекция
## character(0) 1 1 1 7 3 1 1 2
## character(0) 1 4 2 18 9 1 1 1
## character(0) 1 2 3 4 6 1 1 2
## Terms
## Docs либо материалов материалы меньше меняют месяц метод
## character(0) 1 2 5 2 1 4 1
## character(0) 1 2 4 2 1 1 35
## character(0) 2 2 4 1 1 1 3
## Terms
## Docs механика миллиардов мира мобильная могу могут мти найти нам
## character(0) 1 1 2 1 2 1 1 1 3
## character(0) 1 1 2 1 1 10 1 2 6
## character(0) 1 1 2 1 1 1 1 1 1
## Terms
## Docs например насколько начинающего наша наши неделю неделя
## character(0) 5 1 1 2 5 3 2
## character(0) 15 2 2 1 1 2 1
## character(0) 6 2 1 1 4 2 2
## Terms
## Docs неплохо несколько ниже ниичоси никто новости новую нужно
## character(0) 1 2 2 2 2 4 1 4
## character(0) 1 4 1 1 1 4 2 13
## character(0) 1 1 2 2 2 5 1 6
## Terms
## Docs образом образца объектноориентированное одним одной одном
## character(0) 3 1 2 2 2 1
## character(0) 4 1 1 1 2 1
## character(0) 1 1 2 1 3 1
## Terms
## Docs оказалось окна операционку определить опытного оставлять
## character(0) 1 1 1 1 2 1
## character(0) 2 1 1 11 1 1
## character(0) 1 2 1 1 1 1
## Terms
## Docs очень ошибки параметры пасхалка первом первый переменная
## character(0) 8 4 1 1 2 2 4
## character(0) 6 1 17 1 2 2 2
## character(0) 3 1 2 1 1 1 4
## Terms
## Docs песочница плату повесть поводу поддержки подписчики
## character(0) 2 1 2 1 1 1
## character(0) 2 1 1 1 1 1
## character(0) 2 1 2 1 1 1
## Terms
## Docs пожалуйста позволяет поиск показать получаем получается
## character(0) 1 1 1 1 1 2
## character(0) 1 2 1 3 2 1
## character(0) 1 1 2 1 3 2
## Terms
## Docs получить пользователей пользователи помогает помощь помощью
## character(0) 1 1 3 4 2 4
## character(0) 1 2 4 3 2 2
## character(0) 3 1 3 4 2 5
## Terms
## Docs понимаю поскольку последнюю похожие почему поэтому правила
## character(0) 1 1 2 1 3 3 2
## character(0) 1 15 2 1 14 6 4
## character(0) 3 1 2 1 8 2 1
## Terms
## Docs приложения пример примере примерно причин пришли проблема
## character(0) 1 7 1 1 1 1 3
## character(0) 2 12 3 2 1 1 3
## character(0) 2 2 1 3 1 1 2
## Terms
## Docs проблемы проверить программирование программная продаваемые
## character(0) 1 1 4 2 1
## character(0) 1 1 1 1 1
## character(0) 1 1 4 2 1
## Terms
## Docs проектируем прозрачность просто профиль прощай проще
## character(0) 1 1 9 1 2 2
## character(0) 1 1 6 1 1 2
## character(0) 1 1 2 1 2 1
## Terms
## Docs публикации работа работать работе равной разделы разница
## character(0) 5 1 1 1 4 2 1
## character(0) 4 1 4 2 2 2 1
## character(0) 5 1 2 7 4 2 1
## Terms
## Docs разное разрабатывает разработка раннера рано
## character(0) 1 1 4 1 1
## character(0) 1 1 3 1 1
## character(0) 1 1 4 1 1
## Terms
## Docs распространенные регистрация результаты рейтинг реклама
## character(0) 1 1 4 2 9
## character(0) 1 1 2 2 1
## character(0) 1 1 3 2 9
## Terms
## Docs сайт сайте самое самые самый свежих своих сделали семинары
## character(0) 2 1 4 2 1 2 5 2 1
## character(0) 4 1 2 2 1 2 3 1 1
## character(0) 2 2 4 1 1 2 5 1 1
## Terms
## Docs систем скомпрометировал слишком слова служба случае
## character(0) 1 5 2 1 1 1
## character(0) 1 3 1 1 1 6
## character(0) 1 5 1 1 1 1
## Terms
## Docs собственному современная соглашение создание спасибо
## character(0) 4 1 1 1 5
## character(0) 2 1 1 1 1
## character(0) 4 1 1 2 1
## Terms
## Docs спецпроекты способ сразу стала стартапам статье статьи
## character(0) 3 1 2 3 1 2 2
## character(0) 1 3 1 1 1 1 2
## character(0) 3 4 2 1 1 4 4
## Terms
## Docs статью стоит сутки такая также таким такое такому тарифы
## character(0) 1 4 1 5 1 3 4 1 1
## character(0) 3 6 1 1 6 3 12 1 1
## character(0) 3 2 1 3 2 1 5 1 1
## Terms
## Docs телефоны теме тестдрайв тех типизации тостер точке уровень
## character(0) 1 1 1 5 2 1 1 2
## character(0) 1 3 1 3 2 1 1 6
## character(0) 1 2 1 1 2 1 1 1
## Terms
## Docs услуги успешно учетные фона фрилансим фронтенда функций
## character(0) 1 1 1 1 1 2 2
## character(0) 1 1 1 1 1 2 2
## character(0) 1 2 1 1 1 2 2
## Terms
## Docs хабы хотим хотя человека читаемое чтото эта языковая
## character(0) 4 1 4 1 1 4 1 2
## character(0) 2 1 5 1 1 1 3 2
## character(0) 4 2 1 1 1 6 2 2
## Terms
## Docs яндексе anniversary facebook feed frontendразработчики
## character(0) 2 1 2 1 1
## character(0) 1 1 1 1 1
## character(0) 2 1 1 1 1
## Terms
## Docs fuchsia geektimes google htmlверстальщика ibm javascript
## character(0) 1 1 2 1 1 1
## character(0) 1 1 6 1 1 1
## character(0) 1 1 3 1 1 1
## Terms
## Docs kicad microsoft phpдайджест pokemon qeda robot saday
## character(0) 1 1 4 1 1 1 2
## character(0) 1 1 2 1 1 1 1
## character(0) 1 1 4 1 1 1 2
## Terms
## Docs tinkoff update vpnаккаунтов watson web whatsapp windows
## character(0) 5 1 1 1 1 1 2
## character(0) 3 1 1 1 1 1 2
## character(0) 5 1 1 1 1 1 2
inspect(dtms2)
## <<DocumentTermMatrix (documents: 3, terms: 54)>>
## Non-/sparse entries: 162/0
## Sparsity : 0%
## Maximal term length: 38
## Weighting : term frequency (tf)
##
## Terms
## Docs анализ большую будут видим вместо времени выше график
## character(0) 1 1 1 3 1 1 2 3
## character(0) 2 1 1 1 1 4 3 3
## character(0) 3 1 4 1 4 1 3 1
## Terms
## Docs давайте данные делать достаточно значение значит именно
## character(0) 3 4 2 3 1 3 2
## character(0) 1 1 2 2 1 2 1
## character(0) 11 38 1 4 2 1 8
## Terms
## Docs исследования каждой количество которая которые который
## character(0) 1 1 1 1 5 3
## character(0) 1 1 1 2 3 3
## character(0) 1 4 9 4 14 8
## Terms
## Docs меньше метод нам например неплохо ниже нужно очень
## character(0) 1 1 1 2 1 2 2 2
## character(0) 1 1 1 2 1 1 3 1
## character(0) 2 34 6 14 1 1 12 6
## Terms
## Docs позволяет показать получается получить помощью поэтому
## character(0) 1 1 2 1 2 2
## character(0) 1 1 2 3 1 1
## character(0) 2 2 1 1 1 6
## Terms
## Docs просто проще работать работе результаты самое самый способ
## character(0) 1 1 1 1 3 1 1 1
## character(0) 1 1 1 7 2 2 1 4
## character(0) 6 2 3 2 2 1 1 3
## Terms
## Docs сразу статье статьи таким такое точке хотим человека чтото
## character(0) 1 1 1 3 1 1 1 1 1
## character(0) 1 3 4 1 2 1 2 1 2
## character(0) 1 1 1 3 11 1 1 1 1
## Terms
## Docs httpshabrahabrruflowsdevelopразработка hyperlink
## character(0) 1 29
## character(0) 1 20
## character(0) 1 79
inspect(dtms3)
## <<DocumentTermMatrix (documents: 3, terms: 53)>>
## Non-/sparse entries: 159/0
## Sparsity : 0%
## Maximal term length: 12
## Weighting : term frequency (tf)
##
## Terms
## Docs анализ большую будут видим вместо времени выше график
## character(0) 1 1 1 3 1 1 2 3
## character(0) 2 1 1 1 1 4 3 3
## character(0) 3 1 4 1 4 1 3 1
## Terms
## Docs давайте данные делать достаточно значение значит именно
## character(0) 3 4 2 3 1 3 2
## character(0) 1 1 2 2 1 2 1
## character(0) 11 38 1 4 2 1 8
## Terms
## Docs исследования каждой количество которая которые который
## character(0) 1 1 1 1 5 3
## character(0) 1 1 1 2 3 3
## character(0) 1 4 9 4 14 8
## Terms
## Docs меньше метод нам например неплохо ниже нужно очень
## character(0) 1 1 1 2 1 2 2 2
## character(0) 1 1 1 2 1 1 3 1
## character(0) 2 34 6 14 1 1 12 6
## Terms
## Docs позволяет показать получается получить помощью поэтому
## character(0) 1 1 2 1 2 2
## character(0) 1 1 2 3 1 1
## character(0) 2 2 1 1 1 6
## Terms
## Docs просто проще работать работе разработка результаты самое
## character(0) 1 1 1 1 1 3 1
## character(0) 1 1 1 7 1 2 2
## character(0) 6 2 3 2 1 2 1
## Terms
## Docs самый способ сразу статье статьи таким такое точке хотим
## character(0) 1 1 1 1 1 3 1 1 1
## character(0) 1 4 1 3 4 1 2 1 2
## character(0) 1 3 1 1 1 3 11 1 1
## Terms
## Docs человека чтото
## character(0) 1 1
## character(0) 1 2
## character(0) 1 1
Отже, на прикладі частот ми переконалися, що технічні особливості аналізованих текстів можуть дещо спотворювати результати. Найкраще аналізувати тексти, збережені у форматі txt. Але часто доводиться працювати із вордівськими документами, в яких моуть інколи проскакувати слова-команди. При роботі з html-файлами потрібно зважати, що аналізується не просто текст за посиланням, а і коментарі під ним, “шапка” сайту, одним словом - все, що відображається разом із цим текстом. В принципі для кожного сайту окремо можна прописати код так, аби аналізувалася тільки стаття за посиланням, але типового рішення, яке б сміливо застосовувалося по шаблону до всіх сайтів неможливо зробити. Аналізувати саме html-сторінки краще тоді, коли нема часу зберегти потрібний для аналізу контент у текстовий файл.
На прикладі корпусу з файлів txt зробимо різні варіанти аналізу. Побудуємо стовпчикову діаграму частот, з якими зустрічаються слова (10 і більше разів):
library(ggplot2)
p <- ggplot(subset(wf3, freq>10), aes(word, freq))
p <- p + geom_bar(stat="identity")
p <- p + theme(axis.text.x=element_text(angle=45, hjust=1))
p
Глянемо, з якими словами разом зустрічаються в тексті:
findAssocs(dtms3, c("данные" , "анализ"), corlimit=0.98)
## $данные
## будут вместо значение именно каждой количество
## 1.00 1.00 1.00 1.00 1.00 1.00
## которые который меньше метод нам например
## 1.00 1.00 1.00 1.00 1.00 1.00
## позволяет показать просто проще работать давайте
## 1.00 1.00 1.00 1.00 1.00 0.99
## нужно очень поэтому такое
## 0.99 0.99 0.99 0.99
##
## $анализ
## которая
## 0.98
Побудуємо хмарку слів. Для термінів, які зустрічаються більше 25 разів:
library(wordcloud)
wordcloud(names(freq3), freq3, min.freq=25)
Для 100 найпопулярніших термінів:
wordcloud(names(freq3), freq, max.words=100)
Додамо ранжування за кольором:
wordcloud(names(freq3), freq3, min.freq=20, scale=c(5, .1), colors=brewer.pal(6, "Dark2"))
dark2 <- brewer.pal(6, "Dark2")
wordcloud(names(freq3), freq3, max.words=100, rot.per=0.2, colors=dark2)
Тепер зробимо ієрархічний кластерний аналіз.
Для цього відберемо найбільш вживані слова:
dtmss <- removeSparseTerms(dtm3, 0.15)
І зробимо дендрограму:
library(cluster)
d <- dist(t(dtmss), method="euclidian")
fit <- hclust(d=d, method="ward")
## The "ward" method has been renamed to "ward.D"; note new "ward.D2"
fit
##
## Call:
## hclust(d = d, method = "ward")
##
## Cluster method : ward.D
## Distance : euclidean
## Number of objects: 53
plot(fit, hang=-1)
Цей графік можна зробити кращим через додавання виділення. Виділимо 6 кластерів:
plot.new()
plot(fit, hang=-1)
groups <- cutree(fit, k=6) # "k=" defines the number of clusters you are using
rect.hclust(fit, k=6, border="red") # draw dendogram with red borders around the 5 clusters
Але можна застосувати і k-means кластеризацію. Це ми також зробимо:
library(fpc)
d <- dist(t(dtmss), method="euclidian")
kfit <- kmeans(d, 2)
clusplot(as.matrix(d), kfit$cluster, color=T, shade=T, labels=2, lines=0)
Це все тренувальні дані. От коли їх буде більше і метою буде не просто показати можливості аналізу текстових даних, то інтерпретація має бути цікавою.
Таким чином, ми розглянули основи аналізу текстових даних в R. Однак R і пакет tm пропонують масу різних маніпуляцій з текстами, застосування яким ще можна знайти.