初めに
題名の通り。もう少し改良は加えていきたいが、基本的なことはこの仕組みで実行できる。
Rのイメージにpythonをaptでインストールしている。他にもっと良いやり方はあるかも、、、
環境
Windows10 + Docker DeskTop(WSL2 - Ubuntu 20.04.3 LTS)
docker -v
Docker version 20.10.11, build dea9396
フォルダ構成
fastapi
├ Dockerfile
└ app
├ main.py
└ rexec
└ graph.py
ファイル
Dockerfile
# R-4.1.2
FROM r-base:4.1.2
# ダウンロード先のミラーサイト設定(53のJapanを選択)
RUN R -e 'chooseCRANmirror(ind=53)'
# ggplot2のパッケージをインストール
RUN R -e 'install.packages("ggplot2")'
# shinyのパッケージをインストール
RUN R -e 'install.packages("shiny", dependencies = TRUE)'
# python-3.9
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y python3.9 python3-pip python3-setuptools python3-dev
# pipのupgrade
RUN pip3 install -U pip
# fastapiとuvicornのインストール
RUN pip3 install fastapi uvicorn pyper
# ポートはデフォルトの8000で利用
EXPOSE 8000
# デフォルトのディレクトリ設定
WORKDIR /app
# サーバー起動
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import json
import uvicorn
import os
from rexec import graph
app = FastAPI()
# 許可するドメインを設定
origins = [
# "http://domainname.com",
# "https://domainname.com",
"http://localhost",
"http://localhost:3000",
]
# クロス設定
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/api/graph/{type}")
def read_item(type: int):
base64image = graph.getBase64Graph(type)
return base64image
graph.py
import pyper
import os
def getBase64Graph(type: int = 1):
r = pyper.R()
r(
"""
# ライブラリインポート
library(ggplot2)
library(shiny)
"""
)
# typeで出力するグラフを切り替え
if type == 1:
r(
"""
p <- ggplot(data = iris,
mapping = aes(x=Sepal.Width)) +
geom_density(fill = "gold")
"""
)
if type == 2:
r(
"""
p <- ggplot(data = iris,
mapping = aes(x=Species, y=Petal.Length, fill=Species)) +
geom_boxplot()
"""
)
if type == 3:
r(
"""
p <- ggplot(data = iris,
mapping = aes(x=Sepal.Length, y=Petal.Length)) +
geom_point(aes(colour=Species)) +
geom_smooth(method = "lm", aes(colour=Species))
"""
)
# 画像出力&base64変換(sample.pngはアクセスorユーザー毎等でユニークな名前にすることを推奨)
filename = "sample.png"
r(
"""
# 画像出力(サイズは dpi*width × dpi*height のピクセルサイズとなる)
ggsave(filename = "$filename",
plot = p,
device = "png",
width = 6.4, height = 4.8,
dpi = 100)
# base64変換
b64image <- base64enc::dataURI(file = "$filename", mime = "image/png")
""".replace(
"$filename", filename
)
)
# base64で取得
res = r.get("b64image")
# ファイル削除
os.remove(filename)
return res
コンテナ構築シェル
# dockerファイルのイメージビルド
docker build -t pyrimage .
# コンテナ化(-v appのフォルダ共有、-p 8000ポート使用)
docker run -d --name pyrgraph -v $PWD/app:/app -p 8000:8000 pyrimage
確認方法
以下のようなReactのページを用意
※axiosはnpmでインポートしてください
App.jp
import { React, useState, useEffect } from "react";
import axios from "axios";
const App = () => {
const [base64image, setBase64image] = useState(null);
const baseUrl = "http://localhost:8000/api/graph/";
const getBase64Image = async (getNo) => {
const responce = await axios.get(`${baseUrl}${getNo}`);
setBase64image(responce.data);
};
return (
<>
<div>
<button onClick={(e) => getBase64Image(1)}>Graph1</button>
<button onClick={(e) => getBase64Image(2)}>Graph2</button>
<button onClick={(e) => getBase64Image(3)}>Graph3</button>
</div>
{base64image !== null ? <img src={base64image} /> : null}
</>
);
};
export default App;
こんな感じ
最後に
image作成に時間がかかるのがイマイチ、、shinyのインストールは課題。base64にしたいだけだからpythonでやった方が良いかも、、
データをポストして、内容に応じたグラフを描画とかしたい。
個人でグラフ画像とか引っ張りたいときは、Jupyterとか使うと簡単にできるんだろうけどね、、