Когда я впервые начал работать специалистом по данным (или что-то в этом роде), мне сказали программировать на C++ и Java. Затем появился R, и это освободило; моя способность проводить анализ данных существенно возросла. По мере роста размера и сложности моих приложений я начал скучать по структуре Java/C++. В то время Python казался хорошим компромиссом, поэтому я снова переключился. После прихода в Mango Solutions я заметил, что не являюсь аномалией, большинство специалистов по данным здесь знают и Python, и R.

В настоящее время всякий раз, когда я делаю свою работу в R, в моей голове постоянно звучит ворчливый голос, говорящий мне: «Вы должны сделать это в Python». И когда я делаю свою работу на Python, он говорит мне: «Вы можете сделать это быстрее в R». Поэтому, когда вышла упаковка reticulate, я был вне себя от радости, и в этом посте я объясню вам, почему.

ре-тик-у-поздно (rĭ-tĭkˈyə-lĭt, -latˌ)

Так что же именно делает reticulate? Его цель — облегчить взаимодействие между Python и R. Он делает это, встраивая сеанс Python в сеанс R, что позволяет вам вызывать функции Python из R. Я не собираюсь вдаваться в подробности того, как пакет работает здесь; RStudio проделала большую работу, предоставив прекрасную документацию и веб-семинар. Вместо этого я покажу несколько примеров основного функционала.

Как и R, House of Python был построен на пакетах. За исключением Python, вы не загружаете функциональность из пакета через вызов library, а вместо этого импортируете модуль. reticulate имитирует это поведение и открывает все преимущества импортированного модуля.

library(reticulate)
np <- import("numpy")
# the Kronecker product is my favourite matrix operation
np$kron(c(1,2,3), c(4,5,6))
## [1]  4  5  6  8 10 12 12 15 18

В приведенном выше коде я импортирую модуль numpy, который является мощным пакетом для всех видов числовых вычислений. Затем reticulate дает нам интерфейс ко всем функциям (и объектам) из модуля numpy. Я могу вызывать эти функции точно так же, как и любую другую функцию R, и передавать объекты R, reticulate позаботится о том, чтобы объекты R были преобразованы в соответствующие объекты Python.

Вы также можете запустить код Python через source_python, если это весь скрипт, или py_eval/py_run_string, если это одна строка кода. Любые объекты (функции или данные), созданные скриптом, загружаются в вашу среду R. Ниже приведен пример использования py_eval.

data("mtcars")
py_eval("r.mtcars.sum(axis=0)")
## mpg      642.900
## cyl      198.000
## disp    7383.100
## hp      4694.000
## drat     115.090
## wt       102.952
## qsec     571.160
## vs        14.000
## am        13.000
## gear     118.000
## carb      90.000
## dtype: float64

Обратите внимание на использование префикса r. перед объектом mtcars в коде Python. Объект r предоставляет среду R для сеанса Python, его эквивалентом в сеансе R является объект py. mtcars data.frame преобразуется в pandas DataFrame, к которому я затем применяю функцию sum для каждого столбца.

Очевидно, что RStudio приложила много усилий, чтобы обеспечить плавный интерфейс с Python, от простого преобразования объектов до интеграции с IDE. reticulate не только позволит пользователям R извлечь выгоду из множества функциональных возможностей Python, я полагаю, что это также обеспечит более тесное сотрудничество и более широкий обмен знаниями.

Введите почтальон

Так что же именно вы можете сделать с Python, чего не можете с R? Я задавал себе тот же вопрос, пока не наткнулся на следующий вариант использования.

Когда я помогал коллеге с записью в блоге, мне предложили опубликовать ее во вторник. Никакого обоснования не было дано, поэтому, естественно, я задался вопросом, могу ли я предоставить его, используя данные. Данные должны поступать от R-блогеров. Это отличный ресурс для чтения сообщений в блогах о R (и связанных темах), а также они предоставляют ежедневный информационный бюллетень со ссылкой на сообщения в блоге за тот день. В то время новостная рассылка казалась самым простым способом сбора данных 1. Все, что мне нужно было сделать сейчас, это извлечь данные из моей учетной записи Gmail.

В этом и заключается проблема, так как я хочу избежать запросов к серверу Gmail (это не упростит воспроизведение). К счастью, Google упростил загрузку ваших данных (благодаря Фронту освобождения данных Google) через Google Архиватор. К сожалению, все электронные письма экспортируются в формате mbox. Хотя это обычный текстовый формат, для написания синтаксического анализатора на R потребуются некоторые усилия, чего я не хотел делать. А затем появился Python со встроенным парсером mbox в модуле mailbox.

Используя reticulate, я извлек необходимую информацию из каждого электронного письма.

# import the module
mailbox <- import("mailbox")
# use the mbox function to open a file connection
cnx <- mailbox$mbox("rblogs_box.mbox")
# the messages are stored as key/value pairs
# in this case they are indexed by an integer id
message <- cnx$get_message(10L)
# each message has a number of fields with meta-data
message$get("Date")
## [1] "Mon, 12 Dec 2016 23:56:19 +0000"
message$get("Subject")
## [1] "[R-bloggers] Building Shiny App exercises part 1 (and 7 more aRticles)"

И вот оно! Я только что прочитал электронное письмо из файла mbox без особых усилий. Конечно, мне нужно будет сделать это для всех сообщений, поэтому я написал функцию, которая мне поможет. И поскольку мы живем в эпоху R, я поместил эту функцию в пакет R. Вы можете найти его в репозитории MangoTheCat на github, он называется mailman.

Публиковать или не публиковать?

Мне еще предстоит обосновать публикацию сообщения в блоге в определенный день, поэтому давайте быстро перейдем к нему. Теперь, когда пакет отсортирован, я могу вызвать функцию mailman::read_messages, чтобы получить tibble со всем, что мне нужно.

Мы можем извлечь количество сообщений в блоге на определенную дату из темы каждого электронного письма. Объединение этого с днем ​​недели даст нам хорошее представление о том, какой день популярен.

library(dplyr)
library(mailman)
library(lubridate)
library(stringr)
messages <- read_messages("rblogs_box.mbox", type="mbox") %>% 
  mutate(Date = as.POSIXct(Date, format="%a, %d %b %Y %H:%M:%S %z"),
         Day_of_Week = wday(Date, label=TRUE, abbr=TRUE),
         Number_Articles = str_extract(Subject, "[0-9](?=[\\n]* more aRticles)"), 
         # Whenever a regex works you feel like a superhero!
         Number_Articles = as.numeric(Number_Articles) + 1,
         # Ok, sometimes it doesn't work but you're still a hero for trying!
         Number_Articles = ifelse(is.na(Number_Articles), 1, Number_Articles)) %>% 
  select(Date, Day_of_Week, Number_Articles)

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

В итоге

На мой взгляд, пакет reticulate — это новаторская разработка. Это позволяет мне сочетать хорошие части R с хорошими частями Python (он уже используется в пакетах tensorflow и keras). Кроме того, это позволяет сообществу специалистов по данным легче сотрудничать и направлять нашу энергию на выполнение задач. Это будущее, это R и Python (Rython? PRython? PyR?).

  1. После того, как я собрал все данные, Боб Рудис написал об API Feedly и опубликовал набор сообщений в блогах за более длительный период времени. Я бы сказал, что его решение предпочтительнее, хотя мои результаты немного отличаются из-за более позднего временного горизонта.

Читайте другие статьи о науке о данных на OpenDataScience.com