4.5K Views
July 13, 24
スライド概要
第114回R勉強会@東京(#TokyoR)発表資料
https://tokyor.connpass.com/event/323943/
株式会社ホクソエムの公式アカウント
簡易的 rpy2 を作ってみた 2024/7/13 hoxo-m
自己紹介 • 株式会社ホクソエム • Bluesky: @hoxo-m.bsky.social
最近やっていること • Rパッケージの作成 • RLoptimal • 医薬品開発で使われる • 用量反応試験において、被験者の最適な割付を算出する
用量反応試験 • 用量の例: 0, 2, 4, 6, 8 (mg) • それぞれの用量について、薬の効果がどれくらいか知りたい • MCP-Mod 法: 様々な用量反応曲線から最適なものを選ぶ
被験者の割り付け問題 • 被験者の数: 150人 0 mg 2 mg 4 mg 6 mg 8 mg 均等割付 30 30 30 30 30 D-optimal 45 30 15 15 45 TD-optimal 45 40 15 20 30 • 均等な割付よりも偏らせた方が MCP-Mod の精度が良くなる • RL-optimal: 被験者の割り付けを強化学習 (RL) で求める
実装上の課題 • 機械学習は Python が強い ⇒ 強化学習には Python を使う • 統計解析は R が強い ⇒ MCP-Mod には R を使う • MCP-Mod の精度が良くなるように強化学習する R パッケージ を開発したい RL-optimal (R) 強化学習 (Python) MCP-Mod (R)
実装上の課題 • R から Python を呼び出し、さらに Python から R を呼び出す • それぞれ専用のパッケージがあるので簡単 R Python reticulate R rpy2
とでも思ったか?
rpy2 が謎のエラーを吐く・・・ > library(reticulate) > import("rpy2.robjects") ****found a symbol with attributes ****found a symbol with attributes ****found a symbol with attributes ****found a symbol with attributes
公式 Q&A • https://github.com/rpy2/rpy2/issues/942 • Q. reticulate から rpy2 が使えないのですが • A. 残念ながら、これについてはサポートは提供できません
\(^o^)/オワタ
なければ作る、 それがホクソエムの誓い
簡易的 rpy2 を作ってみた 2024/7/13 hoxo-m
subprocess モジュール • Python の subprocess モジュールを使う • コマンドプロンプトに対して入出力を行える • R を起動してコードを実行し、結果を得る from subprocess import Popen, PIPE r_process = Popen("R --vanilla", stdin=PIPE, stdout=PIPE, stderr=PIPE, text=True)
最初はこれが出力される (無視したい) 出力は1行1行読んでいく stdout.readline() 入力位置を超過して読み 込むとハングアップ 入力位置で止めたい
• 特別な文字列を用意 SPECIAL_STRING = '"=== SPECIAL STRING ==="\n' • Rコンソールにこの文字列を入力する • このときの R の出力 [1] "=== SPECIAL STRING ===" • 出力を読んでいき、この文字列と一致したら読み込みを終える while True: line = r_process.stdout.readline() if line == f"[1] {SPECIAL_STRING}": break
最初はこれが出力される (無視したい) この文字列が現れたら読み込み停止
設計方針 • 作成する関数は2つ • execute: 任意のコードを実行できる。出力は無視する • この関数で MCP-Mod を実行し、結果を変数に入れる • get_value: 変数名のみ実行できる。出力を受け取る • ただし、受け取れるのは属性を持たない数値ベクトルのみ • 必要なのは MCP-Mod の精度 (スコア) なのでこれで十分 • この制限をかけることで実装が簡単になる
利用イメージ N = 30 r_code = f""" double <- function(num) num * 2 x <- 1:{N} y <- double(x) """ execute(r_code) y = get_value("y")
execute def execute(r_code): r_process.stdin.write(f"{r_code}\n") r_process.stdin.write(f"{SPECIAL_STRING}") r_process.stdin.flush() while True: line = r_process.stdout.readline() if line == f"[1] {SPECIAL_STRING}": break
get_value • 出力は数値ベクトルに限る [1] 1 2 3 • この文字列を Python のリスト [1, 2, 3] にして返す • 複数行にわたることもある [1] 1 2 3 4 5 [11] 11 12 13 14 15 6 7 8 9 10
def get_value(var_name): r_process.stdin.write(f"{var_name}\n") r_process.stdin.write(f"{SPECIAL_STRING}") r_process.stdout.readline() # 最初の1行は捨てる result = [] while True: line = r_process.stdout.readline() if line == f"> {SPECIAL_STRING}": r_process.stdout.readline() break values = line.split()[1:] result += values return [float(value) for value in result]
R側で起こっていること execute 最初の行は不要 出力として必要 get_value
GitHub で公開してます
• https://github.com/hoxo-m/RProcessPy
• 使い方
>>> r_process = RProcess()
>>> r_process.execute("x <- c(1, 2, 3)")
>>> x = r_process.get_value("x")
>>> print(x)
[1, 2, 3]
まとめ • 簡易的 rpy2 を作ってみた • GitHub で公開してます • https://github.com/hoxo-m/RProcessPy • Rパッケージ開発 (論文実装) は株式会社ホクソエムまで!