æšå¹Žã®PG Day'15 Russiaã«ã³ãã¡ã¬ã³ã¹ã§ãç§ãã¡ã®ã¹ããŒã«ãŒã®1人ã§ãããžã§ã»ãã³ã³ãŠã§ã€ãã10幎以äžã«ããã£ãŠåœŒãäœæããã³ãµããŒãããPL / Ræ¡åŒµæ©èœã®äœ¿çšã«é¢ããè峿·±ãè³æã玹ä»ããŸãããããŒã¿ããŒã¹ Joeã®ã¬ããŒãã«ç€ºãããŠããè³æã«åºã¥ããŠäœæããããã©ããŒã¢ããèšäºã«æ³šç®ããããšæããŸãã ãã®åºçç©ã®ç®çã¯ãPL / Rèšèªã®å¯èœæ§ãç¥ãããšã§ãã ããã«èšèŒãããŠããæ
å ±ãã圹ã«ç«ãŠã°å¹žãã§ãã
æè¿ã®ããã°ããŒã¿ã®åŸåã«ãããåæãšããŒã¿ã®åæãä¿é²ãããŠããŸãããPL / Rã¯ãã®ãããªãµãŒãã¹ãæ§ããã«12幎éæäŸããŠããŸãïŒ çªç¶ããªããç¥ããªãå ŽåãPL / Rã¯PostgreSQLã®æ¡åŒµæ©èœã§ãããç°¡åãã€è©³çްãªåæãç°¡åã«ååŸããããã«ãPostgreSQLããçŽæ¥Rãæ°åŠèšç®ã®èšèªãšããŠäœ¿çšã§ããŸãã æ¡åŒµæ©èœã¯å©çšå¯èœã§ããã2003幎以éç©æ¥µçã«æ¹åãããŠããŸãã ãµããŒããããŠãããã¹ãŠã®ããŒãžã§ã³ã®PostgreSQLããã³ãã¹ãŠã®æè¿ã®ããŒãžã§ã³ã®Rã§åäœããŸããäžçäžã®äœå人ãã®äººã
ãããã®äŸ¿å©ããšæå¹æ§ããã§ã«è©äŸ¡ããŠããŸãã PL / Rãšã¯äœããèŠãŠãããŒã¿åæã«å¯Ÿãããã®ã¢ãããŒãã®é·æãšçæã説æãã説æã®ããã«ããã€ãã®äŸãèããŠã¿ãŸãããã
ãŸããåºæ¬çãªæŠå¿µãå®çŸ©ããŸãããã Rã¯ãçµ±èšããŒã¿åŠçãšã°ã©ãã£ãã¯ã¹ãããã³ç¡æã®ãªãŒãã³ãœãŒã¹ãœãããŠã§ã¢ç°å¢ã®ããã®ããã°ã©ãã³ã°èšèªã§ãã ãŸããPostgreSQLã¯åŒ·åã§ç¡æã®ãªããžã§ã¯ããªã¬ãŒã·ã§ãã«ããŒã¿ããŒã¹ç®¡çã·ã¹ãã ã§ããã25幎以äžã«ããã£ãŠç©æ¥µçã«éçºãããŠããããã®ä¿¡é Œæ§ãšããŒã¿ã®æ£ç¢ºæ§ããã³æŽåæ§ã«ããé«ãè©äŸ¡ãåŸãŠããŸãã ãããŠæåŸã«ãPL / R åè¿°ã®ããã«ãããã¯PostgreSQLçšã®æç¶ãåRèšèªãã³ãã©ã§ãããRã§SQL颿°ãèšè¿°ã§ããŸãã
PL / Rã®å©ç¹ã¯äœã§ããïŒ ãŸããæ°åŠãçµ±èšãããŒã¿ããŒã¹ãããã³Webã¯ããããç°ãªãå°éåéã§ãããããPL / Rã¯äººéã®ç¥èãšã¹ãã«ã®éçºã«è²¢ç®ããŸãããPL / Rã䜿çšããã«ã¯ããçšåºŠãã¹ãŠç¿åŸããå¿
èŠããããŸãã 第äºã«ããã®æ¡åŒµæ©èœã¯ããŒããŠã§ã¢ã®æ¹åãä¿é²ããŸããå€§èŠæš¡ãªããŒã¿ã»ããã®åæã«èãããããµãŒããŒãå¿
èŠã ããã§ãã 第äžã«ããããã¯ãŒã¯å
šäœã«å€§ããªããŒã¿ã»ããã転éããå¿
èŠããªããªããã¹ã«ãŒããããåäžãããããåŠçå¹çãåäžããŸãã 第4ã«ãåæã確å®ã«é 次å®è¡ãããããšã確èªã§ããŸãã 第5ã«ãPL / Rã¯è€éãªã·ã¹ãã ãçè§£ããããä¿å®ããããããŸãã æåŸã«ãè±å¯ãªæ©èœãšå·šå€§ãªãšã³ã·ã¹ãã ã«ãããPL / Rã¯Rãæ¡åŒµããŸãã
ãããããã¡ãããæ¬ ç¹ããããŸãã PostgreSQLãŠãŒã¶ãŒã¯ãç¹ã«åçŽãªã¿ã¹ã¯ã§ã¯PL / Rãé
ãããšã«æ°ä»ãã§ãããã ãŸããRããã°ã©ããŒã«ãšã£ãŠã¯ããããã°ããã»ã¹ãããè€éã«ãªããåæã®æè»æ§ãäœäžããŸãã ããã«ãäž¡æ¹ãæ°ããèšèªãåŠã¶å¿
èŠããããŸãã
Rã®æšæºé¢æ°ã¯æ¬¡ã®ããã«ãªããŸãã
func_name <- function(myarg1 [,myarg2...]) { function body referencing myarg1 [, myarg2 ...] }
PL / Rã§ã®é¢æ°ã®äœæã¯å°ãç°ãªããŸãããPostgreSQLã®ä»ã®PLãšäŒŒãŠããŸãã
CREATE OR REPLACE FUNCTION func_name(arg-type1 [, arg-type2 ...]) RETURNS return-type AS $$ function body referencing arg1 [, arg2 ...] $$ LANGUAGE 'plr'; CREATE OR REPLACE FUNCTION func_name(myarg1 arg-type1 [, myarg2 arg-type2 ...]) RETURNS return-type AS $$ function body referencing myarg1 [, myarg2 ...] $$ LANGUAGE 'plr';
以äžã«äŸã瀺ããŸãã
CREATE EXTENSION plr; CREATE OR REPLACE FUNCTION test_dtup(OUT f1 text, OUT f2 int) RETURNS SETOF record AS $$ data.frame(letters[1:3],1:3) $$ LANGUAGE 'plr'; SELECT * FROM test_dtup(); f1 | f2
PL / Rã§ã§ããããšããã¹ãŠãªã¹ãããããšã¯å°é£ã§ãããæ¬¡ã®ãããªããã€ãã®éèŠãªããããã£ã詳ããèŠãŠã¿ãŸãããã
- PostgreSQLãšã®äºææ§ã
- ã«ã¹ã¿ã SQLéçŽã
- ãŠã£ã³ããŠé¢æ°;
- Rãªããžã§ã¯ããbyteaïŒãã€ããªæååïŒã«å€æããŸãã
PostgreSQLãšã®äºææ§ã®ãããã§ãRã䜿çšããŠãããã¿ã€ããäœæããPL / Rã«ç§»åããŠå®çšŒåã§å®è¡ã§ããŸãã ãŸããæç¢ºãªãã©ã¹ã¯ããã¹ãŠã®ã¯ãšãªãçŸåšã®ããŒã¿ããŒã¹ã§å®è¡ãããããšã§ãã ãã©ã€ããŒãšæ¥ç¶ã®èšå®ã¯ç¡èŠããããããdbDriverãdbConnectãdbDisconnectãdbUnloadDriverã¯è¿œå ã®äœæ¥ãå¿
èŠãšããŸããã
dbDriver(character dvr_name) dbConnect(DBIDriver drv, character user, character password, character host, character dbname, character port, character tty, character options) dbSendQuery(DBIConnection conn, character sql) fetch(DBIResult rs, integer num_rows) dbClearResult (DBIResult rs) dbGetQuery(DBIConnection conn, character sql) dbReadTable(DBIConnection conn, character name) dbDisconnect(DBIConnection conn) dbUnloadDriver(DBIDriver drv)
説æã®ããã«ãããã€ãã®äŸãæããŸãããã æåãªå·¡åã»ãŒã«ã¹ãã³ã®åé¡ã解決ããããã«RããPostgreSQLã䜿çšããå¿
èŠããããšããŸããããã«ã€ããŠã¯åŸã§è©³ãã説æããŸãã
tsp_tour_length<-function() { require(TSP) require(fields) require(RPostgreSQL) drv <- dbDriver("PostgreSQL") conn <- dbConnect(drv, user="postgres", dbname="plr", host="localhost") sql.str <- "select id, st_x(location) as x, st_y(location) as y, location from stands" waypts <- dbGetQuery(conn, sql.str) dist.matrix <- rdist.earth(waypts[,2:3], R=3949.0) rtsp <- TSP(dist.matrix) soln <- solve_TSP(rtsp) dbDisconnect(conn) dbUnloadDriver(drv) return(attributes(soln)$tour_length) }
ãããŠãããã«PL / Rã®åã颿°ããããŸãïŒ
CREATE OR REPLACE FUNCTION tsp_tour_length() RETURNS float8 AS $$ require(TSP) require(fields) require(RPostgreSQL) drv <- dbDriver("PostgreSQL") conn <- dbConnect(drv, user="postgres", dbname="plr", host="localhost") sql.str <- "select id, st_x(location) as x, st_y(location) as y, location from stands" waypts <- dbGetQuery(conn, sql.str) dist.matrix <- rdist.earth(waypts[,2:3], R=3949.0) rtsp <- TSP(dist.matrix) soln <- solve_TSP(rtsp) dbDisconnect(conn) dbUnloadDriver(drv) return(attributes(soln)$tour_length) $$ LANGUAGE 'plr' STRICT;
Rãæçµçã«æäŸãããã®ã次ã«ç€ºããŸãã
tsp_tour_length() [1] 2804.581
ãããŠãPL / Rã®åãæ©èœïŒ
SELECT tsp_tour_length(); tsp_tour_length
ã芧ã®ãšãããçµæã¯åãã§ãã
次ã«ãéèšã«ã€ããŠèª¬æããŸãã PostgreSQLã®æã䟿å©ãªæ©èœã®1ã€ã¯ãç¬èªã®éèšé¢æ°ãäœæã§ããããšã§ãã PostgreSQLã®éèšã¯ãSQLã³ãã³ãã䜿çšããŠå±éã§ããŸãã ãã®å Žåãç¶æ
é·ç§»é¢æ°ãšãå Žåã«ãã£ãŠã¯æçµé¢æ°ã瀺ãããŸãã é·ç§»é¢æ°ã®åææ¡ä»¶ãæå®ã§ããŸãã ãŸããPL / Rã䜿çšããŠãããã®ã¡ãªããã享åã§ããŸãã
以äžã¯ãæ°ããéçŽãå®è£
ããPL / R颿°ã®äŸã§ãã æè¿ãŸã§ãPostgreSQLãããããè¡ãããšã¯ã§ããŸããã§ããããGROUPING SETSæ©èœã¯ããŒãžã§ã³9.5ã§ã®ã¿ç»å ŽããŸããããPL / Rã§ã¯ã©ã®ããŒãžã§ã³ã®PGã§ããããè¡ãããšãã§ããŸãã ããçç£äŒç€Ÿã®å®éã®ããŒã¿ã«åºã¥ããŠéçŽé¢æ°ãäœæããååäœæ°ãšåŒã³ãŸãã
CREATE OR REPLACE FUNCTION r_quartile(ANYARRAY) RETURNS ANYARRAY AS $$ quantile(arg1, probs = seq(0, 1, 0.25), names = FALSE) $$ LANGUAGE 'plr'; CREATE AGGREGATE quartile (ANYELEMENT) ( sfunc = array_append, stype = ANYARRAY, finalfunc = r_quantile, initcond = '{}' ); SELECT workstation, quartile(id_val) FROM sample_numeric_data WHERE ia_id = 'G121XB8A' GROUP BY workstation; workstation | quantile
Rã䜿çšãããšãããŒã¿ãçŽ æµãªã°ã©ãã®åœ¢åŒã§è¡šç€ºã§ããŸãã ãã®å Žåãããã¯ã¹ãã€ã¢ã°ã©ã ã䜿çšãããŸããã

