
Contents
実行環境 Windows 7 + PostgreSQL 9.5.0 に PL/v8 をインストール PostGIS 2.2 のインストールは 9.5 RC1 の時と同様(以上、1 月 23 日) PNG ファイルをインポートして PostGIS ラスタのサンプルデータを作る インポートしたラスタを再び PNG ファイルに出力して確認(以上、1 月 24 日) PostGIS ラスタで画像処理、続けて PNG ファイルに出力するテンプレート PostGIS の関数のソースを見て、PL/v8 への置き換えを考える(以上、1 月 25 日) コールバック関数 ST_Range4ma の PL/v8 版を作る PL/pgSQL の単純化バージョンも作る SQL でもコールバック関数を作る テストの全体(以上、1 月 26 日) コールバック関数 4 つ × 周辺範囲 5 つ × 複数回テスト、を一つのクエリで テスト結果の TSV を PostgreSQL にインポート PL/v8 の方が、元からある PL/pgSQL の数倍速い(以上、1 月 28 日) - テスト結果の
PostgreSQL テーブルを R で読み込む - 今回のテストと外れ値、95%
信頼区間について - 全体を一つの棒グラフにまとめる(エラーバー付)
- ピクセル範囲から所要時間を予測する回帰式(関数別)
- 全体のまとめ
テスト結果の PostgreSQL テーブルを R で読み込む
数日空いてしまいましたが、前回as.matrix(R.Version()) [,1] platform "x86_64-w64-mingw32" arch "x86_64" os "mingw32" system "x86_64, mingw32" status "" major "3" minor "2.2" year "2015" month "08" day "14" svn rev "69053" language "R" version.string "R version 3.2.2 (2015-08-14)" nickname "Fire Safety"

↓ パッケージ
library(RPostgreSQL) con = dbConnect(PostgreSQL(), host='localhost', port=5432, dbname='test_postgis22', user='postgres', password='****') # to close # dbDisconnect(con)
dbGetQuery
↓
(dat = dbGetQuery(con, " select pixel_range, func_pre, avg(sec), stddev(sec) / sqrt(count(*)) * 2.776445 -- qt(0.975, 5-1) on R as err from result_range4ma group by 1, 2 order by 1 desc, 2 desc ")) pixel_range func_pre avg err 1 4 st 92.361922 11.49552451 2 4 sql 28.644770 0.16963925 3 4 simple 71.117605 0.16328329 4 4 plv8 14.807546 0.07551800 5 3 st 57.345901 0.46703252 6 3 sql 20.567276 0.24028493 7 3 simple 46.195001 0.24517174 8 3 plv8 10.111938 0.09228771 9 2 st 32.750697 0.10027578 10 2 sql 14.402145 0.18311965 11 2 simple 25.933485 0.26790897 12 2 plv8 6.430331 0.07915714 13 1 st 16.810590 0.55494263 14 1 sql 10.171218 0.07117002 15 1 simple 12.832783 0.06684748 16 1 plv8 3.859446 0.02598734 17 0 st 7.497573 0.09384754 18 0 sql 7.531693 0.12204661 19 0 simple 5.419449 0.03518697 20 0 plv8 2.218324 0.02873016

今回のテストと外れ値、95% 信頼区間について
今回、各ケース(関数×ピクセル範囲)につきその
もう一つ困ったのは、このように「ある
以上をきちんと書くと長くなるので別の機会とし、とりあえず今回は「外れ値」扱いなし、t
全体を一つの棒グラフにまとめる(エラーバー付)
以下、R(ary = array(c(dat$avg, dat$err), c(4,5,2), dimnames = list( unique(dat$func_pre), unique(dat$pixel_range), c('avg', 'err') ))) , , avg 4 3 2 1 0 st 92.36192 57.34590 32.750697 16.810590 7.497573 sql 28.64477 20.56728 14.402145 10.171218 7.531693 simple 71.11761 46.19500 25.933485 12.832783 5.419449 plv8 14.80755 10.11194 6.430331 3.859446 2.218324 , , err 4 3 2 1 0 st 11.4955245 0.46703252 0.10027578 0.55494263 0.09384754 sql 0.1696393 0.24028493 0.18311965 0.07117002 0.12204661 simple 0.1632833 0.24517174 0.26790897 0.06684748 0.03518697 plv8 0.0755180 0.09228771 0.07915714 0.02598734 0.02873016

