htmlwidgetsでRから渡されたデータをD3.js用の形式に変形する

前回、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)

すると、以下のように表示されているはずです。

f:id:yutannihilation:20141223160811p:plain

だいたい望んだとおりの結果です。

日付型とかの変換は自動ではやってくれないみたいですね。ちなみに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));
    
    },

するとこんな風になるはずです。

f:id:yutannihilation:20141223160749p:plain

でも思いました。

転置なら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はイケてない、というのは共通認識のようです。

to be able to specify one's own JSON converter by yihui · Pull Request #30 · ramnathv/htmlwidgets · GitHub

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は闇。