ã°ã©ãã¯ãçç£ã¹ããŒã·ã§ã³ã®1ã€ãä»ã®çç£ã¹ããŒã·ã§ã³ãããçç£æ§ãäœãããšã瀺ããŠããããããä¿®æ£ããããã®å¯Ÿçãå¿
èŠã§ãã
ãããšã¯å¥ã«ã
ãŠã£ã³ããŠé¢æ°ã§åæ¢ãã䟡å€ã
ãããŸã ã ããŒãžã§ã³8.4以éãPostgreSQLã§å©çšå¯èœã§ãããçµ±èšåæã«æé©ã§ãã ãŠã£ã³ããŠé¢æ°ã¯éèšé¢æ°ãšäŒŒãŠããŸãããããããšã¯ç°ãªããæååãã°ã«ãŒãåããŸããããã¯ãšãªçµæã®çŸåšã®è¡ã ãã§ãªããçŸåšã®è¡ã«é¢é£ä»ããããæååã®ã»ããã§èšç®ã§ããŸãã ã€ãŸããããŒã¿ã¯ã»ã¯ã·ã§ã³ã«åå²ããããããã®ã»ã¯ã·ã§ã³ãã¹ã©ã€ãããŠåããŒã¿ã°ã«ãŒãã®çµæãçæãããŠã£ã³ããŠããããŸãã

