前回、data.frameとD3.jsで必要なデータ形式が違って困った困ったって言ってたわけですが、ちゃんとやり方が用意されていました。RTFMってやつですね。
R側でデータ形式をいじるユーティリティは用意されていなくて、JS側に渡された後にデータを変形する関数を通す、という方法です。やってみます。
ドキュメント
このへん。
Advanced topics - Data transformation
http://www.htmlwidgets.org/develop_advanced.html#data-transformation
D3形式に直す(dataframeToD3
)
R data frames are represented in “long” form (an array of named vectors) whereas d3 typically requires “wide” form (an array of objects each of which includes all names and values). Since the R representation is much faster to transmit over the network, we allow RJSONIO to proceed with it’s default behavior and then transform the data in JavaScript using the
dataframeToD3
helper function.
前回も書きましたが、htmlwidgetsはデフォルトだとdata.frame
を以下のように変換します。
{ "attr1": [1, 2, 3, 4, 5], "attr2": ["a", "b", "c", "d", "e"] }
ですが、D3.jsは以下のような形式を必要とします。
[ {"attr1": 1, "attr2": "a"}, {"attr1": 2, "attr2": "b"}, {"attr1": 3, "attr2": "c"}, {"attr1": 4, "attr2": "d"}, {"attr1": 5, "attr2": "e"} ]
これにはdataframeToD3
というのがあるらしいです。
前回つくったhoge.js
を以下のように書き換えます。
renderValue: function(el, x, instance) { el.innerText = JSON.stringify( HTMLWidgets.dataframeToD3(x) ); },
これをビルドして、data.frameを渡してみます。
set.seed(100) days <- seq(as.Date("2014-12-18"),as.Date("2014-12-23"), by = "day") ( x <- data.frame(days, value = runif(length(days))) ) hoge(x)
すると、以下のように表示されているはずです。
だいたい望んだとおりの結果です。
日付型とかの変換は自動ではやってくれないみたいですね。ちなみにDate
型は、RでもJavascriptでも同じ名前の方があってどちらも1970年1月1日からの経過時間を表しますが、単位が違います。(Rは日数、Javascriptはミリ秒)。注意。
2次元行列を転置する(transposeArray2D
)
Sometimes a 2-dimensional array requires a similar transposition. For this the
transposeArray2D
function is provided.
これはいまいち使い方を理解できていません。。 dygraphsとかではこんな感じの2次元配列のデータが必要らしいです。
[ [1, 2, 3, 4, 5], ["a", "b", "c", "d", "e"] ]
ところが、htmlwidgetsはmatrixを渡すと以下のように値を変換してしまいます。(注:matrixはひとつの型しか持てないので、数字が文字になってしまうのは仕方ないです)
[ ["1", "a"], ["2", "b"], ["3", "c"], ["4", "d"], ["5", "e"] ]
こんなときにはtransposeArray2D
です。
hoge.js
を以下のように修正します。
renderValue: function(el, x, instance) { el.innerText = JSON.stringify(HTMLWidgets.transposeArray2D(x)); },
するとこんな風になるはずです。
でも思いました。
転置ならt()
でよくない??
これがなぜ便利なのか、誰か教えてください。。ちなみに、data.frameを渡すとエラーが出ます。matrixしかむりです。
データ変形をJavascript側でやるようになった経緯(推測)
ここから先は細かい話なので興味ない人は読み飛ばしてください。
Githubでの議論を少し追ってみます。
to_json()
かつてはto_json()
という関数を用意してR側でやろうとしていたみたいです。
htmlwidgets/to_json.R at master · ramnathv/htmlwidgets · GitHub
3行目あたりを読むと、これはRJSONIO
がイケてないために用意された関数のようです。
が、これ、exportされていないので外からは使えません。コードを検索してみても、他の場所で使われている気配はありません。 使おうと用意したけどそのまま放置されているように見えます。なぜでしょう?
疑問は尽きませんが、一旦置いておいて、プルリクエストとイシューでの議論を眺めてみます。
RJSONIO
やめてjsonlite
使おうよ
という男前な提案をyihuiさん(knitrの開発者)がしています。やはりRJSONIO
はイケてない、というのは共通認識のようです。
Switch to jsonlite by yihui · Pull Request #28 · ramnathv/htmlwidgets · GitHub
しかし、ramnathvさん(htmlwidgets・rChatsの開発者)によれば、RJSONIO
を使っているのは、ShinyがRJSONIO
を使っているから仕方なく、という感じのようです。
Shinyがjsonliteを使うようにならないと、切り換えるつもりはなさそうです。
One way to handle things is to have this in a separate branch of htmlwidgets so that it is easy to switch when shiny ultimately switches to jsonlite.
(https://github.com/ramnathv/htmlwidgets/pull/30#issuecomment-61866591)
そしてto_json()
についても言及しています。
@yihui I was merely clarifying the origin of the orient argument. to_json was built as a wrapper around RJSONIO to maintain shiny compatibility and provide jsonlite like features, but if we switch to jsonlite, we would not longer require to_json as you correctly indicated.
(https://github.com/ramnathv/htmlwidgets/pull/28#issuecomment-61888809)
jsonlite使うならどうせ使わなくなるしいっか。という感じで放置されているようです。なるほどー。。
一方そのころShinyは...
Shinyでのjsonlite
への切り替えの議論は、このIssueで進んでいます。
switch from RJSONIO to jsonlite · Issue #572 · rstudio/shiny · GitHub
pull requestも出ていますが、放置されているように見えます。
Switch from RJSONIO to jsonlite by wch · Pull Request #606 · rstudio/shiny · GitHub
ということで、htmlwidgetsでR側からデータ形式をコントロールできるようになるのはけっこう先のようです。ぐぬぬ。。
まとめ
RJSONIO
は闇。