もしエラーバー不要なら、ピクセル範囲でグループ化した棒グラフは
barplot(ary[,,1], beside=T, horiz=T)

これにエラーバーを足し、ラベルとか目盛りとか色々整えようとすると途端に面倒なのが
avg = ary[,,'avg'] cols = adjustcolor(terrain.colors(4), alpha=0.5) xlim = c(0, max(apply(ary, 1:2, sum))) yloc = barplot(avg, beside=T, plot=F) ylim = range(yloc) + c(-1, 1) * 1.5 # set plot area par(lend=1, mfrow=c(1,1), plt=c(12, 98, 15, 98)/100) plot(axes=F, type='n', x=0, xlim=xlim, ylim=ylim, xaxs='i', yaxs='i', xlab='', ylab='') axs = axis(1) usr = par('usr') # plot barplot(add=T, axes=F, beside=T, col=cols, height=avg, horiz=T, las=1, names.arg=paste(dimnames(ary)[[2]], 'pixel\nrange'), xlim=xlim, ylim=ylim) for (r in seq(nrow(ary))) { for (c in seq(ncol(ary))) { tmp = ary[r,c,] arrows(angle=90, code=3, length=0, lwd=5, x0=tmp[1] + tmp[2], x1=tmp[1] - tmp[2], y0=yloc[r, c]) } } mtext(1, text='elapsed time (sec)', line=2.5) segments(x0=axs, y0=usr[3], y1=usr[4], lty=c(1, rep(2, length(axs) - 1))) # mask for legend rect(mean(usr[1:2]), mean(usr[3:4]), usr[2], usr[4], border=NA, col='white') # legned nr = nrow(ary) leg = c(rev(dimnames(ary)[[1]]), NA, 'the 95% confidence interval') leg = sub('^(simple)', '\\1 (modified pl/pgsql)', leg) leg = sub('^(st)', '\\1 (built-in pl/pgsql)', leg) legend( adj=c(1, 1/2), border=c(rep(1, nr+1), NA), bty='n', legend=leg, fill=c(rev(cols), NA, NA), lwd=c(rep(NA, nr+1), 5), x=xlim[1] + diff(xlim) * 0.85, x.intersp=c(rep(-3.5, nr), NA, -2.7), y=usr[4] )

ピクセル範囲が狭い(所要時間が少ない)部分でエラーバーがほとんど見えないので、ピクセル範囲別に五つのグラフを作って並べると ↓ こんな感じ。凡例は省略しました。これでもやっぱり、信頼区間が狭いケースだとエラーバーがほとんど
par(lend=1, mfrow=c(2,3), cex=1, plt=c(15, 96, 22, 99)/100) for (i in rev(colnames(ary))) { mat = ary[,i,] bar = barplot(mat[,1], horiz=T, col=cols, xlim=c(0, max(rowSums(mat)))) apply(cbind(mat, bar), 1, function(d) { arrows(angle=90, code=3, length=0, lwd=5, x0=d[1]-d[2], x1=d[1]+d[2], y0=d[3]) }) mtext(1, text=paste(i, 'pixel range, elapsed time (sec)'), line=2.5) }

