среда, 8 октября 2014 г.

Язык R в помощь хабра-статисту

Язык R в помощь хабра-статисту из песочницы

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

Цель: Собрать средствами языка R данные с любимого HabraHabr'а и провести, собственно то, для чего и был создан язык R, а именно: статистический анализ.

Итак, прочтя этот топик вы узнаете:
  • Как можно использовать R для извлечения данных из Web ресурсов
  • Как преобразовывать данные для последующего анализа
  • Какие ресурсы крайне рекомендуются к прочтению всем желающим познакомиться с R поближе


Ожидается, что читатель достаточно самостоятелен, чтобы самому ознакомиться с основными конструкциями языка. Для этого как никак лучше подойдут ссылки в конце статьи.


Подготовка


Нам понадобятся следующие ресурсы:

После установки вы должны увидеть что-то типа этого:
image
В правой нижней панели на вкладке Packages вы можете найти список установленных пакетов. Нам понадобится дополнительно установить следующие:
  • Rcurl — для работы с сетью. Все кто работал с CURL сразу поймет все открывающиеся возможности.
  • XML — пакет для работы с DOM деревом XML документа. Нам понадобится функционал нахождения элементов по xpath

Жмите «Install Packages», выбирайте нужные, а затем выделите их галочкой, чтобы они загрузились в текущее окружение.

Получаем данные


Чтобы получить DOM объект документа полученного из интернета достаточно выполнить следующие строчки:
url<-"http://habrahabr.ru/feed/posts/habred/page10/"
cookie<-"Мои сверхсекретные печеньки"
html<-getURL(url, cookie=cookie)
doc<-htmlParse(html)

Обратите внимание на передаваемые cookie. Если вы захотите повторить эксперемент, то вам надо будет подставить свои cookie, которые получает ваш браузер после авторизации на сайте. Далее нам надо получить интересующие нас данные, а именно:
  • Когда запись была опубликована
  • Сколько было просмотров
  • Сколько человек занесло запись в избранные
  • Сколько было нажатий на +1 и -1 (суммарно)
  • Сколько было +1 нажатий
  • Сколько -1
  • Текущий рейтинг
  • Количество комментариев

Не в даваясь особо в подробности приведу сразу код:
  published<-xpathSApply(doc, "//div[@class='published']", xmlValue)
  pageviews<-xpathSApply(doc, "//div[@class='pageviews']", xmlValue)
  favs<-xpathSApply(doc, "//div[@class='favs_count']", xmlValue)
  scoredetailes<-xpathSApply(doc, "//span[@class='score']", xmlGetAttr, "title")
  scores<-xpathSApply(doc, "//span[@class='score']", xmlValue)
  comments<-xpathSApply(doc, "//span[@class='all']", xmlValue)
  hrefs<-xpathSApply(doc, "//a[@class='post_title']", xmlGetAttr, "href")

Здесь мы использовали поиск элементов и атрибутов с помощью xpath.
Далее крайне рекомендуется сформировать из полученных данных data.frame — это аналог таблиц базы данных. Можно будет делать запросы разного уровня сложности. Иногда диву даешься, как элегантно можно сделать в R ту или иную вещь.
posts<-data.frame(hrefs, published, scoredetailes, scores, pageviews, favs, comments)

После формирования data.frame необходимо будет подправить полученные данные: преобразовать строчки в числа, получить реальную дату в нормальном формате и т.д. Делаем это таким образом:

  posts$comments<-as.numeric(as.character(posts$comments))
  posts$scores<-as.numeric(as.character(posts$scores))
  posts$favs<-as.numeric(as.character(posts$favs))
  posts$pageviews<-as.numeric(as.character(posts$pageviews))
  
  posts$published<-sub(" декабря в ","/12/2012 ",as.character(posts$published))
  posts$published<-sub(" ноября в ","/11/2012 ",posts$published)
  posts$published<-sub(" октября в ","/10/2012 ",posts$published)
  posts$published<-sub(" сентября в ","/09/2012 ",posts$published)
  posts$published<-sub("^ ","",posts$published)
  posts$publishedDate<-as.Date(posts$published, format="%d/%m/%Y %H:%M")


Так же полезно добавить дополнительные поля, которые вычисляются из уже полученных:
  scoressplitted<-sapply(strsplit(as.character(posts$scoredetailes), "\\D+", perl=TRUE),unlist)
  if(class(scoressplitted)=="matrix" && dim(scoressplitted)[1]==4)
  {
    scoressplitted<-t(scoressplitted[2:4,])  
    posts$actions<-as.numeric(as.character(scoressplitted[,1]))
    posts$plusactions<-as.numeric(as.character(scoressplitted[,2]))
    posts$minusactions<-as.numeric(as.character(scoressplitted[,3]))
  }
  posts$weekDay<-format(posts$publishedDate, "%A")

