Совмещение R и Python: зачем, когда и как?
- пятница, 9 ноября 2018 г. в 00:18:44
R package <INSERT NAME: new great stats model>
. Ваши шансы на успех очень велики! Так, несомненным плюсом R являются возможности продвинутого статистического анализа. В особенности, для ряда специфических областей науки и практики (эконометрика, биоинформатика и др.). На мой взгляд, в R на текущий момент все еще существенно более развит анализ временных рядов. htmlwidgets
, flexdashboard
, shiny
, slidify
. Например, изначально, материалы для этой статьи были собраны в виде интерактивной презентации на slidify.seaborn
) и добавляются puFlux
, pymaclab
, и др.), в R — растет эффективность управления памятью и улучшаются возможности обработки данных (data.table
). Вот тут, например, можно посмотреть примеры базовых операций с данными на R и Python. Так что есть ли преимущество в совмещении языков для вашего проекта, решать только вам.xgboost
, xgboostExplainer
) + визуализация (Markdown-отчеты) с помощью Rnumpy
, pandas
) + вывод результата в dashboard или shiny приложение на R (flexdashboard
, htmlwidgets
)rvest
) + NLP на Python + параметризованный отчет на R (RMarkdown Parameterized Reports
)<cmd_to_run> <path_to_script> <any_additional_args>
<cmd_to_run>
— команда для выполнения скрипта R или Python с помощью командной строки <path_to_script>
— директория расположения скрипта <any_additional_args>
— список аргументов на вход скрипту Command | Python | R |
---|---|---|
Cmd | python path/to/myscript.py arg1 arg2 arg3 |
Rscript path/to/myscript.R arg1 arg2 arg3 |
Fetch arguments | # list, 1st el. - file executed |
# character vector of args |
max.R
. # max.R
randomvals <- rnorm(75, 5, 0.5)
par(mfrow = c(1, 2))
hist(randomvals, xlab = 'Some random numbers')
plot(randomvals, xlab = 'Some random numbers', ylab = 'value', pch = 3)
# calling R from Python
import subprocess
# Define command and arguments
command = 'Rscript'
path2script = 'path/to your script/max.R'
# Variable number of args in a list
args = ['11', '3', '9', '42']
# Build subprocess command
cmd = [command, path2script] + args
# check_output will run the command and store to result
x = subprocess.check_output(cmd, universal_newlines=True)
print('The maximum of the numbers is:', x)
# splitstr.py
import sys
# Get the arguments passed in
string = sys.argv[1]
pattern = sys.argv[2]
# Perform the splitting
ans = string.split(pattern)
# Join the resulting list of elements into a single newline
# delimited string and print
print('\n'.join(ans))
# calling Python from R
command = "python"
# Note the single + double quotes in the string (needed if paths have spaces)
path2script ='"path/to your script/splitstr.py"'
# Build up args in a vector
string = "3523462---12413415---4577678---7967956---5456439"
pattern = "---"
args = c(string, pattern)
# Add path to script as first arg
allArgs = c(path2script, args)
output = system2(command, args=allArgs, stdout=TRUE)
print(paste("The Substrings are:\n", output))
Medium Storage | Python | R |
---|---|---|
Flat files | ||
csv | csv, pandas | readr, data.table |
json | json | jsonlite |
yaml | PyYAML | yaml |
Databases | ||
SQL | sqlalchemy, pandasql, pyodbc | sqlite, RODBS, RMySQL, sqldf, dplyr |
NoSQL | PyMongo | RMongo |
Feather | ||
for data frames | feather | feather |
Numpy | ||
for numpy objects | numpy | RcppCNPy |
numpy
объектов в R и обратно есть быстрая и устойчивая библиотека RCppCNPy
. Пример её использования можно посмотреть тут. feather
, разрабатываемый специально для передачи дата фреймов между R и Python. Изначальная фишка формата — заточенность на R и Python, легкость обработки на обоих языках и очень быстрая запись и чтение. Идея отличная, но с реализацией, как это иногда бывает, есть нюансы. Сами разработчики формата не раз отмечали, что он пока не подходит для долгосрочных решений. При апдейте библиотек для работы с форматом весь процесс может сломаться и потребовать значительных изменений кода. feather
значимо опережает по скорости ключевые библиотеки для работы с классическим форматом csv. data.table
и dplyr
. Работа с feather оказалась наиболее быстрой, но обход по скорости data.table
не высокий. При этом есть определенные сложности с настройкой и работой с feather для R. И поддержка вызывает сомнения. Пакет feather
последний раз обновлялся год назад. pandas
. Выигрыш feather по скорости оказался значительным, проблем с работой с форматом на Python 3.5 не обнаружилось. rpy2
. Работает быстро, имеет хорошее описание как часть официальной документации pandas
, так и на отдельном сайте. Главная фишка — интеграция с pandas
. Ключевым объектом для передачи информации между языками является data frame
. Также декларируется прямая поддержка наиболее популярного пакета для визуализации R ggplot2
. Т.е. пишете код в python, график видите непосредственно в IDE Python. Тема с ggplot2
, правда, барахлит для Windows. rpy2
, пожалуй, один — необходимость потратить некоторое время на изучение туториалов. Для корректной работы с двумя языками это необходимо, поскольку есть неочевидные нюансы в синтаксисе и сопоставлении типов объектов при передаче. К примеру, при передаче числа из Python на вход в R вы получаете не число, а вектор из одного элемента. pipe
, которая находится на втором месте в таблице ниже, — это скорость. Реализация через пайпы, действительно, в среднем ускоряет работу (на эту тему даже есть статья в JSS), а наличие поддержки работы с pandas объектами в R-Python внушает надежду. Но имеющиеся минусы надежно сдвигают библиотеку на второе место. Ключевой недостаток — это плохая поддержка установки библиотек в R через Python. Если вы хотите воспользоваться какой-либо нестандартной библиотекой в R (а R часто нужен именно для этого), то чтобы она установилась и заработала, нужно последовательно (!!!) загрузить все её dependencies. А для некоторых библиотек их может быть примерно 100500 и маленькая тележка. Второй важный недостаток — неудобная работа с графикой. Построенный график можно посмотреть, только записав его в файл на диске. Третий недостаток — слабая документация. Если случается косяк чуть за границами стандартного набора, решение часто не найдешь ни в документации, ни на stackoverflow. pyrserve
проста в использовании, но и значительно ограничена по функционалу. Например, не поддерживает передачу таблиц. Обновление и поддержка библиотеки разработчиками также оставляет желать лучшего. Версия 0.9.1 является последней из доступных уже более 2 лет. Библиотеки | Комментарии |
---|---|
rpy2 | — C-level interface — прямая поддержка pandas — поддержка графики (+ggplot2) — слабая поддержка Windows |
pyper | — Python code — use of pipes (в среднем быстрее) — косвенная поддержка pandas — ограниченная поддержка графики — плохая документация |
pyrserve | — Python code — use of pipes (в среднем быстрее) — косвенная поддержка pandas — ограниченная поддержка графики — плохая документация — низкий уровень поддержки проекта |
reticulate
. Минусы в ней пока не просматриваются. А вот преимуществ достаточно: активная поддержка, понятная и удобная документация, вывод результатов выполнения скриптов и ошибок сразу в консоль, легкая передача объектов (как и в rpy2
, основной объект — дата фрейм). По поводу активной поддержки приведу пример из личного опыта: средняя скорость ответа на вопрос в stackoverflow и github/issues составляет примерно 1 минуту. Для работы можно как выучить синтаксис по туториалу и затем подключать отдельные python-модули и писать функции. Или же выполнять отдельные куски кода в python через функции py_run
. Они позволяют легко выполнить python-скрипт из R, передав необходимые аргументы и получить объект со всем выходом скрипта. rPython
. Ключевое преимущество — простота синтаксиса. Всего 4 команды и вы мастер использования R и Python. Документация также не отстает: все изложено понятно, просто и доступно. Главный недостаток — кривая реализация передачи data frame. И в R и в Python требуется дополнительный шаг, чтобы переданный объект стал таблицей. Второй важный недостаток — отсутствие вывода результата выполнения функции в консоль. При запуске какой-то python-команды из R непосредственно из R вы не сможете быстро понять, успешно она выполнилась или нет. Даже в документации был хороший пассаж от авторов, что для того, чтобы увидеть результат выполнения python кода нужно зайти в сам python и посмотреть. Работа с пакетом из Windows возможна только через боль, но возможна. Полезные ссылки есть в таблице. Rcpp
. На самом деле, это очень хороший вариант. Как правило, то, что реализовано в R с помощью C++ работает устойчиво, качественно и быстро. Но требует времени, чтобы разобраться. Поэтому здесь и указано в конце списка. Библиотеки | Комментарии |
---|---|
reticulate | — хорошая документация — активное развитие (с авг 2016 г.) — волшебная функция
|
rPython | — передача данных через json — непрямая передача таблиц — хорошая документация — слабая поддержка Windows — часто падает с Anaconda |
Rcpp | — через C++ (Boost.Python и Rcpp) — нужны специфические навыки — хороший пример |
max.R
. from rpy2.robjects import pandas2ri # loading rpy2
from rpy2.robjects import r
pandas2ri.activate() # activating pandas module
df_iris_py = pandas2ri.ri2py(r['iris']) # from r data frame to pandas
df_iris_r = pandas2ri.py2ri(df_iris_py) # from pandas to r data frame
plotFunc = r("""
library(ggplot2)
function(df){
p <- ggplot(iris, aes(x = Sepal.Length, y = Petal.Length))
+ geom_point(aes(color = Species))
print(p)
ggsave('iris_plot.pdf', plot = p, width = 6.5, height = 5.5)
}
""") # ggplot2 example
gr = importr('grDevices') # necessary to shut the graph off
plotFunc(df_iris_r)
gr.dev_off()
splitstr.py
. library(reticulate)
# aliasing the main module
py <- import_main()
# set parameters for Python directly from R session
py$pattern <- "---"
py$string = "3523462---12413415---4577678---7967956---5456439"
# run splitstr.py from the slide 11
result <- py_run_file('splitstr.py')
# read Python script result in R
result$ans
# [1] "3523462" "12413415" "4577678" "7967956" "5456439"
R majic
вместе с обсуждавшейся выше rpy2
. Несложно, в целом, но нужно опять же потратить некоторое время на изучение синтаксиса. Другой вариант: установка IRKernel
. Но в этом случае получится только запускать кернелы отдельно, передавая файлы через запись на диске.reticulate
и далее указывать для каждой ячейки ноутбука язык, на котором вы будете творить. Удобная штука!beaker
. Плюс можно было сразу удобно публиковать результат (в общий доступ). Но почему-то всего этого больше нет, даже старые публикации удалены — разработчики сконцентрировались на проекте BeakerX. import pandas as pd
import wbdata as wd
# define a period of time
start_year = 2013
end_year = 2017
# list of countries under inflation targeting monetary policy regime
countries = ['AM', 'AU', 'AT', 'BE', 'BG', 'BR', 'CA', 'CH', 'CL', 'CO', 'CY', 'CZ', 'DE', 'DK', 'XC', 'ES', 'EE', 'FI', 'FR', 'GB', 'GR', 'HU', 'IN', 'IE', 'IS', 'IL', 'IT', 'JM', 'JP', 'KR', 'LK', 'LT', 'LU', 'LV', 'MA', 'MD', 'MX', 'MT', 'MY', 'NL', 'NO', 'NZ', 'PK', 'PE', 'PH', 'PL', 'PT', 'RO', 'RU', 'SG', 'SK', 'SI', 'SE', 'TH', 'TR', 'US', 'ZA']
# set dictionary for wbdata
inflation = {'FP.CPI.TOTL.ZG': 'CPI_annual', 'NY.GDP.MKTP.KD.ZG': 'GDP_annual'}
# download wb data
df = wd.get_dataframe(inflation, country = countries, data_date = (pd.datetime(start_year, 1, 1), pd.datetime(end_year, 1, 1)))
print(df.head())
df.to_csv('WB_data.csv', index = True)
library(tidyverse)
library(data.table)
library(DT)
# get df with python results
cpi <- fread('WB_data.csv')
cpi <- cpi %>% group_by(country) %>% summarize(cpi_av = mean(CPI_annual), cpi_max = max(CPI_annual),
cpi_min = min(CPI_annual), gdp_av = mean(GDP_annual)) %>% ungroup
cpi <- cpi %>% mutate(country = replace(country, country %in% c('Czech Republic', 'Korea, Rep.', 'Philippines',
'Russian Federation', 'Singapore', 'Switzerland',
'Thailand', 'United Kingdom', 'United States'),
c('Czech', 'Korea', 'Phil', 'Russia', 'Singap', 'Switz', 'Thai', 'UK', 'US')),
gdp_sign = ifelse(gdp_av > 0, 'Positive', 'Negative'),
gdp_sign = factor(gdp_sign, levels = c('Positive', 'Negative')),
country = fct_reorder(country, gdp_av),
gdp_av = abs(gdp_av),
coord = rep(ceiling(max(cpi_max)) + 2, dim(cpi)[1])
)
print(head(data.frame(cpi)))
library(viridis)
library(scales)
ggplot(cpi, aes(country, y = cpi_av)) +
geom_linerange(aes(x = country, y = cpi_av, ymin = cpi_min, ymax = cpi_max, colour = cpi_av), size = 1.8, alpha = 0.9) +
geom_point(aes(x = country, y = coord, size = gdp_av, shape = gdp_sign), alpha = 0.5) +
scale_size_area(max_size = 8) +
scale_colour_viridis() +
guides(size = guide_legend(title = 'Average annual\nGDP growth, %', title.theme = element_text(size = 7, angle = 0)),
shape = guide_legend(title = 'Sign of\nGDP growth, %', title.theme = element_text(size = 7, angle = 0)),
colour = guide_legend(title = 'Average\nannual CPI, %', title.theme = element_text(size = 7, angle = 0))) +
ylim(floor(min(cpi$cpi_min)) - 2, ceiling(max(cpi$cpi_max)) + 2) +
labs(title = 'Average Inflation and GDP Rates in Inflation Targeting Countries',
subtitle = paste0('For the period 2013-2017'),
x = NULL, y = NULL) +
coord_polar() +
theme_bw() +
theme(legend.position = 'right',
panel.border = element_blank(),
axis.text.x = element_text(colour = '#442D25', size = 6, angle = 21, vjust = 1))
ggsave('IT_countries_2013_2017.png', width = 11, height = 5.7)
rpy2
и reticulate
.