わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

Node.jsで項目反応曲線のモデルを描く

 この件のDocker化を試みました.
 研究室の4年生に,公開されているRのベースイメージを調べてもらったところ,rocker/tidyverserocker/rstudioが利用できたというメールが届きました.手元でpullしてみると,イメージは2GB前後になりました.
 Rの公式Dockerイメージを探すと,r-baseでした.pullしたら,811MBでした.
 ところで「研究室」というのは,昨年度に開発したWebアプリケーションに機能拡張を行うことを予定しておりまして,項目反応理論に基づく分析(項目反応パターンから,困難度・識別力などを推定すること)や項目特性曲線その他の描画機能を取り入れたいと,考えたのでした.
 昨年度の分は,Docker Composeでビルドや起動・停止を行っており,PostgreSQLのコンテナと,Node.js(node:16-alpine)をベースイメージにして自作のjsファイルにより起動するWebサーバのコンテナで構成されています.
 Rの処理の追加方法として,次の2種類が考えられます.Rに関するコンテナを追加してWebサーバのコンテナと通信する方法と,WebサーバのイメージにRと項目反応理論のパッケージを追加し必要なときに起動する方法です.
 後者を少し掘り下げてみました.nodeを見るとペースイメージはnode:21-alpineが良さそうです.パッケージの追加などはapkコマンドです.こまごました調査や,ビルド・実行・ファイル更新を行いながら,動作するためのファイル一式を作りました.ファイルは全部で4つ(Dockerfile, install-irtoys.R, icc.js, icc.R)です.各ファイルの最初の行に,コメントとしてファイル名を記載しています.

# Dockerfile
FROM node:21-alpine AS builder
WORKDIR /w
RUN apk update && \
    apk add make gcc g++ R R-dev
COPY install-itroys.R /w/
RUN Rscript install-itroys.R

FROM node:21-alpine
WORKDIR /w
RUN apk update && \
    apk add R
COPY --from=builder /usr/lib/R/library /usr/lib/R/library
CMD ["node", "--version"]
// install-itroys.R
install.packages("irtoys", repos="https://cloud.r-project.org", dependencies=TRUE)
// icc.js
const { execSync } = require('child_process')
const stdout = execSync('Rscript icc.R')
console.log(`stdout: ${stdout.toString()}`)
# icc.R
library(irtoys)
est <- array(c(1,1,1,1,1,0,-2,-1,1,2,0,0,0,0,0),dim=c(5,3))
icc <- irf(est)
pdf("icc.pdf", width=6, height=5)
plot(icc, label=TRUE, co=NA)
dev.off()

 上記4つのファイルを1つのディレクトリに置いてから,端末でこのディレクトリに移動します.ビルドとコンテナ起動はそれぞれ以下のコマンドにします.

docker image build -t noder .
docker container run --name=noder --rm --mount type=bind,source=$(pwd),target=/w/ noder node icc.js

 「--mount type=bind,source=$(pwd),target=/w/」はボリュームマウントのオプションで,カレントディレクトリを,Dockerコンテナの/wのディレクトリにマウントします.「$(pwd)」とあるとおり,端末はLinuxやWSL2などのbashを想定しています.PowerShellでは,うまくいきませんので,カレントディレクトリを変数に格納してコンテナ起動時に参照することにします(ビルドは変更ありません).1年前の研究室内のメモによると,以下のように実行します.

$pwd_as_linux = "/$((pwd).Drive.Name.ToLowerInvariant())/$((pwd).Path.Replace('\', '/').Substring(3))"
docker container run --name=noder --rm --mount type=bind,source=${pwd_as_linux},target=/w/ noder node icc.js

 正常に実行を終えると,(--rmオプションのため)コンテナはすぐに消滅し,ボリュームマウントの効果で,カレントディレクトリにicc.pdfが作られます.冒頭のリンク先の最初の画像と同じく,5色の曲線が描かれています.
 noderイメージのサイズは,なんと,347MBです.苦労した甲斐がありました.

 Dockerfileについて少し説明しておきます.マルチステージビルドを使用しており,builderについては,irtoysのインストールを行っています(通信時間は短いですが,ビルドに時間がかかります).この作業に,make・gcc・g++・R-devのパッケージも必要となります.irtoysのインストールが無事に終わると,/usr/lib/R/library以下にファイルが作られます.そこでbuilderの作業はここで終え,使用するためのイメージではこのディレクトリ以降をコピーします.apkによるインストールは,Rだけです.完成にあたり,以下のページを参考にしました.