24

d3.stackを使用して積み上げ面グラフを作成していますが、各レイヤーに同じ数のアイテムがない場合はエラーが発生します。私は次のようなデータの配列から始めています:

[  
   {key:'Group1',value,date},  
   {key:'Group1',value,date},  
   {key:'Group1',value,date},  
   {key:'Group2',value,date},  
   {key:'Group2',value,date}  
]

そして、nest()とstack()を実行した後、予想どおり、この形式になります。

[  
   {key: 'Group1',  
    values: [ {key,value,date}, {key,value,date}, {key,value,date} ] },  
   {key: 'Group2',  
    values: [ {key,value,date}, {key,value,date} ]  }  
]

このjsFiddleの問題を示すために、スタック領域のサンプルを少し変更しました:http: //jsfiddle.net/brentkeller/rTC3c/2/

sourceData配列のデータポイントのいずれかを削除すると、コンソールに「Undefinedのプロパティ「1」を読み取れません」というエラーメッセージが表示されます。

d3.stackに、欠落しているデータポイントの値をゼロと見なす方法はありますか?そうでない場合は、不足している値を埋めるための洗練されたソリューションはありますか?

4

3 に答える 3

17

これはd3固有ではなく、キーデータの配列のギャップを埋めるための一般的なソリューションです。ここでjsfiddleを次の関数で変更しました。

function assignDefaultValues( dataset )
{
    var defaultValue = 0;
    var keys = [ 'Group1' , 'Group2', 'Group3' ];
    var hadData = [ true, true, true];
    var newData = [];
    var previousdate = new Date();
    var sortByDate = function(a,b){ return a.date > b.date ? 1 : -1; };

    dataset.sort(sortByDate);
    dataset.forEach(function(row){
        if(row.date.valueOf() !== previousdate.valueOf()){
            for(var i = 0 ; i < keys.length ; ++i){
                if(hadData[i] === false){
                    newData.push( { key: keys[i], 
                                   value: defaultValue, 
                                   date: previousdate });
                }
                hadData[i] = false;
            }
            previousdate = row.date;
        }
        hadData[keys.indexOf(row.key)] = true; 
    });
    for( i = 0 ; i < keys.length ; ++i){
        if(hadData[i] === false){
            newData.push( { key: keys[i], value: defaultValue, 
                            date: previousdate });
        }
    }
    return dataset.concat(newData).sort(sortByDate);
}

指定されたデータセットをウォークスルーし、新しい値に遭遇するたびに、まだ表示されていないデータセットdateにデフォルト値を割り当てます。keys

于 2013-02-24T04:01:04.587 に答える
5

スタックは、実際にグラフをスタックするということを実行するため、ユーザーはデータを正しい形式で提供する責任があります。スタックは基本的にデータ形式にとらわれないため、これは考えてみれば理にかなっています。これは、レイヤーごとに同じ数のポイントにアクセスできるという唯一の制限により、非常に高い柔軟性を提供します。どのポイントが欠けているかをどのように判断しますか? 第 1 層に 5 点、第 2 層に 10 点があるとすると、第 1 層は 5 点不足ですか? または、3 番目のレイヤーにさらに多くのポイントが含まれているため、両方のレイヤーにポイントがありません。そして、ポイントが不足している場合、どのポイントですか? 最初、最後、中間のどこか?繰り返しになりますが、スタック実装がこれを理解する賢明な方法はありません (非常に厳密なデータ構造を強制しない限り)。

それで、しかし、あなたができることは何もありませんか?できると思います。完全な実装を提供することはできませんが、正しい方向への指針を示すことはできます。ここから始めます。

var stack = d3.layout.stack()
  .offset("zero")
  .values(function(d) { return d.values; })

ここでは値を返すだけで、例ではネスト演算子の結果になります。したがって、この時点で、値を「修正」することができます。

最初に行う必要があるのは、観測の最大数を決定することです。

var nested = nest.entries(data);
var max = nested.reduce(function(prev, cur) {
  return Math.max(prev, cur.values.length);
}, 0);

今、トリッキーな部分です。要素の最大数がわかったら、値に渡される関数を調整する必要があります。ここでは、データに対して仮定を立てる必要があります。あなたの質問から、一部のグループの値が欠落していることを理解しています。したがって、2 つの可能性があります。最大数の要素を持つグループに範囲内のすべてのアイテムが含まれていると想定するか、特定の範囲を想定して、範囲内の各「目盛り」の値が含まれているかどうかすべてのグループをチェックします。したがって、範囲が(例のように)日付範囲であり、毎日(またはその間隔で)測定を期待する場合は、グループ内のアイテムを調べてギャップを自分で埋める必要があります。数値範囲の(テストされていない)例を挙げてみます。

// define some calculated values that can be reused in correctedValues
var range = [0, 1];
var step = 0.1;

function correctedValues(d) {
  var values = d.values;
  var result = [];
  var expected = 0;
  for (var i = 0; i < values.length; ++i) {
     var value = values[i];
     // Add null-entries
     while (value.x > expected) {
       result.push({x: expected, otherproperties_you_need... });
       expected += step;
     }
     result.push(value); // Now add the real data point.
     expected = value.x;
  }

  // Fill up the end of of the array if needed
  while(expected < range[1]) {
    result.push({x: expected, otherproperties_you_need... });
    expected += step;
  }
  return result;
}

// Now use our costom function for the stack
var stack = d3.layout.stack()
 .offset("zero")
 .values(correctedValues)
...

前述のように、この部分はテストされておらず、問題を直接解決するものではありませんが (数値範囲を使用しているため)、問題を解決する方法 (および問題の実際の原因は何か) についてのアイデアが得られるはずです)。

于 2013-02-21T17:23:06.900 に答える
2

他の人が説明したように、値を補間する方法は非常に多く、明白な選択肢がないため、積み上げグラフが各データ ポイントの欠損値を推測するのは不合理です。

ただし、d3.svg.line()独自の補間方法を選択して欠損値を埋めるための合理的な方法を提供しているようです。これは SVG パスを生成するために設計されていますが、一般的な行を定義するために適応させることができます。補間方法は、ここで提案されています。

https://github.com/mbostock/d3/wiki/SVG-Shapes#wiki-line_interpolate

残念なことに、クラスには今のところ、これらすべての素晴らしい補間メソッド (d3 の他の場所には表示されない) がありますが、任意の中間値ではなく SVG パス データの生成に制限されています。おそらく @mbostock がこれを見れば、機能の一般化を検討するでしょう。

line(data)ただし、今のところ、d3 のフォークを作成し、SVG 文字列に書き込まれる前の中間結果を取得したい場合があります。これは、以下の補間を行うソースpathの部分です。

  function line(data) {
    var segments = [],
        points = [],
        i = -1,
        n = data.length,
        d,
        fx = d3_functor(x),
        fy = d3_functor(y);

    function segment() {
      segments.push("M", interpolate(projection(points), tension));
    }

    while (++i < n) {
      if (defined.call(this, d = data[i], i)) {
        points.push([+fx.call(this, d, i), +fy.call(this, d, i)]);
      } else if (points.length) {
        segment();
        points = [];
      }
    }

    if (points.length) segment();

    return segments.length ? segments.join("") : null;
  }
于 2013-02-21T20:51:57.350 に答える