PypeRでPythonコードとRコードをまとめる方法

(株)日立製作所 研究開発グループ サービスコンピューティング研究部の上野です。

Pythonはさまざまなライブラリが提供されているため、その応用範囲が広い言語です。
一方でRは統計解析に特化した言語であるために応用範囲は限られますが、統計解析処理やビジュアル化に関してはPythonよりシンプルにコードを書くことができます。

Pythonでも統計解析やビジュアル化をサポートするパッケージが配布されているものの、既にあるRコードをPythonに書き換えることは手間ではないでしょうか。

ゆえに、Pythonを介してRを呼び出すPythonパッケージである[PypeR](https://pypi.org/project/PypeR/)を用いることもよくあるケースかと思います。

しかし、分散処理のように複数のインスタンスに処理を分散させるとなると、各インスタンスへ.Rファイルを配布することとなり、配布後のファイルの管理が必要となります。このような場合は、分散させたいRでの処理をPythonコード内に記述して、別々だった.pyファイルと.Rファイルを1つの.pyファイルにまとめ上げて使用した方が便利です。

## PypeRのインストール

PypeRはpipでインストールできます。

```sh
pip install pyper
```

Pyperを使用するためにはRのインストールも必要です。(Rがインストール済みである場合は、この工程は無視してください。)
Ubuntuの場合、以下のコマンドでR v4.0がインストールできます。他のOSでのインストールや他のバージョンのインストール関しては[Rの公式ドキュメント](https://cran.ism.ac.jp)をご参考ください。

```sh
# aptパッケージリストを更新 (-q, --quietで実行じの進捗状況の表示を省略)
sudo apt update -qq
# 必要なパッケージをインストール
# software-properties-commonはインストール済のソフトウェアのリポジトリを管理する。
# dirmngrは証明書失効リスト(CRL)やオンライン証明書状態プロトコル(OCSP)を管理するサーバ。
sudo apt install software-properties-common dirmngr
# Rのランタイムのリポジトリの署名鍵を取得
wget -qO- https://cloud.r-project.org/bin/linux/ubuntu/marutter_pubkey.asc | sudo tee -a /etc/apt/trusted.gpg.d/cran_ubuntu_key.asc
# CRANからR v4.0のリポジトリを取得
sudo add-apt-repository "deb https://cloud.r-project.org/bin/linux/ubuntu $(lsb_release -cs)-cran40/"
# Rのランタイムをインストール
sudo apt install r-base
```



## 使い方

PythonでPypeRオブジェクトを生成します。

```py
import pyper
r = pyper.R()
```

基本的には`r("<Rコード>")`のように、Rで実行したいコードを文字列として`r`オブジェクトに渡します。

例えば、Rコードが`arr <- 'testValue'`であるのならば下記のようになります。

```py
r("arr <- 'testValue'")
```

PythonからRへ値を渡したいときは、`r.assign('<key in R>', <value in Python>)`のようにして、値を受け取るRの変数名と渡す値を指定して`r`オブジェクトに渡します。

例えば、PythonからRへ文字列`'valuePython'`もしくは変数`'valPython'`に代入された値を渡すのであれば、それぞれ下記のようになります。

```py
# 文字列を渡す場合
r.assign('keyR', 'strPython')
# 変数の値を渡す場合
r.assign('keyR', valPython)
```

PythonからRの値を受け取りたいときは、`r.get('<key in R>')`のようにして、受け取りたい変数名を指定して`r`オブジェクトに渡します。

例えば、PythonからR内の変数`keyR`を受け取りたいのであれば、下記のようになります。

```py
KeyR = r.get('keyR')
```

## 利用例

ここでは実際に.pyファイルと.Rファイルを提示して、PypeRを用いてそれらを1つの.pyファイルへまとめ上げる一例を紹介します。

以下のようにRとPythonのコードを完全に分離しているコードがあったとします。

example.py

```py
file_path = Path(__file__).parent / 'pattern.R'
```

pattern.R

```R
# 必要なパッケージをインストール
install.packages(c("/arules_1.6-8.tar.gz"), dependencies = TRUE,repos = NULL, type = "source")
install.packages("arulesSequences", dependencies = TRUE)

# ライブラリをインポート
library(Matrix) # 行列クラスを扱うためのメソッド
library(arules) # トランザクションデータとパターンを扱うためのインフラストラクチャ
library(arulesSequences) # 頻繁なシーケンスを処理、マイニングするためのルールのアドオン

# R処理を実行 (パターンマイニング)
x <- read_baskets('data/temp.txt', info = c("sequenceID","eventID","SIZE"))
as(x, "data.frame")
s1 <- cspade(x, parameter = list(support = 0.4, maxgap = NULL), control = list(verbose = TRUE))
summary(s1)
results = as(s1, "data.frame")
```

PypeRを用いて、上記の.pyファイルと.Rファイルを1つの.pyファイルにまとめ上げると、以下のようになります。

```py
import pyper
r = pyper.R(use_pandas=True)

supportThreshold = 0.4

# 必要なパッケージをインストール
r("install.packages('Matrix')")
r("install.packages('arules')")
r("install.packages('arulesSequences')")

# ライブラリをインポート
r("library(Matrix)")
r("library(arules)")
r("library(arulesSequences)")

# PythonからRにデータを渡す
r.assign('supportTh', supportThreshold)

# R処理を実行 (パターンマイニング)
r("x <- read_baskets('~/data/temp.txt', info = c('sequenceID','eventID','SIZE'))")
r("as(x, 'data.frame')")
r("s1 <- cspade(x, parameter = list(support = supportTh, maxgap = NULL), control = list(verbose = TRUE))")
r("summary(s1)")
r("results = as(s1, 'data.frame')")

# RからPythonにデータを渡す
Results = r.get('results')
```

ちなみに、テスト用のデータdata/temp.txtは、一例は以下の通りです。

```txt
1 10 2 C D
1 15 3 A B C
1 20 3 A B F
1 25 4 A C D F
2 15 3 A B F
2 20 1 E
3 10 3 A B F
4 10 3 D G H
4 20 2 B F
4 25 3 A G H
```

.pyファイル1つでPythonもRも実行できるので、処理を複数インスタンスで実行させたい場合は、ファイルの管理がシンプルになり役に立ちます。

## まとめ

本記事では、PypeRを用いてPythonコードとRコードを1つのPythonコードにまとめる方法を紹介しました。

PypeRを用いることで、既存の.Rファイルを使ってPythonとRの連携をスムーズに行えますが、ファイル管理の手間が増えてしまう可能性があります。
特に複数のインスタンスを用いる分散処理において、RコードをPythonコードにまとめることで、Rでの処理内容が変わった際も各インスタンスでの.Rファイルの更新を考える必要がなくなります。また、Pythonコード1つで処理をすませることができるため、Rコードの配布作業をなくすことになり、作業工数を削減することができます。