gridSVGで散布図上の点のIdentification

gridSVG - Export grid graphics as SVG

gridSVGはgridライブラリによるグラフィックスをSVG形式で出力するためのライブラリ。ggplot2はgridを利用しているので、ggplot2の出力もSVGにできる。単に出力するだけでなくJavaScriptを埋め込むことができるので、インタラクティブな機能を実装できる。標準でもSVG出力はサポートしているが、プロットの構造は保持できないため、データに依存したインタラクティブ機能の実装が自動化できない。

散布図上の点をクリックするとその点の情報がalertで表示される例。Chromeで動く。Firefoxはuseタグの扱い(correspondingUseElement)が違うのでクロスブラウザ対策が必要。

library(ggplot2)
library(gridSVG)
library(grid)
library(rjson)

pdf(file=NULL, width=7, height=5)

ggplot(iris, aes(Petal.Width, Petal.Length)) + 
	geom_point(aes(colour=Species, size=Sepal.Width))

grid.script(paste("var x=", toJSON(iris$Petal.Width),";",sep=""))
grid.script(paste("var y=", toJSON(iris$Petal.Length),";",sep=""))

grid.script("
	info = function(e){
		id = e.target.correspondingUseElement.id;
		id = parseInt(id.replace('geom_point.points.1.',''));
		alert('Petal.Width='+x[id-1]+'\\nPetal.Length='+y[id-1]);
	}
")

grid.gedit("geom_point.points", name="geom_point.points")
grid.garnish("geom_point.points", onclick="info(evt)")
grid.export("iris_plot.svg")

dev.off()


f:id:nonki1974:20131201092548j:plain

Rで統計API

統計APIの試験運用が始まったということで、Rから使ってみたのでメモ。
自治体独自の統計もこの形式に準拠できればいいかも。

次世代統計利用システム
http://statdb.nstac.go.jp/

使ったライブラリは以下の4つ。

library(RCurl)
library(rjson)
library(XML)
library(ggplot2)

統計表検索のための関数。
optionsには、検索パラメータ名と値の組のリストを与える。
appIdはサイトで登録して取得。

getStatList <- function(options){
	appId <- "hogehoge"
	URL <- "http://statdb.nstac.go.jp/rest/1.0/app/getStatsList?"
	appIdstring <- paste("appId", appId, sep="=")
	opstring <- paste(names(options), unlist(options), sep="=", collapse="&")

	result <- xmlToList(getURI(paste(URL, appIdstring, opstring, sep="&")))
	
	data.frame(do.call("rbind",sapply(result$DATALIST_INF, function(x){
		if(class(x)=="list") unlist(x)
	})))
}

データ取得のための関数。
statsDataIdに統計表IDを指定。
optionsには絞り込みのためのパラメータをリストで与える。

getDFfromStatAPI <- function(statsDataId, options=NULL){

	appId <- "hogehoge"
	URL <- "http://statdb.nstac.go.jp/rest/1.0/app/json/getStatsData?"
	appIdstring <- paste("appId", appId, sep="=")
	sdstring <- paste("statsDataId", statsDataId, sep="=")

	opstring <- paste(names(options), unlist(options), sep="=", collapse="&")

	data.json <- getURI(paste(URL, appIdstring, sdstring, opstring, sep="&"))
	data.list <- fromJSON(data.json)

	data <- data.frame(
		do.call("rbind", data.list$GET_STATS_DATA$STATISTICAL_DATA$DATA_INF$VALUE))
	names(data) <- gsub("X.", "", names(data))
	names(data)[ncol(data)] <- "X"

	classinf <- with(data.list$GET_STATS_DATA$STATISTICAL_DATA$CLASS_INF, {
		classinf <- list()
		for(i in 1:length(CLASS_OBJ)){
			classinf[[CLASS_OBJ[[i]]$`@id`]] <- list()
			classinf[[CLASS_OBJ[[i]]$`@id`]]$name <- CLASS_OBJ[[i]]$`@name`
			
			if(is.null(CLASS_OBJ[[i]]$CLASS$`@code`)){
				classinf[[CLASS_OBJ[[i]]$`@id`]]$code <- data.frame(
					do.call("rbind", CLASS_OBJ[[i]]$CLASS))
			} else {
				classinf[[CLASS_OBJ[[i]]$`@id`]]$code <- data.frame(
					do.call("cbind", CLASS_OBJ[[i]]$CLASS))
			}	
		}
		classinf
	})

	data.df <- list()
	for(name in names(classinf)){
		data.df[[paste(classinf[[name]]$name,"コード",sep="")]] <- unlist(data[[name]])
		data.df[[classinf[[name]]$name]] <- unlist(
			classinf[[name]]$code$X.name[match(data[[name]], classinf[[name]]$code$X.code)])
	}

	data.df$X <- unlist(data$X)
	data.df$unit <- unlist(data$unit)

	as.data.frame(data.df)

}

2013年に公開された人口推計(政府統計コード:00200524)を検索。

search.result <- getStatList(list(statsCode="00200524", openYears="2013"))

参考表 年齢(各歳)(統計表ID: 0003080204)
日本人人口(050)、男女(001および002)、0歳から88歳まで(1000番台)

jinsui.df <- getDFfromStatAPI("0003080204", list(
	cdCat01="050", cdCat02="001,002",
	cdCat03From="01001", cdCat03To="01999"))

jinsui.df$年齢各歳コード <- as.numeric(as.character(jinsui.df$年齢各歳コード))
jinsui.df$X <- as.numeric(as.character(jinsui.df$X))

男女別人口のプロット。

qplot(as.factor(as.numeric(gsub("歳","",年齢各歳))), X,
	  geom="bar", stat="identity", position="dodge",fill=男女別,
	  data=jinsui.df, xlab="年齢", ylab="人口",
	  main="日本人人口 平成24年10月1日現在人口")

出力。

f:id:nonki1974:20130614221130j:plain

RApacheにおけるマルチバイト文字の利用について

RApacheでのマルチバイト文字(例えばUTF-8)を扱うには

Sys.setlocale(category='LC_ALL','ja_JP.UTF-8')

を実行しておく必要がある。これをやっておかないと、マルチバイト文字を含むファイルの読み込みや、iconvによる文字コード変換が正しく動作しない。

※ 参考 "UTF-8 characters in RApache"
https://groups.google.com/forum/?fromgroups=#!topic/rapache/V3IAIRhAybw

ある要素の子要素全てにアクセスする方法(SVG)

SVGでいくつかの要素をgタグでグループ化しているとき、getElementById()でgタグの要素を取得して、その子要素全てにアクセスする例。

childNodesで子ノードが取得できる。ノードにはテキストノード(text要素ではない)が含まれるので、nodeNameでノードの種類をチェックした上で処理を行う。

<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink"
	width="800" height="800"
     version="1.0" onload="init()">
<script type="text/javascript">
<![CDATA[
function init(){
	var group1 = document.getElementById("group1");
	var children = group1.childNodes;
	for(i=0;i<children.length;i++){
		if(children[i].nodeName=='circle'){
			children[i].style.setProperty('fill', '#00ff00');
		}
	}
}
]]>
</script>
<g id="group1">
	<circle cx="100" cy="100" r="50" style="fill: #ff0000" />
	<circle cx="200" cy="100" r="50" style="fill: #ff0000" />
	<circle cx="300" cy="100" r="50" style="fill: #ff0000" />
	<circle cx="400" cy="100" r="50" style="fill: #ff0000" />
</g>
<g id="group2">
	<circle cx="100" cy="200" r="50" style="fill: #ff0000" />
	<circle cx="200" cy="200" r="50" style="fill: #ff0000" />
	<circle cx="300" cy="200" r="50" style="fill: #ff0000" />
	<circle cx="400" cy="200" r="50" style="fill: #ff0000" />
</g>
</svg>

SVGの要素をHTMLのフォームパーツで操作する

objectタグで適当なidをつけてSVGを埋め込んで、

document.getElementById('hogehoge').contentDocument;

で、SVGDocumentを取得。あとは、SVGに対するJavaScriptを記述すればよい。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>traffic light</title>
<script type="text/javascript">
var now = 'blue';
var svg;
function init(){
	svg = document.getElementById("testsvg").contentDocument;
	switch_on(now);
}

function switch_on(col){
	var circle = svg.getElementById(now);
	circle.style.setProperty('fill', '#999999');
	circle = svg.getElementById(col);
	circle.style.setProperty('fill', col);
	now = col;
	
}
</script>
</head>
<body onload="init()">
<h1>traffic light</h1>
<object id="testsvg" data="test.svg" type="image/svg+xml" width="600px" height="200px"> </object>
<p>
<input type="radio" name="switch" value="blue" onclick="switch_on(this.value);" checked>Blue
<input type="radio" name="switch" value="yellow" onclick="switch_on(this.value);">Yellow
<input type="radio" name="switch" value="red" onclick="switch_on(this.value);">Red
</p>
</body>
</html>

SVGのコードは以下のとおり。

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     width="600px"
     height="200px"
     version="1.0">
<circle id="blue" cx="100" cy="100" r="100" style="fill:#999999" />
<circle id="yellow" cx="300" cy="100" r="100" style="fill:#999999" />
<circle id="red" cx="500" cy="100" r="100" style="fill:#999999" />
</svg>

MacOSXでiplotsを使う

iplotsはJGR(Java GUI for R)の中で動作するので、JGRのインストールも必要。

install.packages("iplots")
install.packages("JGR")

さらに、CarbonELパッケージも必要。これはinstall.packagesでインストールできないので、CarbonELのダウンロードページからCarbonEL_0.1-4.tgzをダウンロードして解凍、できたフォルダをライブラリのフォルダ(/Library/Frameworks/R.framework/Versions/2.15/Resources/library など)にコピーする。次に、

Sys.setenv(NOAWT=1)
library(JGR)
JGR()

これでJGRのコンソールが起動して、iplotsが利用できるようになる。Sys.setenvを実行しないとエラーとなる。