読者です 読者をやめる 読者になる 読者になる

/home/by-natures/dev*

ソフトウェア開発者として働く人の技術的なメモ

NVD3 で multiBarChart を扱う場合、データには数値型を渡す

Java での開発は少しずつ慣れているのですが、JavaScript は入る現場ごとに使うフレームワークが変わる印象があります。4年前ほどにジョインしたプロジェクトでは ExtJS, 同時期に走っていた別のプロジェクトでは AngularJS を使っていて、チーム間の技術的な交流はほとんどありませんでした。最近は React.js をよく耳にしますが、現在のプロジェクトでも React.js を使っています。最初は JavaScript の中にタグを直接書く JSX というフレームワークに強い抵抗がありましたが、出てくる DOM と対応がよく取れていて、慣れるととても便利です。

一方、JavaScript にはグラフ描画のための d3.js というライブラリがあり、このラッパーである NVD3 というライブラリがあります。更にそれを React.js で使えるようにした react-nvd3 というライブラリがありますが、今回はこのライブラリで詰まった点があったので共有します。

NVD3 では様々なグラフが簡単に扱えます。その中の一つに棒グラフがあります:

var props = {
  type: "multiBarChart",
  datum: [{
    key: "num",
    values: [{ x: "A0", y: "5" },{ x: "A1", y: "5" },{ x: "A2", y: "5" },{ x: "A3", y: "5" }]
  },{
    key: "num2",
    values: [{ x: "A0", y: "1" },{ x: "A1", y: "1" },{ x: "A2", y: "1" },{ x: "A3", y: "1" }]
  },{
    key: "num3",
    values: [{ x: "A0", y: "2" },{ x: "A1", y: "2" },{ x: "A2", y: "2" },{ x: "A3", y: "2" }]
  }],
  containerStyle: {
    width: 500,
    height: 300
  }
};

...

return (<div><NVD3Chart {...props}/></div>);

これを実行すると、以下のような棒グラフが出てきます:

f:id:bynatures:20160914025026p:plain

上に Grouped, Stacked という選択肢があるのですが、左が種類ごとに出力する棒グラフ、右が積み上げの棒グラフです。ここで Stacked を選択すると…

f:id:bynatures:20160914025333p:plain

オレンジの領域が他の種別に覆いかぶさってしまっています。上のプログラムを見ると、y 軸の値が "5" のような文字列になっているのが分かります。これが問題の原因だったのですが、Grouped では正常に動いていたため、気づくのが遅れました。(実際はプログラムから上のオブジェクトを生成していたので、ここまで簡略化してようやく気づきました。。)

y 軸の値を整数値にすることで以下のような正しい積み上げ棒グラフが得られます(単に y軸の tickFormat で変換するだけではダメで、元々の与えるデータを整数値にしないと駄目でした):

f:id:bynatures:20160914030846p:plain

z軸…というか値のかぶせ方を決める計算が誤っているのだ思い、ソースコードを見てみましたが、NVD3 の GitHub repo の multiBar.js に該当する処理がありそうですが、処理が複雑で追いきれていません。

意図しない挙動を避けるためにも、棒グラフ・折れ線グラフの y軸など、数値が想定されている箇所には強制的に数値へ変換してしまうのがよいような気がしています。+"a" など、何かの値に単項演算子+ をつけると数値か NaN へ変換してくれるので便利です。

var a = "str";
+a;   // =>  NaN
+a  ||  0;   // =>  0

質問して結局自己解決してしまった StackOverflow の質問:

stackoverflow.com