2015-12-16 8 views
10

グループ化された棒グラフを.csvファイルをネストして作成しています。グラフは折れ線グラフとしても見ることができるので、線オブジェクトに合ったネスティング構造が必要です。私の元.CSVは、次のようになります。d3グループ化された棒グラフでネストされたデータにアクセスする

Month,Actual,Forecast,Budget 
Jul-14,200000,-,74073.86651 
Aug-14,198426.57,-,155530.2499 
Sep-14,290681.62,-,220881.4631 
Oct-14,362974.9,-,314506.6437 
Nov-14,397662.09,-,382407.67 
Dec-14,512434.27,-,442192.1932 
Jan-15,511470.25,511470.25,495847.6137 
Feb-15,-,536472.5467,520849.9105 
Mar-15,-,612579.9047,596957.2684 
Apr-15,-,680936.5086,465313.8723 
May-15,-,755526.7173,739904.081 
Jun-15,-,811512.772,895890.1357 

と私のネストが、このようなものです:

d3.csv("data/net.csv", function(error, data) { 
    if (error) throw error; 

      var headers = d3.keys(data[0]).filter(function(head) { 
      return head != "Month"; 
      }); 

        data.forEach(function(d) { 
        d.month = parseDate(d.Month); 
      }); 
      var categories = headers.map(function(name) { 

       return { 
       name: name, // "name": the csv headers except month 
       values: data.map(function(d) { 
        return { 
        date: d.month, 
        rate: +(d[name]), 
        }; 
       }), 
       }; 

      }); 

私のグラフを作成するためのコードです:

var bars = svg.selectAll(".barGroup") 
     .data(data) // Select nested data and append to new svg group elements 
     .enter() 
     .append("g") 
     .attr("class", "barGroup") 
     .attr("transform", function (d) { return "translate(" + xScale(d.month) + ",0)"; }); 

    bars.selectAll("rect") 
     .data(categories) 
     .enter() 
     .append("rect") 
     .attr("width", barWidth) 
     .attr("x", function (d, i) { if (i < 2) {return 0;} else {return xScale.rangeBand()/2;}}) 
     .attr("y", function (d) { return yScale(d.rate); }) 
     .attr("height", function (d) { return h - yScale(d.rate); }) 
     .attr("class", function (d) { return lineClass(d.name); }); 

グラム要素は罰金であり、個々のバーは、x値とクラスが正しく適用された状態でマップされています。

私の問題は、バーの高さとy値の 'レート'のデータにアクセスすることになります。上記の形式では、NaNが返されます。

.data(function(d) { return d.values }) 

これは私が速度データにアクセスすることができますが、rangeBandsのそれぞれに、すべての36本のバーをマップ:私はまた、グラム要素を追加するカテゴリデータを使用して、その後でrectsを追加しようとしました。

フラットなデータ構造でもうまく動作しますが、2レベル下にネストされている場合は使用できないようです。

レートデータにアクセスするにはどうすればよいですか?

var margin = {top: 20, right: 18, bottom: 80, left: 50}, 
     w = parseInt(d3.select("#bill").style("width")) - margin.left - margin.right, 
     h = parseInt(d3.select("#bill").style("height")) - margin.top - margin.bottom; 

    var customTimeFormat = d3.time.format.multi([ 
     [".%L", function(d) { return d.getMilliseconds(); }], 
     [":%S", function(d) { return d.getSeconds(); }], 
     ["%I:%M", function(d) { return d.getMinutes(); }], 
     ["%I %p", function(d) { return d.getHours(); }], 
     ["%a %d", function(d) { return d.getDay() && d.getDate() != 1; }], 
     ["%b %d", function(d) { return d.getDate() != 1; }], 
     ["%b", function(d) { return d.getMonth(); }], 
     ["%Y", function() { return true; }] 
    ]); 


    var parseDate = d3.time.format("%b-%y").parse; 

    var displayDate = d3.time.format("%b %Y"); 

    var xScale = d3.scale.ordinal() 
     .rangeRoundBands([0, w], .1); 

    var xScale1 = d3.scale.linear() 
      .domain([0, 2]); 

    var yScale = d3.scale.linear() 
     .range([h, 0]) 
     .nice(); 

    var xAxis = d3.svg.axis() 
     .scale(xScale) 
     .tickFormat(customTimeFormat) 
     .orient("bottom"); 

    var yAxis = d3.svg.axis() 
     .scale(yScale) 
     .orient("left") 
     .innerTickSize(-w) 
     .outerTickSize(0); 

    var svg = d3.select("#svgCont") 
     .attr("width", w + margin.left + margin.right) 
     .attr("height", h + margin.top + margin.bottom) 
     .append("g") 
     .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 

    var thous = d3.format(",.0f") 

    var lineClass = d3.scale.ordinal().range(["actual", "forecast", "budget"]); 

    var tip = d3.tip() 
     .attr('class', 'd3-tip') 
     .offset([-10, 0]) 
     .html(function(d) { 
     return "<p id='date'>" + displayDate(d.date) + "</p><p id='value'>$" + thous(d.rate); 
     }) 

    d3.csv("data/net.csv", function(error, data) { 
     if (error) throw error; 

       var headers = d3.keys(data[0]).filter(function(head) { 
       return head != "Month"; 
      }); 

        data.forEach(function(d) { 
         d.month = parseDate(d.Month); 
      }); 
       var categories = headers.map(function(name) { 

       return { 
        name: name, 
        values: data.map(function(d) { 
        return { 
         date: d.month, 
         rate: +(d[name]), 
         }; 
        }), 
       }; 

       }); 

    var min = d3.min(categories, function(d) { 
         return d3.min(d.values, function(d) { 
          return d.rate; 
         }); 
        }); 



    var max = d3.max(categories, function(d) { 
         return d3.max(d.values, function(d) { 
          return d.rate; 
         }); 
        }); 

    var minY = min < 0 ? min * 1.2 : min * 0.8; 

        xScale.domain(data.map(function(d) { return d.month; })); 
        yScale.domain([minY, (max * 1.1)]); 

    var barWidth = headers.length > 2 ? xScale.rangeBand()/2 : xScale.rangeBand() ; 

    svg.call(tip); 

    svg.append("g") 
     .attr("class", "x axis") 
     .attr("transform", "translate(0," + h + ")") 
     .call(xAxis); 

    svg.append("g") 
      .attr("class", "y axis") 
      .call(yAxis); 

    var bars = svg.selectAll(".barGroup") 
      .data(data) 
      .enter() 
      .append("g") 
      .attr("class", "barGroup") 
      .attr("transform", function (d) { return "translate(" + xScale(d.month) + ",0)"; }); 

    bars.selectAll("rect") 
      .data(categories) 
      .enter() 
      .append("rect") 
      .attr("width", barWidth) 
      .attr("x", function (d, i) { if (i < 2) {return 0;} else {return xScale.rangeBand()/2;}}) 
      .attr("y", function (d) { return yScale(d.rate); }) 
      .attr("height", function (d) { return h - yScale(d.rate); }) 
      .attr("class", function (d) { return lineClass(d.name) + " bar"; }); 


    var legend = svg.selectAll(".legend") 
      .data(headers) 
      .enter() 
      .append("g") 
      .attr("class", "legend"); 

    legend.append("line") 
      .attr("class", function(d) { return lineClass(d); }) 
      .attr("x1", 0) 
      .attr("x2", 40) 
      .attr("y1", function(d, i) { return (h + 30) + (i *14); }) 
      .attr("y2", function(d, i) { return (h + 30) + (i *14); }); 

    legend.append("text") 
     .attr("x", 50) 
     .attr("y", function(d, i) { return (h + 32) + (i *14); }) 
     .text(function(d) { return d; }); 

    svg.selectAll(".bar") 
     .on('mouseover', tip.show) 
     .on('mouseout', tip.hide); 

    }); 

更新2月18日'16:シリルの要求に応じて

は、ここに完全なコードです。

私は十分にうまくやっていることを説明していないようです。チャートのラインとバーのバージョンは別々に表示されます。つまり、ユーザーは選択要素への入力に従ってどちらかを見ることができます。また、私はデータが最初にどのように入ってくるかを制御することはできません。

私はまだそれを介して働いていたとき、私はa version of exactly how it should work here.

この質問を提起しましたが、私はこの問題を解決したことがない - 私は、データの2個の別々の巣を行うための回避策を使用していました。

+0

あなたは完全なコードを投稿できますか.iはyscaleに問題があると思います。 – Cyril

+0

こんにちはシリル、 私は一瞬で完全なコードを投稿しますが、私はそれが問題のyScaleではないと確信しています。私はd.rateの代わりに数字を入れてチェックしました。また、ネストされたデータがよりフラットな形式になっているときにも機能しました。スケールは以下の通りです。 var yScale = d3.scale.linear()。range([h、0]).nice(); – tgerard

+0

@tgerard確認するには、日付をx軸、値をy軸とするグループ化棒グラフを設定しますか?その場合は、カテゴリオブジェクトをどのように使用するかは明確ではありません。提供されているコードは、d.rateのカテゴリオブジェクトを調べています。しかし、カテゴリオブジェクトは{name: "Actual"、値:[{date: ""、rate:0}]の形をしています。それで、それにはd.rateプロパティはありません。ビルドしようとしていることを確認できますか?それを解決するにはどうすればよいかアドバイスできますか? –

答えて

1

の問題は、私は信じて、あなたはこのように、バーの選択にカテゴリ配列を結合していることである。

bars.selectAll("rect").data(categories) 

私の知る限り(実行中のデモwhithout)カテゴリを見ることができるように 4つの値(各カテゴリに1つ)を持つ配列です。

ネストされたデータ構造では、「より深い」ステップを実行する必要があります。あなたはカテゴリを反復し、選択に実際の値が含まれてい配列をバインドする必要がカテゴリごとに棒のセットを描画する

ような何か:

categories.each(function (category) { 
    var klass = category.name; 
    bars.selectAll("rect ." + klass) 
     .data(category.values) 
     .enter() 
     .append("rect") 
     .attr("class", klass) 
     .attr("width", barWidth) 
     .attr("x", function (d, i) { /* omitted */}) 
     .attr("y", function (d) { return yScale(d.rate); }) 
     .attr("height", function (d) { return h - yScale(d.rate); }); 
    }); 

----代わりに上記のコードの編集

は、あなたがラインで行うと同じようにバーを描画について考えます。

var bars = svg.selectAll(".barGroup") 
    .data(categories) 
    .enter() 
    .append("g") 
    .attr("class", function (d) { return lineClass(d.name) + "Bar barGroup"; }) 
    .attr("transform", function (d, i) { 
     var x = i > 1 ? xScale.rangeBand()/2 : 0; 
     return "translate(" + x + ",0)"; 
    }) 
    .selectAll('rect') 
    .data(function (d) { return d.values; }) 
    .enter() 
    .append("rect") 
    .attr("class", "bar") 
    .attr("width", barWidth) 
    .attr("x", function (d, i) { return xScale(d.date); }) 
    .attr("y", function (d, i) { return yScale(d.rate); }) 
    .attr("height", function (d) { return h - yScale(d.rate); }); 
+0

はい、Doktornは、まさに私がやりたいことです - 構造に「深く」一歩手を伸ばしてください。この解決策はおそらく正しいでしょう。 残念なことに、このプロジェクトは少し古く、私は回避策を見つけました。そのため、私は12月にそれをテストする場所を再構築する必要があります。私は今それをすることができませんが、明日(オーストラリアの時間)それに立ち往生するでしょう。 – tgerard

+0

@Dotkomエラーを回避するために 'categories.each'を' categories.forEach'に変更しましたが、そうでなければこれを実行しました。 rangeBandsに36個の長方形をすべて追加しています。すなわち、7月の位置に36個のデータポイントのそれぞれにバーがあり、8月に36個のデータポイントがあります。 [ここにその例があります:](http://lowercasen.com/dev/tests/stackoverflowTest.html) – tgerard

4

リンクjsfiddleへ:このよう(1): https://jsfiddle.net/sladav/rLh4qwyf/1/

を私は問題の根本は、あなたが明示的にあなたの元のデータセットに存在しない2つの変数を使用したいということだと思いますカテゴリおよび(2)レート

データは、各カテゴリが独自の変数を取得し、レートの値が指定されたカテゴリの月と1つの交差点に存在するため、ワイド形式で書式設定されます。私はあなたが究極的に入れ子にしている方法は、少なくともこれに対処するべきだと思いますが、翻訳で何かが失われたかどうか、私には不明です。概念的には、達成しようとしているものと一致する組織から始める方が理にかなっていると思います。私は、元のデータを再フォーマットし、それを再び近づい - 概念レベルでのネストは簡単でシンプルなようで...

NEW COLUMNS:

  • 月:時間変数を、 X軸にマップ
  • カテゴリ:カテゴリ値[実際、予測、予算];グループ化/色に使用する
  • レート:数値; Y軸CSV再編成

にマッピングされた(NULLをドロップ):新しくフォーマットされたデータと

Month,Category,Rate 
Jul-14,Actual,200000 
Aug-14,Actual,198426.57 
Sep-14,Actual,290681.62 
Oct-14,Actual,362974.9 
Nov-14,Actual,397662.09 
Dec-14,Actual,512434.27 
Jan-15,Actual,511470.25 
Jan-15,Forecast,511470.25 
Feb-15,Forecast,536472.5467 
Mar-15,Forecast,612579.9047 
Apr-15,Forecast,680936.5086 
May-15,Forecast,755526.7173 
Jun-15,Forecast,811512.772 
Jul-14,Budget,74073.86651 
Aug-14,Budget,155530.2499 
Sep-14,Budget,220881.4631 
Oct-14,Budget,314506.6437 
Nov-14,Budget,382407.67 
Dec-14,Budget,442192.1932 
Jan-15,Budget,495847.6137 
Feb-15,Budget,520849.9105 
Mar-15,Budget,596957.2684 
Apr-15,Budget,465313.8723 
May-15,Budget,739904.081 
Jun-15,Budget,895890.1357 

は、あなたがCATEGORY変数を明示的GROUPにデータをd3.nestを使用して開始します。データは2つの層に分かれています。第1層には3つのグループ(各カテゴリに1つ)があります。 2番目の層には、各行/棒グラフのRATEデータが含まれています。データの選択範囲も入れ子にしなければなりません。最初のレイヤーは線を描画するために使用され、2番目のレイヤーはバーのために使用されます。あなたのデータをネスト

var nestedData = d3.nest() 
     .key(function(d) { return d.Category;}) 
     .entries(data) 

はあなたのグループ化された、第一階層のデータのためのSVGグループを作成します。

d3.select(".plot-space").selectAll(".g-category") 
    .data(nestedData) 
    .enter().append("g") 
    .attr("class", "g-category") 

はあなたのライン/パスを追加するには、このデータを使用します。

d3.selectAll(".g-category").append("path") 
    .attr("class", "line") 
    .attr("d", function(d){ return lineFunction(d.values);}) 
    .style("stroke", function(d) {return color(d.key);}) 

最後に、第2層にステップインしてバー/レットを追加します。

d3.selectAll(".g-category").selectAll(".bars") 
    .data(function(d) {return d.values;}) 
    .enter().append("rect") 
     .attr("class", "bar") 
     .attr("x", function(d) {return x(d.Month);}) 
     .attr("y", function(d) {return y(d.Rate);}) 
     .attr("width", 20) 
     .attr("height", function(d) {return height - y(d.Rate)}) 
     .attr("fill", function(d) {return color(d.Category)}) 

これは、グループ化されたデータを使用して線を描画し、個々のデータ点を使用してバーを描画するという、一度に1つのカテゴリを取るという簡単なアプローチです。

LAZY EDIT:

[1、nCategories]に順序尺度マッピングカテゴリを作成側によって

をカテゴリバー側を取得します。これは、動的にバー/ラインやトランジションを選択して関数を作成のいずれかのバーや線(両方ではない)

を表示するには

translate(newScale(category)*barWidth) 

のようなものでバーを相殺するために使用します/彼らの可視性/不透明度を切り替えます。ドロップダウン入力が変更され、ドロップダウン入力が関数の入力として実行されると実行されます。

+0

こんにちは、私を助けてくれてありがとう。私が持っているアプローチを取った理由はいくつかあります。 1つは、棒グラフのバージョンがグループ化された棒グラフであるため、カテゴリに入れ子にする必要があります。もう1つは、元のデータ形式を制御できないことです。クライアントからのものです。 正確にどのように表示され、機能するかに関するリンクを含む質問への更新を掲載しました。しかし、結果を達成するために2つの別々のデータネストを使用したため、この質問はまだ適切です。これは私にとっては非効率的です。 – tgerard

+0

@tgerardあなたの作業バージョンは素晴らしいようです!私はあなたが探していた余分な "機能"を得るために必要なものをどのように拡張することができるかを説明するために、私の答えを編集しました。あなたのアプローチの「非効率性」に関する限り、元のデータの組織では必要かもしれないと思います。あなたはjavascriptで再フォーマットしてから自分のものに従うことができますが、その時点では、あなたが持っているものに固執するほうが意味があります。 –

+0

はい、私は私の元のアプローチに固執します。最初にこの質問を投稿したとき、私はそれよりも優れたコーディングスキルを持つ人には簡単に答えることができると思っていましたが、実際にはかなり難しく、2つの巣を持つことを恐れるべきではないという結論に達しました。それに亀裂を与えてくれてありがとう。 – tgerard

関連する問題