Здесь мы всем известные сообщения вида «Всего 35: ↑29 и ↓6» преобразовали в массив данных по тому, сколько вообще было произведено действий, сколько было плюсов и сколько было минусов.

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

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

getPostsForPages<-function(pages, cookie, sleep=0)
{
  urls<-paste("http://habrahabr.ru/feed/posts/habred/page", pages, "/", sep="")
  ret<-data.frame()
  for(url in urls)
  {
    ret<-rbind(ret, getPosts(url, cookie))
    Sys.sleep(sleep)
  }
  return(ret)
}

Здесь мы используем системную функцию Sys.sleep, чтобы не устроить случайно хабраэффект самом хабру:)
Данную функцию предлагается использовать следующим образом:
posts<-getPostsForPages(10:100, cookie,5)

Таким образом мы скачиваем все страницы с 10 по 100 с паузой в 5 секунд. Страницы до 10 нам не интересны, так как оценки там еще не видны. После нескольких минут ожидания все наши данные находятся в переменной posts. Рекомендую их тут же сохранить, чтобы каждый раз не беспокоить хабр! Делается это таким образом:
write.csv(posts, file="posts.csv")

А считываем следующим образом:
posts<-read.csv("posts.csv")


Ура! Мы научились получать статистические данные с хабра и сохранять их локально для следующего анализа!

Анализ данных


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

Пользователи хабра значительно охотнее плюсуют, чем минусуют.

Это видно по следующему графику. Заметьте, на сколько «облако» минусов равномернее и шире, чем разброс плюсов. Корреляция плюсов от количества просмотров значительно сильнее, чем для минусов. Другими словами: плюсуем не думая, а минусуем за дело!
(Прошу прощения за надписи на графиках: пока не разобрался, как выводить их правильно на русском языке)



Действительно есть несколько классов постов

Это утверждение в упомянутом посте использовалось как данность, но я хотел убедиться в этом в действительности. Для этого достаточно посчитать среднюю долю плюсов к общему количеству действий, тоже самое для минусов и разделить второе на первое. Если бы все было однородно, то множество локальных пиков на гистограмме мы не должны наблюдать, однако они там есть.

Как вы можете заметить, есть выраженные пики в районе 0.1, 0.2 и 0.25. Предлагаю читателю самому найти и «назвать» эти классы.
Хочу заметить, что R богата алгоритмами для кластеризации данных, для аппроксимации, для проверки гипотез и т.п.

Полезные ресурсы


Если вы действительно хотите погрузиться в мир R, то рекомендую следующие ссылки. Пожалуйста, поделитесь в комментариях вашими интересными блогами и сайтами на тему R. Есть кто-нибудь пишущий об R на русском?



Считаю, что такие языки как R, haskell, lisp, javascript, python — должен знать каждый уважающий себя программист: если не для работы, то как минимум для расширения кругозора!

P.S. Обещанный исходник
+49
24734
247
DbLogs 10,1

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

+6
korjik,#
Дело в том, что после ознакомления с языком R я крайне искоса смотрю на любые попытки, что-то посчитать в экселе.

Каждому своё. Какие-то лёгкие вычисления просто глупо делать под R. Да и документы заказчикам отправлять легче в табличках.

Хотя в целом довольно интересно.
+11
Ivanhoe,#
Хорошая статья.

Но:
статист
статистик
+1
Ivanhoe,#
Но я все же использовал бы для парсинга сайта, очистки данных Python + Scrapy (или <имя_вашего_любимого_инструмента>), а R оставил бы для собственно анализа.
+1
DbLogs,#
Полностью с Вами согласен, но именно в этом слове и скрывался подтекст;) Спасибо, что раскрыли:)
0
Ashot,#
Но надо признать, что и с R я познакомился лишь неделю назад.

позвольте угадаю — курс на codeshcool?
0
DbLogs,#
И да и нет:) Заприметил еще записываясь на курсы на Coursera, а непосредственно поставил и попробовал именно после прохождения курсов на CodeSchool.
0
bO_oblik,#
Прикольно, и синтаксис хорош, только "<-" вместо православного "=" удручает :)
0
ffriend,#
"=" тоже разрешено, но не приветствуется: "<-" более информативна в смысле того, что сразу понятно, в какую сторону идёт присваивание. Кроме того, есть ещё и другая стрелочка — "->", которая инвертирует порядок присваивания:

x[1] -> y[1]

