d3のd3.interpolateObjectで返される補間オブジェクトが共有化されてる
d3.interpolateObject
は2つのオブジェクトを補間してくれるので欠損データの扱いとかが楽にできるんですが、バグ?というか、ハマるところ。
var ts = [ {time: new Date(2012, 0, 1), sum: 132435}, {time: new Date(2012, 0, 2), sum: 133450}, {time: new Date(2012, 0, 3), sum: 164345}, {time: new Date(2012, 0, 4), sum: 133100}, {time: new Date(2012, 0, 8), sum: 46345}, {time: new Date(2012, 0, 9), sum: 136345}, {time: new Date(2012, 0, 10), sum: 106345} ];
のようなデータがあるけど、Holt-Wintersとかで使うには都合が悪い これを一定間隔にしないと行けない。補間の方法は、線形補完とか、スプラインとかいろいろあるけど、それは割愛
で、時間を埋めるのに
var idx = 0; var prev = ts[idx], cur, cnt; while(idx++ < ts.length - 1){ cur = ts[idx]; if(cur.time - prev.time > 24*3600*1000){ cnt = (cur.time - prev.time) / 24 * 3600 * 1000; var ip = d3.interpolateObject(prev, cur); d3.range(1, cnt).forEach(function(d){ ts.splice(idx++, 0, ip(d/cnt)); }); } }
とすると、timeがnew Date(2012, 0, 7)
のものでうめつくされて困る。
これは、最初にあげたissueでもあるように、.interpolateObject()
がパフォーマンスを考え、共有オブジェクトの参照を返してくるから。pull reqも考えたんだけど、手元で対応するほうがいい気もして、悩ましい。手元でやるなら、循環参照さえないことがわかっていれば*1、さくっと
ts.splice(idx++, 0, JSON.parse(JSON.stringify(ip(d/cnt))));
としてあげたほうがよい
*1:普通のデータ配列には参照は無いはず