Rをはじめよう 生命科学のためのRStudio入門のデータセット(ここにある)を使って、線形回帰をやるJuliaコード。データを読み込み、図で結論を予想し、モデルの健全性を診断し、検定し、図を仕上げる。コードや出力の説明などは技術書典6(2019/04/14)で頒布予定。
plant.jl
using CSV, Gadfly, DataFrames, DataFramesMeta, Statistics, HypothesisTests,
Cairo, Fontconfig, GLM, Distributions, Distances, LinearAlgebra
# データ読み込み
dat = CSV.read("datasets/plant.growth.rate.csv", allowmissing=:none)
# カラム名に . が入ってるとあとでうまく扱えないので、名前を変える
names!(dat, [:moisture, :growth]);
# データは要するに二群、各群でのオゾン濃度の平均値と分散
describe(dat)
var(dat[:moisture])
var(dat[:growth])
draw(PDF("fig/plant_1.pdf", 10cm, 8cm),
hstack(plot(dat, y = :moisture, Geom.boxplot),
plot(dat, y = :growth, Geom.boxplot)))
draw(PDF("fig/plant_2.pdf", 10cm, 8cm),
plot(dat, x = :moisture, y = :growth, Geom.point))
# 線形回帰して、モデルの値(期待値)を計算し、診断プロットを描く
model = lm(@formula(growth ~ moisture), dat)
pred = predict(model)
# 診断プロット1:Residuals vs Fitted
res = dat[:growth] - pred
draw(PDF("fig/plant_3.pdf", 10cm, 8cm),
plot(x = pred, y = res, Geom.point,
Guide.title("Residuals vs Fitted"),
Guide.xlabel("Fitted values"), Guide.ylabel("Residuals")))
# 診断プロット2:Normal Q-Q plot
N = length(res)
stdRes = (res .- mean(res) .* ones(N)) ./ (std(res)) # 標準化残差
qx = quantile.(Normal(), range(0.5, stop=(N-0.5), length=(N)) / (N + 1))
draw(PDF("fig/plant_4.pdf", 10cm, 8cm),
plot(layer(x = qx, y = sort(stdRes), Geom.point),
layer(x = [-3,3], y = [-3,3], Geom.line, style(line_style = [:dot])),
Guide.title("Normal Q-Q plot"),
Guide.xlabel("Theoretical Quantiles"),
Guide.ylabel("Standardized residuals")))
# 診断プロット3:Scale-Location
sqrtStdRes = sqrt.(abs.(stdRes))
draw(PDF("fig/plant_5.pdf", 10cm, 8cm),
plot(x = pred, y = sqrtStdRes, Geom.point,
Guide.title("Scale-Location"),
Guide.xlabel("Fitted values"),
Guide.ylabel("|Standardized residuals|<sup>1/2</sup>")))
# 診断プロット4:Residuals vs Leverage
x = dat[:moisture]
Sxx = sum(x .* x) - sum(x)^2/N
m = mean(x)
leverage = ones(length(res))
for i in 1:N leverage[i] = (x[i] - m)^2 / Sxx + 1/N end
draw(PDF("fig/plant_6.pdf", 10cm, 8cm),
plot(x = leverage, y = stdRes, Geom.point,
Guide.xlabel("leverage"), Guide.ylabel("stdRes")))
# で、データとモデルと信頼区間のプロット
# まずx軸の範囲を決める、データの範囲を探して、50点に区切る
# データフレームに入れて、回帰時の変数名と同じ変数名を付けないと、
# predict が受け取ってくれない
xmin = describe(dat)[1, :min]
xmax = describe(dat)[1, :max]
newx = DataFrame([range(xmin, stop = xmax, length = 50)])
names!(newx, [:moisture])
# で予測値を計算して、結果のデータフレームにxの値を挿入
pred = predict(model, newx, interval=:confidence)
insertcols!(pred, 1, moisture=newx[:moisture])
# でデータと予測直線をプロット
# 先に置いた Geom.point が Geom.line の上に描画される
# ymin と ymax は Geom.ribbon が使う
draw(PDF("fig/plant_7.pdf", 10cm, 8cm),
plot(layer(dat, x = :moisture, y = :growth, Geom.point),
layer(pred,x = :moisture, y = :prediction,
ymin = :lower, ymax = :upper,
Geom.line, Geom.ribbon),
Coord.cartesian(xmin=0.2, xmax=2.0),
Guide.title("土壌水分量と生育速度"),
Guide.xlabel("土壌水分量"),
Guide.ylabel("生育速度")))