htmlwidgetsでJavascriptの可視化をRに

@yutannihilation
2015/2/21, Tokyo.R 46

自己紹介

インフラエンジニア

infrastructure

  • リソース分析
  • キャパシティプランニング
  • 障害調査

とかでRを使います。(でもほぼ趣味)

統計のことあんまり分かりません。。

電子工作ワナビー

巷で流行っているというラズパイとか。 らずぱい

htmlwidgets

htmlwidgetsとは

Javascriptの可視化をRのパッケージで使うのを簡単にするフレームワーク的なもの(たぶん)。

公式サイト
http://www.htmlwidgets.org/

leaflet

dataTable networkD3

htmlwidgetsでつくられたパッケージ

+ 自分でも作れる!

というわけで、   つくってみます。

1. 使いたいJavascriptライブラリを探す

探し方はひとそれぞれ

  • ググる or ヤフる or …
  • Githubで流行ってそうなライブラリをExplore
  • 突然のリプが飛んでくる
  • totsuzen

今回はhighstock

Bowerにあるか調べる

BowerはJavascriptのパッケージマネージャ。

Bower

highstockはあります。

bower search

データ形式を調べる

※この辺の考慮はjsonliteになれば不要かも

  • 列方向 → だいたいそのままでOK
  • 行方向 → Javascript側で変換が必要
  • 時系列データ → いちど文字列にしてJavascript側で再度変換した方が無難

D3.jsでよくあるデータ形式

  • 行方向(点にデータを持たせる)
[
  {x: 1, y: 2, value: 100, lable: "A"},
  {x: 3, y: 9, value:   0, lable: "B"},
  {x: 3, y: 3, value: 999, lable: "C"},
  ...
]

HTMLWidgets.dataframeToD3()を使いましょう。

highstockはこんな感じ

[
  [1147651200000,376.20],
  [1147737600000,371.30],
  [1147824000000,374.50],
  [1147910400000,370.99],
  [1147996800000,370.02],
  ...
]

グラフを描くのに必要な条件

  • 依存しているライブラリ
  • div要素をクラス名を特定の名前にする
  • 設定をしたあとでrender()を呼び出す

highstockはこんな感じ

$('#foo').highcharts('StockChart', {
  series : [
    {name : 'GOOG', data : data1},
    {name : '^GSPC', data : data2}
  ]
});
  • 要素のIDを渡してjQuery化($())する
  • .highcharts()を呼ぶ

だけやればおk

2. パッケージのひな形をつくる

Rパッケージのひな形をつくる

New project > New Directory > R package

new

(devtoolsの場合)

devtools::create("mywidget")
setwd("mywidget")

htmlwidgetsのひな形をつくる

htmlwidgets::scaffoldWidget("ウィジェット名", bowerPkg = "bowerでのパッケージ名")

以下のファイルができる

  • R/ウィジェット名.R
  • inst/htmlwidgets/ウィジェット名.yaml
  • inst/htmlwidgets/ウィジェット名.js

※ウィジェット名がパッケージ名と同じだと「ウィジェット名.R」と「パッケージ名.R」が衝突する。

不要なファイルを消す

bowerは、inst/htmlwidgets/lib/以下にレポジトリ丸ごと取ってきてしまう。

→ 関数名.yamlに書いてあるファイルさえあればいい。残りは消した方がパッケージが軽量に。

ゴミ箱の写真

3. コードを書く

highstockはこんな感じ

[
  [1147651200000,376.20],
  [1147737600000,371.30],
  [1147824000000,374.50],
  [1147910400000,370.99],
  [1147996800000,370.02],
  ...
]
  • 時刻は1970/1/1 00:00:00基準のマイクロ秒
  • matrixならそのまま渡せばいい

こんなデータを渡せば大丈夫

library(dplyr)
data <- RFinanceJ::rfj("USDJPY=X") %>%
  transmute(Date = as.numeric(Date) * 3600 * 24 * 1000, Close) %>%
  arrange(Date) %>% # データは昇順に整列
  as.matrix

# オブジェクトではなく配列に変換するためcolnameを消す
colnames(data) <- NULL