С «равно» такое, естественно, не прокатывет. 
+1
theoden,#
Про эти знаки хороший, на мой взгляд, комментарий сделал Phaker.
0
titanbull,#
Еще одно очень большое преимущество R заключается в том, что используемый код, полученные графики, таблицы и т.д можно напрямую переводить в Latex c помощью расширения Sweave.
0
seriyPS,#
Да вы издеваетесь? Описали как с помощью R кравлить веб-странички, а как анализировать данные — нет. Нельзя так делать!

Сам пару месяцев назад книжку прочитал, но применения к текущим задачам не нашел. И вот неделю назад захотел бенчмарк один устроить — вот тут R и пригодился.
Идея в том, что бенчмарки запускались bash скриптами, потом человекочитаемый текстовый вывод бенчмарка преобразовывался в CSV файлы python скриптом (этот шаг в принципе можно было бы пропустить), и уже CSV файлы обрабатывались из R (строились графики в основном — ggplot2). Так работать с R очень удобно, легко и приятно.

Ещё раз: лучший интерфейс для передачи данных в R это CSV файлы, созданные заранее (ну или реляционная БД). Не нужно самим R кравлить веб-странички, запускать сторонние программы и пр. Он для этого не предназначен!
0
grimich,#
Саму книжку то насоветуйте
+1
seriyPS,#
Полноценной книжкой это вряд ли назовёшь, но для экспресс-курса / знакомства с возможностями достаточно:
An Introduction to R; W. N. Venables, D. M. Smith and the R Core Team

cran.r-project.org/manuals.html

В итоге помогли 2 вещи:
Первое (и самое, пожалуй, важное) — наличие подходящей задачи (обработка результатов бенчмарка).
Второе — осознание того, что его можно применять, хоть и с натяжкой, примерно так же, как SQL. Т.е. думаешь «а как бы я это на SQL сделал?» и пишешь потом это на R.
0
grimich,#
спасибо
0
theoden,#
Почему обязательно издевается? Взгляните на это с другой стороны: про анализ данных на Хабре были статьи. А про то, что краулить можно прямо из R я, к примеру, узнал впервые из этой статьи. Спасибо автору.
0
seriyPS,#
Ну классно, давайте писать статьи «как написать интернет-магазин на С», «отказоустойчивый веб-сервер на BASH», «Приложения для iOS на матлаб», «Пишем 3D видеоигру на PHP»…
0
theoden,#
Предлагаю взглянуть с точки зрения управления, к примеру. Рассмотрим 2 полностью идентичных системы, решающих задачу из топика «распарсить и провести стат.анализ». Первая написана только на R, вторая использует Python для первой подзадачи и R — для второй. Мастер, создавший это, уволился. Для поддержания первой системы требуется найти человека, знающего только R. Для второй — нужно либо найти двух людей, знающих каждый по языку, либо искать сочетание знания именно этих языков в одном человеке. На мой взгляд решить задачу поддержки первой системы проще.

Похоже, для вас парсинг при помощи R выглядит, как приведенные выше преувеличения. Но представьте, что есть люди, которым это меньшенапоминает забивание гвоздей микроскопом.
0
Drabadum,#
Расскажите, есть ли решительное преимущество R перед Matlab/Octave? (кроме бесплатности)
0
discobot,#
Для занятий статистикой и машинным обучением безусловно есть (нет наверное ни одного современного алгоритма в этой области не реализованного на R).
0
signum,#
Во-первых, как сказал discobot, большой выбор библиотек. Во-вторых, R является объектным языком, так что зачастую удобно выводить данные, поскольку многие функции возвращают объект с возможностью представления результатов в удобном виде через plot или просто в консоль. В принципе, оба языка являются интерактивными, но R с этой точки зрения, как мне кажется, лучше развит.

С другой стороны, если нужно какие-то алгоритмы реализовывать, то Octave может быть удобнее, на мой взгляд.
+2
Neir0,#
Меня R удручает своим необычным синтаксисом, странными типами, плохими сообщениями об ошибках компиляции. Даже самые простые операции по преобразованию данных причиняют мне боль. Мой мозг отказывается воспринимать applyy sapply и прочие подобные функции. Очень не хватает LINQ из c#. Конечно, огромное количество пакетов это несомненный плюс, но вот сам язык… сделайте меня развидеть это.
0
biseptol,#
Я когда-то написал Thunderplot, потому что задолбало запускать тяжеленный убогий Excel, муторно вычищать мусор из данных, или даже учить новый язык программирования, только ради того, чтобы быстренько глянуть на график из файла.

Кому интересно, могу выслать promotion code для Mac App Store (только для OS X, к сожалению), интересно послушать мнение хабраюзеров.

Комментариев нет:

Отправить комментарий