Rpy2 wrapper2: PythonからRの統計関数を簡単に呼び出す

## 1. 背景

５年ほど前にRpy2を使って、PythonからRの統計関数を呼び出すラッパーを作成していました。

https://qiita.com/MTNakata/items/129d334cea397a6ec0c3

https://qiita.com/MTNakata/items/9c80070158fbe90afdd3

Rを再勉強し始めたことと、以前のWrapperが

- クラス定義でcsvファイルから直で読み込めるようにしていたが、可読性が超低い
- 文字列で返る仕様にしていたため、結果を整理しづらい

これらの点を改善してラップし直しました。

- 可読性を重視し、関数のみで定義
- 結果をPandas DataFrameで返す

GitHubでソースコードを公開しています。

https://github.com/MTNakata/Rpy2_wrapper2

## 2. 使用方法

### 2-1. RとRパッケージのインストール

Rを[CRANミラーサイト](https://cran.ism.ac.jp/)からインストールします。

Rのコンソールで`DescTools`をインストールしておきます。

```
> install.packages("DescTools")
```

### 2-2. Pythonの環境

私はMinicondaを使っていますが、多分他のツールでも大丈夫です。

python==3.11
pandas==2.2.1
jupyterlab==4.0.11
rpy2==3.5.15

```
% conda install pandas jupyterlab
% pip install rpy2
```
rpy2は`conda install`では入りませんでした。

私はVSCode上でJupyterノートブックで実施して問題なくいけましたが、他の環境で実施した方がいらっしゃいましたら情報いただけますと幸いです。

### 2-3. 必要なパッケージのインストール

```python
import pandas as pd
import rpy2.robjects as ro
from rpy2.robjects import pandas2ri
from rpy2_wrapper2 import *
```

### 2-4. データの準備

dfに好きなPandasデータフレームを代入してください。

```r
df = pd.DataFrame()
```

データに名前をつけます(Rでの変数名になります）。

```python
dname = "dname"
```

Pandasデータフレームを、Rのdnameに渡します。

```python
ro.globalenv[dname] = pandas2ri.py2rpy(df)
```

### 2-5. パラメータの準備

解析したい列名を`variables`に、カテゴリーの列名を`category`に与えます。

例えば、[irisデータセット](https://www.statology.org/iris-dataset-r/)なら以下のように与えます。

```python
category = "Species"
variables = ["Sepal.Length","Sepal.Width","Petal.Length","Petal.Width"]
```

for文で対象列を一度に検定できるように、variablesに解析したい列名をリストで与えています。

### 2-6. 使用できる関数

- aov
- one-way (one-way ANOVA)
- tukeyHSD (Tukey検定のHSD法）
- dunnett_test （Dunnettの検定）

Rで使える関数です。詳細は割愛します。

dunnett_testのみ`DescTools`パッケージの関数を使っています。

関数はそのうち増やすかも、、、（全く未定）

### 2-7. tukeyHSDの解析例

```python
tukeyHSD(df,category,variable)
```

で全部の結果が返ってきます。p値だけに絞りたい場合は"params"の列の"p.adj"で。

irisデータセットを使いました。

```python
tukey_results = []

for variable in variables:
    result = tukeyHSD(df=dname,category=category,variable=variable)
    tukey_results.append(result)

tukey_results_df = pd.concat(tukey_results)
tukey_pvalues = tukey_results_df[tukey_results_df["params"]=="p.adj"]
tukey_pvalues.head()
```

| | params | var1      | var2      | value         | variable    |
|---|--------|-----------|-----------|---------------|-------------|
|3| p.adj  | versicolor| setosa    | 3.386180e-14  | Sepal.Length|
|7| p.adj  | virginica | setosa    | 2.997602e-15  | Sepal.Length|
|11| p.adj  | virginica | versicolor| 8.287558e-09  | Sepal.Length|
|3| p.adj  | versicolor| setosa    | 3.097522e-14  | Sepal.Width |
|7| p.adj  | virginica | setosa    | 1.360737e-09  | Sepal.Width |


### 2-8. dunnett_testの解析例

```python
dunnett_test(df,category,variable,control)
```

で全部の結果が返ってきます。p値だけに絞りたい場合は"params"の列の"pval"で。

こちらもirisデータセット。
Speciesのうち、`setosa`をコントロールにしました。

```python
control="setosa"

dunnett_results = []

for variable in variables:
    result = dunnett_test(df=dname,category=category,variable=variable,control=control)
    dunnett_results.append(result)

dunnett_results_df = pd.concat(dunnett_results)
dunnett_pvalues = dunnett_results_df[dunnett_results_df["params"]=="pval"]
dunnett_pvalues.head()
```

|| params | var1      | var2    | value         | variable    |
|---|--------|-----------|---------|---------------|-------------|
|3| pval   | versicolor| setosa  | 1.776357e-15  | Sepal.Length|
|7| pval   | virginica | setosa  | 4.440892e-16  | Sepal.Length|
|3| pval   | versicolor| setosa  | 2.220446e-16  | Sepal.Width |
|7| pval   | virginica | setosa  | 9.074665e-10  | Sepal.Width |
|3| pval   | versicolor| setosa  | 2.220446e-16  | Petal.Length|

以上です。