Javascript側

  • initialize(el, width, height):
    • グラフ描画前に呼ばれる。サイズの設定とか。
    • 返り値がrenderValue()、resize()で使える。
    • elは対象の要素
  • renderValue(el, x, instance)
    • Rからデータ(x)を受け取ってグラフを描く
    • instanceはinitializeの返り値
  • resize(el, width, height, instance)
    • 画面のサイズが変わったとき再描画する。

Javascript側

とりあえずrenderValue()があれば動く。

renderValue: function(el, x, instance) {
  $('#' + el.id)
    .highcharts('StockChart', {
      series : [
        {name : x.name, data : x.data}
      ]
    });
}
  • el.idは要素のID。セレクタにするときは#を付ける必要あるのに注意。

R側

  • ウィジェット名()
    • グラフを描く。createHTMLWidgetを呼ぶ。
  • ウィジェット名Output()
    • Shiny用。説明は省略。
  • renderウィジェット名()
    • Shiny用。説明は省略。
  • ウィジェット名_html()
    • DOM要素をカスタマイズする。(例:ラベル用のdivを追加する)

R側

# forward options using x
x = list(
  data = data,
  name = name
)

# create widget
htmlwidgets::createWidget(
  name = 'highstock',
  x,
  width = width,
  height = height,
  package = 'highstockR'
)

DESCRIPTION

Shinyapps.ioで使うときとかは、この一行がないとパッケージのインストールに失敗する。

Imports: htmlwidgets

misc

  • YAMLに書いたバージョンをv2.1.0から2.1.0
  • 依存しているjQueryをインストール
  • jQueryをYAMLに書き加える

つまづきポイントが地味にいろいろあります(白目)

結果

highstockR::highstock(as.matrix(data), name = "USDJPY=X")

highstock

#tsurami

やれやれ、僕はJSした。

  • htmlwidgetsがある程度やってくれるとはいえ、Javascriptを書かないとどうしようもない場面もある
    • 軸のラベル用の関数とか
    • Javascriptの関数を返すRの関数を書くのはこんな感じで悩ましい:
function(interval = 1, offset = 0) {
  htmlwidgets::JS(
    sprintf("function(value, index) {return (index - %d) %% %d === 0 ? %s : null;}", offset, interval)
  )
}

Why are u using UTF-8???

D3.jsがUTF-8の変数名を使っていて、knitすると文字化けする(最新版pandocでは改善済み)

before: d3

after: knitted

d3.min.jsを使うと安全 参考:htmlwidgetsでD3.jsを使おうとしたら文字コードの闇に飲まれかけた話 - Technically, technophobic.

テストしづらい

(ネタ切れです)

  • R側のエラーは拾えるけど、Javascript側のエラーはデバッグがつらい
  • 開発者ツール(F12で表示されるやつ)とかconsole.log()を駆使する
  • testthatでのテストは文法チェックくらいしか書けなそう

グラフ間でのオブジェクト共有

  • インタラクティブなグラフは、同じオブジェクトを共有しないといけないこともある。
  • いい感じの手段は用意されていない。たぶんwindowのプロパティに渡す?

めでたし、めでたし!

めでたし、めでたし?

ホラーそうな写真

開いてしまった、  パンドラの箱!

こんなことばを聞いたことがないですか?

「IE爆発しろ!!!」

爆発の写真

想像してください…

  • あなたのMacBook AirのChromeではグラフが表示されても、クライアントが使っているWindows XPのIE8では表示されないところを
  • RPubsに公開して昨日まで表示されていたグラフが、ブラウザをアップデートしたら表示できなくなるところを
  • そして、「これどうにかしてよ」と言われて、全く詳しくないJavascriptとCSSと戯れざるを得なくなる日を
  • そう、IE爆発しろ!と言いたくなる日を

Reproducibilityは実際大事。古事記にもそう書いてある。

  • ブラウザに依存する以上、「誰がやっても同じ結果になる」ことは保障されない
  • Javascriptのチャラいグラフもいいけど、確実なのは画像

まとめ

  • Javascriptは楽しい
  • つらみもある
  • つらみを乗り越えても越えられない壁があるかも
  • 使用上の注意と用法を守って楽しみましょう

highstockR

URL

https://github.com/yutannihilation/highstockR

インストール

library(devtools)
install_github("yutannihilation/highstockR")

References