1. モチベーション
遅ればせながら、弊社にもAPI化の波がやってまいりました。作成したモデルなどを社内(外)に公開しようということでしょうか。Rユーザーの私からしたら、セキュリティーポリシーどうにかして、サッとShinyでウェブアプリ作った方がいいんじゃないのと思うわけですが、会社では色々な政治があり、流れに合わせて適度な技術を適切なタイミングで提供していくことが求められるわけです。
長くなりましたが、今回は何も考えずにRのプログラムをAPI化するということだけに集中します。「R web API」などで検索すると、{plumber}という便利なパッケージが見つかりました。(※1-2)結構前からあったんですね。いくつか日本語の紹介ブログ(※3)もありますし、Qiitaにも投稿がありますね。※4
2. {plumber} パッケージ
端的に言えば、このパッケージを使うと、「Rで作成したコードの結果を、URLにアクセスすることで取得」できるようになります。あとはIT系のチームの方々にお願いすれば、HTMLやらPHP、Javascriptを使ったきれいなUIができて、立派なウェブアプリが完成するということになります。モデリングに集中できていいですね。
とりあえず、パッケージのインストールを行い、簡単な例で動かしてみます。
install.packages("plumber")
最新版が好きな人は以下の通り。
library(devtools)
install_github("trestletech/plumber")
3. コードの準備
まず、APIの実行内容を記述したコードを準備します。以下の内容を書いて「do_xxx.R」という名前で保存しておきます。
#* @get /hello
hw <- function() {
return("Hello world!")
}
定石の関数です。その上にコメントで、#* @get /hello
と書きました。今回設置するAPIは、RESTなAPI(※5)というもので、「URLにGETやPOSTなどのHTTPメソッドでアクセスすることでデータの送受信が可能になる」というものだそうです。つまり、GETで情報取ってねということでしょうか。その後ろの /hello
は名前です。
4. Rの実行
先ほど保存したファイルをRで読み込みます。(実装する際にはウェブサーバーもしくは計算用のサーバーでRを起動しておくということになるでしょうか。コマンドでRを何回も起動して毎回パッケージを読み込んだりなんてしませんよね?)
library(plumber)
rapi <- plumb("do_xxx.R")
rapi$run(port=8000)
1行目:ライブラリの読み込み
2行目:plumb()関数でコードを読み込み、rapiに代入
3行目:ポートを指定してrapi$run()を実行
Starting server to listen on port 8000
Running the swagger UI at http://127.0.0.1:8000/__swagger__/
こんなメッセージがコンソールに出たらOKです。
さっそく、ブラウザで http://localhost:8000/hello にアクセスしてみましょう。
["Hello world!"]
と表示されてるでしょうか。
MacやLinuxの人は、以下のコマンドでも確認できます。
$ curl "http://localhost:8000/hello"
5. 引数を渡す
HTMLとお話ししてくれないとAPIもおもしろくないので、関数に引数を与えたいと思います。以下の様に、先ほどのhelloを修正して保存しなおします。
#* @get /hello
hw <- function(x="world") {
return(paste0("Hello ",x,"!"))
}
http://localhost:8000/hello にアクセスすると、先ほどと同じ結果が出力されますが、
http://localhost:8000/hello?x=Qiita としてアクセスすると、
["Hello Qiita!"]
と表示されるはずです。引数は「?x=」以降に追記すればいいということになります。ここで気になるのが、値として渡した「Qiita」がcharacterとして扱われているということですね。数値を渡したいときは、関数側で明示的にas.numeric()やas.integer()を行う必要があるということになります。
6. 数値計算の例
では、「do_xxx.R」に数値計算を行う関数を追記してみます。
#* @get /hello
hw <- function(x="world") {
return(paste0("Hello ",x,"!"))
}
#* @get /multiply
ml <- function(a=1,b=1) {
a <- as.numeric(a)
b <- as.numeric(b)
return(a*b)
}
引数として、a=1, b=3を与えると回答は[3]になるはずです。
http://localhost:8000/multiply?a=1&b=3
helloやmultiplyなど、アドレスを変えるだけで関数を選べるので便利ですね。
7. 画像の取得
画像の取得に関して説明します。Rで作成したグラフを画像として取得できるのも便利ですね。散布図を作成してみます。
#* @get /plot
#* @png
ggp <- function() {
p <- ggplot(data=iris, aes(x=Sepal.Length,
y=Sepal.Width,
colour=Species)) +
geom_point(size=3) +
scale_color_manual(values=c("#235273",
"#e1c564",
"#999999"))
print(p)
}
ここで、2行目に、#* @png
を指定しています。jpegでほしければ、#* @jpeg
を指定します。また、plotの場合はreturn()ではなく、print()を使用するというところが間違えそうなところです。
「do_xxx.R」を修正し、Rの方で以下の通り実行します。今回は{ggplot2}パッケージを利用しているので、それも読み込んでいます。
library(plumber)
library(ggplot2)
rapi <- plumb("do_xxx.R")
rapi$run(port=8000)
http://localhost:8000/plot にアクセスすると、pngファイルが表示されると思います。
8. 最後に
#* @png
とかのことを書いておきます。他にも以下の指定ができるようです。
Annotation | Content Type | Description/References |
---|---|---|
@json | application/json | jsonlite::toJSON() |
@html | text/html; charset=utf-8 | Passes response through without any additional serialization |
@jpeg | image/jpeg | jpeg() |
@png | image/png | png() |
@htmlwidget | text/html; charset=utf-8 | htmlwidgets::saveWidget() |
@unboxedJSON | application/json | jsonlite::toJSON(unboxed=TRUE) |
たとえば、
#* @get /jlist
#* @serializer unboxedJSON
jlist <- function() {
return(list(name="Qiita", color="Lime green"))
}
http://localhost:8000/jlist にアクセスすると、以下のような出力になるはずです。
{"name":"Qiita","color":"Lime green"}
参考
- 「plumber」 https://github.com/trestletech/plumber
- 「Creating APIs in R with Plumber」 https://www.rplumber.io/docs/index.html
- 「RでREST APIを作る(plumber編)」 http://uribo.hatenablog.com/entry/2016/03/12/213000
- 「RのコードをREST API化してNode-REDで叩いてみた」 http://qiita.com/junkonakajima/items/510b42bafdd6d987d16b
- 「REST入門 基礎知識」 http://qiita.com/TakahiRoyte/items/949f4e88caecb02119aa