ピクセル範囲から所要時間を予測する回帰式(関数別)
絶対的な所要時間の傾向は分かったので、もう少し分析的に、関数別の回帰式(説明変数(dat2 = dbGetQuery(con, " select pixel_range, func_pre, sec from result_range4ma order by 1, 2 ")) pixel_range func_pre sec 1 0 plv8 2.215204 2 0 plv8 2.215203 3 0 plv8 2.246404 4 0 plv8 2.184004 5 0 plv8 2.230803 6 0 simple 5.444409 7 0 simple 5.397609 8 0 simple 5.428810 9 0 simple 5.382010 10 0 simple 5.444409 11 0 sql 7.425613 12 0 sql 7.690814 13 0 sql 7.488013 (...)

↓ まず線型(1
par(lend=1, mfrow=c(2,2), cex=1, plt=c(18, 98, 20, 98)/100) options(show.signif.stars=F) ylm = c(0, max(dat2$sec)) for(f in unique(dat2$func_pre)) { d = subset(dat2, func_pre==f) attach(d, warn.conflicts=F) pxr = pixel_range lm = lm(sec ~ pxr) smry = summary(lm) str = paste(collapse='\n', c( capture.output(smry)[9:12], paste('r.squared', round(smry$r.squared, 4)))) message('-----\n', f, '\n', str) plot(pxr, sec, xlab='pixel_range', ylim=ylm) abline(lm, col='red') text(adj=0, cex=2, lab=f, x=0, y=102.5) text(adj=0, cex=0.8, x=0, y=80, lab=str) } ----- plv8 Coefficients: Estimate Std. Error t value Pr(>|t|) (Intercept) 1.1993 0.3122 3.842 0.000833 pxr 3.1431 0.1275 24.660 < 2e-16 r.squared 0.9636 ----- simple Coefficients: Estimate Std. Error t value Pr(>|t|) (Intercept) -0.6520 1.8237 -0.358 0.724 pxr 16.4759 0.7445 22.129 <2e-16 r.squared 0.9551 ----- sql Coefficients: Estimate Std. Error t value Pr(>|t|) (Intercept) 5.7390 0.5550 10.34 4.04e-10 pxr 5.2622 0.2266 23.23 < 2e-16 r.squared 0.9591 ----- st Coefficients: Estimate Std. Error t value Pr(>|t|) (Intercept) -0.6995 2.9250 -0.239 0.813 pxr 21.0264 1.1941 17.608 7.61e-15 r.squared 0.9309

回帰式の当てはまりは悪くないけど、プロットされた実測値を見ると非線型の方が妥当な気がします。方法が色々ある中、今回は簡単に説明変数(ピクセル範囲)を
for(f in unique(dat2$func_pre)) { d = subset(dat2, func_pre==f) attach(d, warn.conflicts=F) pxr = pixel_range lm = lm(sec ~ I(pxr^2)) smry = summary(lm) str = paste(collapse='\n', c( capture.output(smry)[9:12], paste('r.squared', round(smry$r.squared, 4)))) message('-----\n', f, '\n', str) plot(pxr, sec, xlab='pixel_range', ylim=ylm) usr = par('usr') coef = smry$coefficients func = function(x) { coef[1,1] + x ^ 2 * coef[2,1] } curve(add=T, col='red', expr=func, from=usr[1], to=usr[2]) text(adj=0, cex=2, lab=f, x=0, y=102.5) text(adj=0, cex=0.8, x=0, y=80, lab=str) } ----- plv8 Coefficients: Estimate Std. Error t value Pr(>|t|) (Intercept) 2.90126 0.12979 22.35 <2e-16 I(pxr^2) 0.76404 0.01543 49.53 <2e-16 r.squared 0.9907 ----- simple Coefficients: Estimate Std. Error t value Pr(>|t|) (Intercept) 8.11989 0.53482 15.18 1.77e-13 I(pxr^2) 4.02996 0.06356 63.40 < 2e-16 r.squared 0.9943 ----- sql Coefficients: Estimate Std. Error t value Pr(>|t|) (Intercept) 8.56346 0.19504 43.91 <2e-16 I(pxr^2) 1.28333 0.02318 55.37 <2e-16 r.squared 0.9926 ----- st Coefficients: Estimate Std. Error t value Pr(>|t|) (Intercept) 10.2803 1.2068 8.519 1.44e-08 I(pxr^2) 5.1788 0.1434 36.109 < 2e-16 r.squared 0.9827

処理するピクセル数がピクセル範囲の
全体のまとめ
1- Windows + PostgreSQL 9.5 + PostGIS 2.2 + PL/v8
の実行環境が整った - PostGIS
ラスタのコールバック関数を PL/v8 で作ってみた - PL/v8
の配列処理(Typed Array でなく普通の配列)は、PL/pgSQL より数倍速い - PL/v8
より劣るが、SQL で unnest + 集約関数を使うと PL/pgSQL の配列処理より速い - いずれにしても
1 ピクセルごとに関数を呼び出すので、どの言語でも実用的な画像処理には遠い… - PostGIS
ラスタを直接 PL/v8 に渡すとか、何か新機軸を考えたい! - テスト中、1
回だけ変に時間かかり過ぎる時あったが、外れ値として除外するのは意外に難しい
こんな感じかなぁ。最後の点はいずれ別の記事にします。