PostgreSQLã¯ãŠã£ã³ããŠé¢æ°ãã»ãšãã©ãµããŒãããŠããŸããããPL / Rã§ã¯éåžžã«äŸ¿å©ã§ãã äŸã¯æ¬¡ã®ãšããã§ããåçãšæ ªäŸ¡ãã·ãã¥ã¬ãŒãããã©ã³ãã ããŒã¿ããŒãã«ãäœæããŸãã
CREATE TABLE test_data ( fyear integer, firm float8, eps float8 ); INSERT INTO test_data SELECT (bf + 1) % 10 + 2000 AS fyear, floor((b.f+1)/10) + 50 AS firm, f::float8/100 + random()/10 AS eps FROM generate_series(-500,499,1) b(f); CREATE OR REPLACE FUNCTION r_regr_slope(float8, float8) RETURNS float8 AS $BODY$ slope <- NA y <- farg1 x <- farg2 if (fnumrows==9) try (slope <- lm(y ~ x)$coefficients[2]) return(slope) $BODY$ LANGUAGE plr WINDOW;
ãããŠãåçŽãªååž°æ³ã䜿çšããŠãæšå¹Žã®ææšã«åºã¥ããŠãä»å¹Žã®åçãäºæž¬ã§ãããã©ããã調ã¹ãããã®é¢æ°ãäœæããŸãã
SELECT *, r_regr_slope(eps, lag_eps) OVER w AS slope_R FROM ( SELECT firm AS f, fyear AS fyr, eps, lag(eps) OVER (PARTITION BY firm ORDER BY firm, fyear) AS lag_eps FROM test_data ) AS a WHERE eps IS NOT NULL WINDOW w AS (PARTITION BY firm ORDER BY firm, fyear ROWS 8 PRECEDING); f | fyr | eps | lag_eps | slope_r
ã芧ã®ãšããããã®é¢æ°ã¯åå
¥ã®åå¹Žã®ææšãžã®äŸå床ãèšç®ããŸããã
æåŸã«è©³ãã説æããã®ã¯ãRãªããžã§ã¯ããè¿ãããã®ã¡ã«ããºã ãšãããããä¿åããæ¹æ³ã§ãã
åšåº«ããŒã¿ã®äŸã瀺ããŸãã ãããè¡ãã«ã¯ãããã£ãã«ãŒãïŒæ ªäŸ¡è¡šç€ºïŒã®YahooããHi-Low-CloseããŒã¿ãååŸããŸãã ããªã³ãžã£ãŒã®ç·ãšããªã¥ãŒã ã§ãã£ãŒããäœæããŸãããã æ¬¡ã®ã³ãã³ãã䜿çšããŠRããååŸã§ãã远å ã®Rãã±ãããå¿
èŠã§ãã
install.packages(c('xts','Defaults','quantmod','cairoDevice','RGtk2'))
ãªã¯ãšã¹ããè¡ããŸãïŒ
CREATE OR REPLACE FUNCTION plot_stock_data(sym text) RETURNS bytea AS $$ library(quantmod) library(cairoDevice) library(RGtk2) pixmap <- gdkPixmapNew(w=500, h=500, depth=24) asCairoDevice(pixmap) getSymbols(c(sym)) chartSeries(get(sym), name=sym, theme="white", TA="addVo();addBBands();addCCI()") plot_pixbuf <- gdkPixbufGetFromDrawable(NULL, pixmap, pixmap$getColormap(),0, 0, 0, 0, 500, 500) buffer <- gdkPixbufSaveToBufferv(plot_pixbuf, "jpeg", character(0), character(0))$buffer return(buffer) $$ LANGUAGE plr;
äžè¬çãªãµãŒããŒã§ã¯ãã°ã©ããäœæããããã«ç»é¢ãããã¡ãŒãå¿
èŠã§ãã
Xvfb :1 -screen 0 1024x768x24 export DISPLAY=:1.0
CYMIãã£ãã«ãŒã®é¢æ°ãPHPããåŒã³åºããŸãã
<?php $dbconn = pg_connect("..."); $rs = pg_query($dbconn, "select plr_get_raw(plot_stock_data('CYMI'))"); $hexpic = pg_fetch_array($rs); $cleandata = pg_unescape_bytea($hexpic[0]); header("Content-Type: image/png"); header("Last-Modified: " . date("r", filectime($_SERVER['SCRIPT_FILENAME']))); header("Content-Length: " . strlen($cleandata)); echo $cleandata; ?>
ãããŠãããã§åºåã§ãã®ãããªã°ã©ããååŸããŸãïŒ

