3

このコードの動作はここで確認できます:http://bl.ocks.org/2626142

このコードは折れ線グラフを描画し、3つのサンプルデータセット間を遷移します。小さなデータセットから大きなデータセットに移動すると、既存の線からスムーズに展開するのではなく、余分なデータポイントが突然表示されます。

大きなデータセットから小さなデータセットに移動すると、グラフ全体を埋めるために遷移する前に、線が突然切り捨てられます。

このコードでは、線とグリッド線に突然の追加と削除があります。それらを削除するにはどうすればよいですか?

var data = [
    [0,2,3,2,8],
    [2,4,1,5,3],
];
var data2 = [
    [0,1,2,3,4,5],
    [9,8,7,6,5,6],
];
var data3 = [
    [1,3,2],
    [0,8,5],
];

var w = 300,
    h = 100;

var chart = d3.select('body').append('div')
    .attr('class', 'chart')
    .append('svg:svg')
    .attr('width', w)
    .attr('height', h);

var color = d3.scale.category10();

function drawdata(data, chart) {
    var num = data[0].length-1;
    var x = d3.scale.linear().domain([0, num]).range([0,w]);
    var y = d3.scale.linear().domain([0, 10]).range([h, 0]);

    var line = d3.svg.line()
        .x(function(d, i) { return x(i); })
        .y(function(d) { return y(d); });

    var flat = d3.svg.line()
        .x(function(d, i) { return x(i); })
        .y(y(-1));

    var lines = chart.selectAll('.line')
        .data(data);

    lines.enter().append('path')
            .attr('class', 'line')
            .style('stroke', function(d,i) { return color(i); })
            .attr('d', line);

    lines.transition()
        .ease('linear')
        .duration(500)
        .attr('d', line);

    lines.exit().remove();

    // legend
    var ticks = chart.selectAll('line')
        .data(x.ticks(num));

    ticks.enter().append('line')
            .attr('x1', x)
            .attr('x2', x)
            .attr('y1', 0)
            .attr('y2', h)
            .attr('class', 'rule');
    ticks.transition()
        .ease('linear')
        .duration(500)
        .attr('x1', x)
        .attr('x2', x)
        .attr('y1', 0)
        .attr('y2', h);
    ticks.exit().remove();
}
var dats = [data, data2, data3];
function next() {
    var it = dats.shift();
    dats.push(it);
    drawdata(it, chart);
}
setInterval(next, 2000);
next();
4

1 に答える 1

5

最近、同様の問題に直面し、パスのカスタム補間を使用して解決しました。

// Add path interpolator to d3
d3.interpolators.push(function(a, b) {
  var isPath, isArea, interpolator, ac, bc, an, bn;

  // Create a new array of a given length and fill it with the given value
  function fill(value, length) {
    return d3.range(length)
      .map(function() {
        return value;
      });
  }

  // Extract an array of coordinates from the path string
  function extractCoordinates(path) {
    return path.substr(1, path.length - (isArea ? 2 : 1)).split('L');
  }

  // Create a path from an array of coordinates
  function makePath(coordinates) {
    return 'M' + coordinates.join('L') + (isArea ? 'Z' : '');
  }

  // Buffer the smaller path with coordinates at the same position
  function bufferPath(p1, p2) {
    var d = p2.length - p1.length;

    // Paths created by d3.svg.area() wrap around such that the 'end'
    // of the path is in the middle of the list of coordinates
    if (isArea) {
      return fill(p1[0], d/2).concat(p1, fill(p1[p1.length - 1], d/2));
    } else {
      return fill(p1[0], d).concat(p1);
    }
  }

  // Regex for matching the 'd' attribute of SVG paths
  isPath = /M-?\d*\.?\d*,-?\d*\.?\d*(L-?\d*\.?\d*,-?\d*\.?\d*)*Z?/;

  if (isPath.test(a) && isPath.test(b)) {
    // A path is considered an area if it closes itself, indicated by a trailing 'Z'
    isArea = a[a.length - 1] === 'Z';
    ac = extractCoordinates(a);
    bc = extractCoordinates(b);
    an = ac.length;
    bn = bc.length;

    // Buffer the ending path if it is smaller than the first
    if (an > bn) {
      bc = bufferPath(bc, ac);
    }

    // Or, buffer the starting path if the reverse is true
    if (bn > an) {
      ac = bufferPath(ac, bc);
    }

    // Create an interpolater with the buffered paths (if both paths are of the same length,
    // the function will end up being the default string interpolator)
    interpolator = d3.interpolateString(bn > an ? makePath(ac) : a, an > bn ? makePath(bc) : b);

    // If the ending value changed, make sure the final interpolated value is correct
    return bn > an ? interpolator : function(t) {
      return t === 1 ? b : interpolator(t);
    };
  }
});

新しい補間器を使用した場合の元の要点は次のとおりです:http://bl.ocks.org/4535474

そのアプローチは、最初に長さゼロの線分を挿入することによって、小さいデータセットのパスを「バッファリング」することです。その結果、新しいセグメントは線の始点で1つのポイントから拡張され、未使用のセグメントも同様に1つのポイントに折りたたまれます。

異なるサイズのデータ​​セット間での移行は(明らかに)一般的な問題ではなく、普遍的な解決策はありません。時系列データを視覚化し、日次、週次、月次の間隔で移行していたため、視覚的な連続性を維持するために、パスの終わりに向かってセグメントが必要でした。パスの最初に同じことをしたい場合や、セグメント全体を均一にバッファリングしてパスを拡張/縮小したい場合を想像できます。どちらの方法でも同じアプローチが機能します。

于 2013-01-15T03:02:27.637 に答える