Использование R для анализа и визуализации данных

По просьбе коллег подготовил небольшую заметку об использовании возможностей R для анализа отзывов страхователей на сайте strahnadzor.ua
По сути, чтобы получить подобный отчет нам надо будет написать три блока кода:
Собрать данные со страниц сайта
Подготовить таблицы и построить графики
Подготовить текст и построить облака слов
Коротко опишем код по каждому блоку.
Сбор данных со страниц сайта
Используем библиотеку library('rvest'), чтобы иметь возможность загружать страницы сайта и разбирать их текст.
Данные за последний год (мы решили строить отчет за год) находятся на первых 84 страницах сайта. Открываем цикл, чтобы их отобрать
for (j in c(1:84)) {
Сформируем переменную, которая будет содержать адрес конкретной страницы сайта, которую мы хотим считать.
url <- paste0('https://www.strahnadzor.ua/comments/?page=', as.character(j))
Получим ее html-код в переменную webpage.
webpage <- read_html(url)
Далее нам необходимо понять в каком разделе страницы находятся данные с названием компании. Можно установить специализированное приложение, например, SelectorGadget (https://chrome.google.com/webstore/detail/selectorgadget/mhjhnkcfbdhnjickkkdbjoemdmbfginb). Но можно и просто выполнив команду inspect по правой кнопке мыши в Хроме.
В нашем случае название компании содержится в блоке '.feeling_х .date a'', где «х» обозначает оценку страхователя от 0 до 5. Так как нас эта оценка интересует, то откроем цикл
for (f in c(0:5)) { в котором будем на странице собирать отзывы в разрезе оценок и сохранять их вместе с названиями компаний.
Формируем строку с нужным блоком оценок
feeling_string<-paste0('.feeling_', as.character(f))
Формируем текст с названием нужного блока
paste(feeling_string, '.date a')
Копируем часть html-кода из нужной секции с помощью команды html_nodes(‘страница’,’секция’)
ic_data_html <- html_nodes(webpage,paste(feeling_string, '.date a'))
Извлекаем текст из html-кода (в нашем случае название компании) командой
ic_data <- html_text(ic_data_html)
Аналогично извлекаем город страхователя и другие данные
# город страхователя
reviewercity_data_html<-html_nodes(webpage,paste(feeling_string,'.date span:nth-child(5)'))
reviewercity_data <- append(reviewercity_data,html_text(reviewercity_data_html))
Учитывая, что часть необходимых нам данных находится на странице с каждым конкретным отзывом (например, текст отзыва, дата и время отзыва и т.п.), то опишем блок кода, который будет формировать url страницы с конкретным отзывом и считывать необходимую информацию.
Так как на странице размещается по 10 отзывов, то откроем цикл
for (i in c(1:10)) {
и сформируем строку с адресом
urlreview <- paste0('https://www.strahnadzor.ua/comments/',reviewnumber_data[(j-1)*10+i])
Дальше аналогично. Считываем html-код и получаем нужные нам данные
webpagereview <- read_html(urlreview)
reviewtext_data_html<-html_nodes(webpagereview,'h1')
reviewtext_data1 <- html_text(reviewtext_data_html)
reviewtext_data<-append(reviewtext_data,reviewtext_data1[1])
Используем команду as.factor(), чтобы сделать часть данных категориальными, в частности данные с названиями страховых компаний, оценок страхователей и т.п.
# страховая компания
ic_data<-as.factor(ic_data)
После того, как собрали и подготовили все данные, формируем итоговую таблицу (dataframe)
review_df<-data.frame(q=qnt, id=reviewnumber_data, InsCompany=ic_data, RDate=reviewdate_data,
RTime=reviewtime_data, Name=reviewername_data, Feeling=feeling_data, FeelingText=feelingtext_data, Hour=hour_data, Minutes=minutes_data, Month=month_data, OMonth=ordered_month_data, CountMessage=countmessage_data, City=CityName, Subject=reviewtext_data, SubjectL=nchar(reviewtext_data, type = "chars"), Text=reviewfulltext_data, TextL=nchar(reviewfulltext_data, type = "chars"))
Ее и будем использовать для подготовки данных в необходимых разрезах, чтобы анализировать и строить графики.
Подготовка таблиц и построение графиков
Я не буду здесь копипастить весь код R для подготовки данных и графиков. Приведу только несколько примеров.
Так как нам надо будет строить графики, где фигурируют ТОП-10 или ТОП-20 компаний, то заготовим табличку (текстовый вектор) с перечнем компаний с наибольшим количеством отзывов.
Считаем количество отзывов в разрезе СК. Сделать это можно многими способами, например так
Qnt_df<-aggregate(q ~ InsCompany, data = review_df, sum)
Сортируем по количеству отзывов
TopQnt<-Qnt_df[order(Qnt_df$q,decreasing = TRUE),]
Отбираем первые 10 записей. Они и будут ТОП-10 СК
TopQnt<-head(TopQnt,n=10)
Строим текстовый вектор из названий ТОП-10 СК
TopInsCompany<-TopQnt$InsCompany
Рассмотрим пример с построением графика распределения количества отзывов по времени суток

Чтобы его построить нам необходима таблица в которой бы содержались данные о количестве отзывов с оценками в разрезе времени суток. Создадим ее одной строчкой кода
HourFeeling_df<-aggregate(q~Hour+FeelingText, data= review_df, sum), где
review_df – исходная таблица со всеми данными
FeelingText – оценки страхователя в текстовом виде
'1. Автор восхищен'
'2. Автор доволен'
'3. Автор спокоен'
'4. Автор волнуется'
'5. Автор зол'
'6. Нет оценки'
Hour – час суток, когда был оставлен отзыв
Перейдем к построению графиков.
Будем использовать известную библиотеку ggplot2. О ней есть много информации в сети Интернет, так что не буду описывать все ее возможности.
library('ggplot2')
Разберем основные параметры кода для построения графика, который сохраним в переменную «p».
p <- ggplot(HourFeeling_df, aes(x = Hour, y = q, fill = FeelingText)) +
HourFeeling_df – таблица с данными, которую мы сформировали ранее одной строчкой кода. На этих данных и будет основан график.
x = Hour – по оси х будет откладываться время суток (часы)
y = q – по оси y будет откладываться количество отзывов
fill = FeelingText – для заполнения графика возьмем данные оценки страхователя
Таким образом мы сформировали основу для графика – базовый слой.
Сформируем теперь слой с графиком.
geom_bar(…, position = "stack", …) +
geom_bar – формирует столбчатую диаграмму
position = "stack" – означает, что категориальные данные с оценкой страхователя (FeelingText)надо расположить в столбик.
Если нам надо посчитать долю каждой оценки в течение часа, чтобы получить такой график

то просто меняем это параметр на «fill» geom_bar(…, position = " fill ", …) +
и получаем нужное.
Теперь «раскрасим» наш график. Для этого воспользуемся командой
scale_fill_brewer('',palette = "RdYlGn", direction = -1)+
которая означает, что для «раскраски» данных надо взять палитру "RdYlGn" из пакета ‘RColorBrewer’ и отображать ее в обратном порядке direction = -1
Мы выбрали именно такую палитру, так как данные у нас отображают оценку по шкале от хороших к плохим и данных у нас 6 категорий.
Детально палитру можно посмотреть по ссылке http://colorbrewer2.org/#type=diverging&scheme=RdYlGn&n=6
Если бы мы выполнили описанный код, то получили бы такой вот график

Но параметр времени «Hour» цикличен и нам показалось, что более интересно и наглядно его можно отобразить на «циферблате». Поэтому перейдем к другой системе координат командой
coord_polar(start = 0) +
где start = 0 указывает на начальное смещение в радианах от стартовой точки – 12 часов
Дальнейший код связан с «оформительством» и мало интересен
Сохраняем рисунок на диск командой
ggsave('HourFeeling_Q1.png', p, width = 7, height = 7, dpi=300)
Если будет интересен код любого другого графика из статьи «О чем говорят страхователи…», то задавайте вопрос на facebook.com/DDM.center.ua/ или в комментариях под статьей в блоге https://www.ddm.center/blog
Подготовка текста и построение облака слов
Ранее в статье «О чем говорят в Игре Престолов» я уже описывал код R для получения облака слов. Чтобы не повторяться, здесь опишу только некоторые отличия.
Так как мы хотим посмотреть как отличаются наборы слов отзывов с хорошими и плохими оценками, то нам надо будет сформировать два текстовых вектора.
Для начала сгруппируем оценки в два вектора
PositiveVector<-c('1. Автор восхищен', '2. Автор доволен', '3. Автор спокоен')
NegativeVector<-c('4. Автор волнуется', '5. Автор зол', '6. Нет оценки')
Из нашей общей таблицы сформируем два текстовых блока, отбирая оценки, которые входят в первый и во второй векторы
PositiveFeelingFullText_df<-filter(insur_df, FeelingText %in% PositiveVector)
NegativeFeelingFullText_df<-filter(insur_df, FeelingText %in% NegativeVector)
и соберем их в два документа, приводя все знаки к нижнему регистру и кодируя в utf8, чтобы корректно отображались русские символы.
PositiveDoc<-enc2utf8(tolower(paste(as.vector(PositiveFeelingFullText_df$Text),collapse = " ")))
NegativeDoc<-enc2utf8(tolower(paste(as.vector(NegativeFeelingFullText_df$Text),collapse = " ")))
Сформируем текстовый корпус для дальнейшего анализа, указав источником таблицу данных (data.frame)
dv<-c(PositiveDoc,NegativeDoc)
docs <- data.frame(docs = dv, row.names=c('Positive','Negative'))
Эти манипуляции нам нужны, чтобы построить сравнительное облако слов по двум документам с положительными оценками, и с отрицательными оценками.
ds <- DataframeSource(docs)
articles<-Corpus(ds,readerControl = list(reader = readPlain, language = "ru",load = T))
Дальше стандартная очистка данных и подготовка матрицы для построение облака слов.
tdm <- TermDocumentMatrix(articles,control = list(tokenize=scan_tokenizer))
m <- as.matrix(tdm)
v <- sort(rowSums(m),decreasing=TRUE)
d <- data.frame(word = names(v),freq=v)
Ну и, собственно, облако
wordcloud2(d, size = 1,minRotation = pi/6, gridSize =0, maxRotation = -pi/6, rotateRatio = .7,color=pal, backgroundColor="seashell", shape = 'circle', widgetsize=c(900,480))

Также строим сравнительное облако слов
colnames(m) <- c("Позитивные оценки","Негативные оценки")
comparison.cloud(m,scale=c(8,1),max.words=1500, random.order=FALSE,rot.per=.1, colors=pal1, use.r.layout=TRUE,title.size=3)

Кому интересно, можно еще такую вот дендрограмму построить
my.df <- as.data.frame(inspect(tdm))
my.df.scale <- scale(my.df)
d <- dist(my.df.scale,method="euclidean")
fit <- hclust(d, method="ward.D2")
plot(fit)

Надеюсь, эта статья была интересной и может быть даже полезной
Загрузите отчет в pdf-формате