åæããŸããPL/ Rã§æ°åè¡ãPHPã§æ°åè¡æªæºãèšè¿°ã§ããããšã¯é©ãã¹ãããšã§ãããã®çµæãããŒã¿ã®è©³çްãªèŠèŠè¡šç€ºãšåæãå¯èœã«ãªããŸãã
æ
å ±ãçµ±åããã«ã¯ãããè€éãªäŸãæ€èšããŠãã ããã å€ãã®äººããã³ãã©ãŒãã®æ³åããŸãã¯å®æ°ããååŸããéã®ååžã«ç¹å®ã®æåã®éèŠãªæ°åãçŸãã確çãèšè¿°ããæåã®æ°åã®æ³åã«ç²ŸéããŠãããšæããŸãã ãããããã®ååšãç¥ãããæ³åŸãé©çšãã代ããã«ãå¿
èŠã«å¿ããŠããŒã¿ãé
ä¿¡ãã人ãããŸãã
ãã³ãã©ãŒãã®æ³åã¯ãæœåšçãªè©æ¬ºã®ç¹å®ã«äœ¿çšã§ããŸãããã®å©ããåããŠãããã€ãã®åæå€ã«åºã¥ããŠè¿äŒŒã®å¹ŸäœåŠçã·ãŒã±ã³ã¹ãæ§ç¯ãããããå®éã®ããŒã¿ãšæ¯èŒããŠççŸãèŠã€ããããšãã§ããŸãã ãã®æ¹æ³ã¯ã販売ã¹ã±ãžã¥ãŒã«ãåœå¢èª¿æ»ããŒã¿ãã³ã¹ãã¬ããŒããªã©ã«é©çšã§ããŸãã
ã«ãªãã©ã«ãã¢ã®ãšãã«ã®ãŒå¹çæé©åããã°ã©ã ã®äŸãèããŠã¿ãŸãããã ãŸãããããžã§ã¯ããžã®æè³ã«é¢ããããŒã¿ãå«ãããŒãã«ãäœæããŠèšå
¥ããŸãïŒããŒã¿ã¯
http://open-emv.com/dataã§å
¥æã§ããŸãïŒã
CREATE TABLE open_emv_cost(value float8, district int); COPY open_emv_cost FROM 'open-emv.cost.csv' WITH delimiter ',';
次ã«ããã³ãã©ãŒãã®æ³åã®é¢æ°ãæžããŸãã
CREATE TYPE benford_t AS ( actual_mean float8, n int, expected_mean float8, distorion float8, z float8 ); CREATE OR REPLACE FUNCTION benford(numarr float8[]) RETURNS benford_t AS $$ xcoll <- function(x) { return ((10 * x) / (10 ^ (trunc(log10(x))))) } numarr <- numarr[numarr >= 10] numarr <- xcoll(numarr) actual_mean <- mean(numarr) n <- length(numarr) expected_mean <- (90 / (n * (10 ^ (1/n) - 1))) distorion <- ((actual_mean - expected_mean) / expected_mean) z <- (distorion / sd(numarr)) retval <- data.frame(actual_mean,n,expected_mean,distorion,z) return(retval) $$ LANGUAGE plr;
ãããŠãæ¯èŒãå®è¡ããŸãã
SELECT * FROM benford(array(SELECT value FROM open_emv_cost)); -[ RECORD 1 ]-+
å®éã®ããŒã¿ã¯äºæž¬ãšäžèŽããŠããããã§ããããããã®å Žåãäžæ£ã®å
åã¯ãããŸããã
å·¡åã»ãŒã«ã¹ãã³ã®åé¡ã«æ»ããŸãããã ããã¯çµã¿åããæé©åã®æãæåãªåé¡ã®1ã€ã§ããããã¯ããããã®éœåžãå°ãªããšã1åééããŠããå
ã®éœåžã«æ»ãæãåçæ§ã®é«ãã«ãŒããèŠã€ããããšã«ãããŸãã åé¡ã®ç¶æ³ã§ã¯ãã«ãŒãã®åçæ§ã®åºæºïŒæçãæäœãéçŽåºæºãªã©ïŒãšãè·é¢ãã³ã¹ããªã©ã®å¯Ÿå¿ãããããªãã¯ã¹ã瀺ãããŸãã ååãšããŠãã«ãŒãã¯åéœåžã1åã ãééããå¿
èŠãããããšã瀺ãããŠããŸãã ãã®ç¹å®ã®ãªãã·ã§ã³ãèŠãŠã¿ãŸãããã
æåã«ã蚪åããå¿
èŠããããã¹ãŠã®éœåžãå«ãããŒãã«ãäœæããå
¥åããŸãã
CREATE TABLE stands ( id serial primary key, strata integer not null, initage integer ); SELECT AddGeometryColumn('','stands','boundary','4326','MULTIPOLYGON',2); CREATE INDEX "stands_boundary_gist" ON "stands" USING gist ("boundary" gist_geometry_ops); SELECT AddGeometryColumn('','stands','location','4326','POINT',2); CREATE INDEX "stands_location_gist" ON "stands" USING gist ("location" gist_geometry_ops); INSERT INTO stands (id,strata,initage,boundary,location) VALUES ( 1,1,1, GeometryFromText( 'MULTIPOLYGON(((59.250000 65.000000,55.000000 65.000000,55.000000 51.750000, 60.735294 53.470588, 62.875000 57.750000, 59.250000 65.000000 )))', 4326 ), GeometryFromText('POINT( 61.000000 59.000000 )', 4326) ), ( 2,2,1, GeometryFromText( 'MULTIPOLYGON(((67.000000 65.000000,59.250000 65.000000,62.875000 57.750000, 67.000000 60.500000, 67.000000 65.000000 )))', 4326 ), GeometryFromText('POINT( 63.000000 60.000000 )', 4326 ) ), ( 3,3,1, GeometryFromText( 'MULTIPOLYGON(((67.045455 52.681818,60.735294 53.470588,55.000000 51.750000, 55.000000 45.000000, 65.125000 45.000000, 67.045455 52.681818 )))', 4326 ), GeometryFromText('POINT( 64.000000 49.000000 )', 4326 ) ) ;
çµæãåŸãã«ã¯ãããã€ãã®è£å©ã¯ãšãªãäœæããå¿
èŠããããŸãã æåã®ãã®ã¯ãåºåã§ååŸãããã«ãŒããäœæããŸãïŒ
INSERT INTO stands (id,strata,initage,boundary,location) VALUES ( 4,4,1, GeometryFromText( 'MULTIPOLYGON(((71.500000 53.500000,70.357143 53.785714,67.045455 52.681818, 65.125000 45.000000, 71.500000 45.000000, 71.500000 53.500000 )))', 4326 ), GeometryFromText('POINT( 68.000000 48.000000 )', 4326) ), ( 5,5,1, GeometryFromText( 'MULTIPOLYGON(((69.750000 65.000000,67.000000 65.000000,67.000000 60.500000, 70.357143 53.785714, 71.500000 53.500000, 74.928571 54.642857, 69.750000 65.000000 )))', 4326 ), GeometryFromText('POINT( 71.000000 60.000000 )', 4326) ), ( 6,6,1, GeometryFromText( 'MULTIPOLYGON(((80.000000 65.000000,69.750000 65.000000,74.928571 54.642857, 80.000000 55.423077, 80.000000 65.000000 )))', 4326 ), GeometryFromText('POINT( 73.000000 61.000000 )', 4326) ), ( 7,7,1, GeometryFromText( 'MULTIPOLYGON(((80.000000 55.423077,74.928571 54.642857,71.500000 53.500000, 71.500000 45.000000, 80.000000 45.000000, 80.000000 55.423077 )))', 4326 ), GeometryFromText('POINT( 75.000000 48.000000 )', 4326) ), ( 8,8,1, GeometryFromText( 'MULTIPOLYGON(((67.000000 60.500000,62.875000 57.750000,60.735294 53.470588, 67.045455 52.681818, 70.357143 53.785714, 67.000000 60.500000 )))', 4326 ), GeometryFromText('POINT( 65.000000 57.000000 )', 4326) ) ;
2çªç®ã¯ãplr_modulesã«å
¥åããããŒã¿ãšãçµæã®ããŒã¿ã®ã¿ã€ãã§ãã
DROP TABLE IF EXISTS events CASCADE; CREATE TABLE events ( seqid int not null primary key,
ã¡ã€ã³ã®PL / R颿°ãäœæããŸãã
CREATE OR REPLACE FUNCTION solve_tsp(makemap bool, mapname text) RETURNS SETOF events AS $$ require(TSP) require(fields) sql.str <- "select id, st_x(location) as x, st_y(location) as y, location from stands;" waypts <- pg.spi.exec(sql.str) dist.matrix <- rdist.earth(waypts[,2:3], R=3949.0) rtsp <- TSP(dist.matrix) soln <- solve_TSP(rtsp) tour <- as.vector(soln) pg.thrownotice( paste("tour.dist=", attributes(soln)$tour_length)) route <- make.route(tour, waypts, dist.matrix) if (makemap) { make.map(tour, waypts, mapname) } return(route) $$ LANGUAGE 'plr' STRICT;
ããã§ãmake.routeïŒïŒé¢æ°ãèšå®ããå¿
èŠããããŸãã
INSERT INTO plr_modules VALUES ( 0, $$ make.route <-function(tour, waypts, dist.matrix) { velocity <- 500.0 starts <- tour[1:(length(tour))-1] stops <- tour[2:(length(tour))] dist.vect <- diag( as.matrix( dist.matrix )[starts,stops] ) last.leg <- as.matrix( dist.matrix )[tour[length(tour)],tour[1]] dist.vect <- c(dist.vect, last.leg ) delta.x <- diff( waypts[tour,]$x ) delta.y <- diff( waypts[tour,]$y ) bearings <- atan( delta.x/delta.y ) * 180 / pi bearings <- c(bearings,0) for( i in 1:(length(tour)-1) ) { if( delta.x[i] > 0.0 && delta.y[i] > 0.0 ) bearings[i] <- bearings[i] if( delta.x[i] > 0.0 && delta.y[i] < 0.0 ) bearings[i] <- 180.0 + bearings[i] if( delta.x[i] < 0.0 && delta.y[i] > 0.0 ) bearings[i] <- 360.0 + bearings[i] if( delta.x[i] < 0.0 && delta.y[i] < 0.0 ) bearings[i] <- 180 + bearings[i] } route <- data.frame(seq=1:length(tour), ptid=tour, bearing=bearings, dist.vect=dist.vect, velocity=velocity, travel.time=dist.vect/velocity, loiter.time=0.5) route$total.travel.dist <- cumsum(route$dist.vect) route$total.travel.time <- cumsum(route$travel.time+route$loiter.time) route$location <- waypts[tour,]$location return(route)}$$ );
make.mapïŒïŒé¢æ°ïŒ
INSERT INTO plr_modules VALUES ( 1, $$make.map <-function(tour, waypts, mapname) { require(maps) jpeg(file=mapname, width = 480, height = 480, pointsize = 10, quality = 75) map('world2', xlim = c(20, 120), ylim=c(20,80) ) map.axes() grid() arrows( waypts[tour[1:(length(tour)-1)],]$x, waypts[tour[1:(length(tour)-1)],]$y, waypts[tour[2:(length(tour))],]$x, waypts[tour[2:(length(tour))],]$y, angle=10, lwd=1, length=.15, col="red" ) points( waypts$x, waypts$y, pch=3, cex=2 ) points( waypts$x, waypts$y, pch=20, cex=0.8 ) text( waypts$x+2, waypts$y+2, as.character( waypts$id ), cex=0.8 ) title( "TSP soln using PL/R" ) dev.off() }$$ );
TSPã¿ã°ãåä¿¡ããæ©èœãéå§ããŸãã
詳现ãã¥ãŒïŒ
\x SELECT * FROM solve_tsp(true, 'tsp.jpg'); NOTICE: tour.dist= 2804.58129355858 -[ RECORD 1 ]
ãããã£ãŠããããã®éœåžã蚪ãã䟡å€ãããé åºããããã®éœåžéã®è·é¢ãªã©ãååŸããŸããã ããã«ãå·¡åã»ãŒã«ã¹ãã³ã®åé¡ã«å¯Ÿããæç¢ºãªè§£æ±ºçããããŸãã

ä»åºŠã¯å°éããŒã¿ã䜿çšããå¥ã®äŸãèããŠã¿ãŸãããã
å°éãçºçãããšãéåžž15ã20ç§ç¶ããå°éåŠè
ã¯ãã®æ¯åã®åŒ·åºŠã«é¢ããããŒã¿ãåéããŸãã ã€ãŸããæ³¢åœ¢ã®åœ¢åŒã®ããŒã¿ã®æç³»åïŒtimeseriesïŒïŒæ³¢åœ¢ããŒã¿ïŒããããŸãã ããŒã¿ã¯ãäžå®ã®ãµã³ããªã³ã°åšæ³¢æ°ã§å°éã€ãã³ãäžã«èšé²ãããæµ®åå°æ°ç¹æ°ïŒæµ®åå°æ°ç¹æ°ïŒã®é
åãšããŠä¿åãããŸãã ãããã¯ãåã¢ã¯ãã£ããã£ã®åå¥ã®ãã¡ã€ã«ã§ãªã³ã©ã€ã³ãœãŒã¹ããå
¥æã§ããŸãã åãã¡ã€ã«ã«ã¯çŽ16,000ã®èŠçŽ ãå«ãŸããŠããŸãã
PL / pgSQLã䜿çšããŠ1000ã®å°é掻åãããŒãããŸãã
DROP TABLE IF EXISTS test_ts; CREATE TABLE test_ts ( dataid bigint NOT NULL PRIMARY KEY, data double precision[] ); CREATE OR REPLACE FUNCTION load_test(int) RETURNS text AS $$ DECLARE i int; arr text; sql text; BEGIN arr := pg_read_file('array-data.csv', 0, 500000); FOR i IN 1..$1 LOOP sql := $i$INSERT INTO test_ts(dataid,data) VALUES ($i$ || i || $i$,'{$i$ || arr || $i$}')$i$; EXECUTE sql; END LOOP; RETURN 'OK'; END; $$ LANGUAGE plpgsql; SELECT load_test(1000); load_test
ããã¯ãRã®å©ããåããŠå¥ã®æ¹æ³ã§è¡ãããšãã§ããååã®æéãããããŸã-PL / Rã䜿çšããå Žåãããã»ã¹ãé
ãããã®ã§ã¯ãªããã¹ããŒãã¢ããããããšã¯ãŸãã§ãïŒ
DROP TABLE IF EXISTS test_ts_obj; CREATE TABLE test_ts_obj ( dataid serial PRIMARY KEY, data bytea ); CREATE OR REPLACE FUNCTION make_r_object(fname text) RETURNS bytea AS $$ myvar<-scan(fname,sep=",") return(myvar); $$ LANGUAGE 'plr' IMMUTABLE; INSERT INTO test_ts_obj (data) SELECT make_r_object('array-data.csv') FROM generate_series(1,1000); INSERT 0 1000 Time: 12166.137 ms
ãªã·ãã°ã©ã ãäœæããŸãããïŒ
CREATE OR REPLACE FUNCTION plot_ts(ts double precision[]) RETURNS bytea AS $$ library(quantmod) library(cairoDevice) library(RGtk2) pixmap <- gdkPixmapNew(w=500, h=500, depth=24) asCairoDevice(pixmap) plot(ts,type="l") plot_pixbuf <- gdkPixbufGetFromDrawable( NULL, pixmap, pixmap$getColormap(), 0, 0, 0, 0, 500, 500 ) buffer <- gdkPixbufSaveToBufferv( plot_pixbuf, "jpeg", character(0), character(0) )$buffer return(buffer) $$ LANGUAGE 'plr' IMMUTABLE; SELECT plr_get_raw(plot_ts(data)) FROM test_ts WHERE dataid = 42;
ç¹å®ã®å°éã«ã€ããŠãã®ç»åãååŸããŸãã

次ã«ãããšãã°ãåæ§ã®å°é掻åãããå°åã§å»ºç©ãèšèšããæåã®æ¹æ³ãèŠã€ããããã«ãåæããå¿
èŠããããŸãã ãããããåŠæ ¡ã®ç©çåŠã³ãŒã¹ã§ãå
±æ¯åšæ³¢æ°ããªã©ã®ããšãèŠããŠããã§ãããã 念ã®ãããããã¯çºæ¯æ¯å¹
ãæ¥æ¿ã«å¢å ãããããªçºæ¯åšæ³¢æ°ã§ããããšãæãåºãããŠãã ããã ãã®é »åºŠã¯ãã·ã¹ãã ã®ããããã£ã«ãã£ãŠæ±ºãŸããŸãã ã€ãŸããå°é掻åã®ããå°åã§ã¯ãå
±æ¯åšæ³¢æ°ãå°éã®åšæ³¢æ°ãšäžèŽããªãããã«æ§é ãèšèšããå¿
èŠããããŸãã ããã¯Rã䜿çšããŠå®è¡ã§ããŸãã
CREATE OR REPLACE FUNCTION plot_fftps(ts bytea) RETURNS bytea AS $$ library(quantmod) library(cairoDevice) library(RGtk2) fourier<-fft(ts) magnitude<-Mod(fourier) y2 <- magnitude[1:(length(magnitude)/10)] x2 <- 1:length(y2)/length(magnitude) mydf <- data.frame(x2,y2) pixmap <- gdkPixmapNew(w=500, h=500, depth=24) asCairoDevice(pixmap) plot(mydf,type="l") plot_pixbuf <- gdkPixbufGetFromDrawable( NULL, pixmap, pixmap$getColormap(), 0, 0, 0, 0, 500, 500 ) buffer <- gdkPixbufSaveToBufferv( plot_pixbuf, "jpeg", character(0), character(0) )$buffer return(buffer) $$ LANGUAGE 'plr' IMMUTABLE; SELECT plr_get_raw(plot_fftps(data)) FROM test_ts_obj WHERE dataid = 42;
ãããŠãããã¯æçµçµæãã©ã®ããã«èŠãããã§ãããæ¯åã®åšæ³¢æ°ãšãã®æ¯å¹
ã瀺ããŠããŸãïŒ

建ç¯å®¶ã¯ãã®ãããªã¹ã±ãžã¥ãŒã«ãç®ã®åã«ããŠãèšç®ãè¡ããæåã®éãã§åŽ©å£ããªãæ§é ãèšèšã§ããŸãã
ã芧ã®ããã«ãPL / Rã«ã¯ã¢ããªã±ãŒã·ã§ã³ã®å€ãã®é åããããŸããé«åºŠãªåæãããŒã¿ããŒã¹ã«æ¥ç¶ããåæã«RãšPostgreSQLã®æ©èœããã¹ãŠã®å©ç¹ãšãšãã«äœ¿çšã§ããããã§ãã ãã®èšäºãã圹ã«ç«ãŠã°å¹žãã§ãããããŠããã®ãããªå¹æçã§æ±çšæ§ã®é«ãããŒã«ã®äœ¿çšãèŠã€ããŠãã ããã
ãŸãããã®èšäºãäœæãããè³æã«åºã¥ããŠãPG Day'15 Russiaã§ã®Joe Conwayã®ããã©ãŒãã³ã¹ããã®ãã¬ãŒã³ããŒã·ã§ã³ãšãããªãã芧ãã ããã PL / Rã䜿çšãããããã¯ãããªãã«ãšã£ãŠè峿·±ããšæãããå Žåã¯ãå¿
ãããªãã®èããã¢ã€ãã¢ãå
±æããŠãã ããã 7æã«éå¬ãããPG Day'16ã§ããã®ãããã¯ã«é¢ããã¬ããŒããæŽçãããããããŸããã ãã€ãã®ããã«ãããªãã®ã³ã¡ã³ããåŸ
ã£ãŠããŸãã