2014-02-28 1 views
1

d3.js를 사용하여 초점 및 컨텍스트 막대 그래프를 만들었지 만 완벽하게 작동하지만 영역을 축소하면 그래프의 그룹화 값을 표시하려고합니다 .초점 및 컨텍스트 그래프에 대한 데이터 그룹화 - d3.js

아래 스크린 샷에서 볼 수 있듯이 값 1의 막대가 두 개 있지만 데이터를 축소하여 한 달 동안 표시 할 때 값 1 인 막대가 하나만 표시됩니다.

데이터를 축소 할 때 두 개가 표시되도록 그룹화하려고합니다. 어떤 도움을 주셔서 감사합니다. enter image description here

나는 두 개의 파일이 있습니다 기능

defaults: { 
     margin: {top: 10, right: 20, bottom: 100, left: 40}, 
     margin2: {top: 425, right: 20, bottom: 30, left: 300}, 
    }, 
    onRender: function() { 
     var that = this; 
     //Set up graph parameters 
     var margin = this.options.margin; 
     var margin2 = this.options.margin2; 
     this.height = (this.options.height)? this.options.height - margin.top - margin.bottom: 960 - margin.top - margin.bottom, 
     this.width = (this.options.width)? this.options.width - margin.left - margin.right: 500 - margin.left - margin.right, 
     this.height2 = (this.options.height)? this.options.height - margin2.top - margin2.bottom : 500 - margin2.top - margin2.bottom, 
     this.width2 = this.width * .5; 

     //Set up ranges (a scaling factor to map input data to output in pixels) 
     this.scales = { 
      x: this.getXScale(), 
      x2: this.getX2Scale(), 
      y: this.getYScale(), 
      y2: this.getY2Scale() 
     }; 

     //Set up and define graph content 
     //----------axis---------- 
     this.renderAxes(); 

     //Setup groups to organize layout, brush areas and perform clipping 
     //----------groups---------- 
     this.svg = d3.select(this.el).append("svg") 
      .attr("class","FCBChart") 
      .attr("width", this.width + margin.left + margin.right) 
      .attr("height", this.height + margin.top + margin.bottom) 
      .attr("viewBox", "0 0 " + (this.width + margin.left + margin.right) + " " + (this.height + margin.top + margin.bottom)) 
      .attr("preserveAspectRatio", "xMidYMid meet"); 
     this.svg.append("defs").append("clipPath") 
      .attr("id", "clip") 
      .append("rect") 
      .attr("width", this.width + margin.left + margin.right) 
      .attr("height", this.height + margin.top + margin.bottom); 
     var aspect = (this.width + margin.left + margin.right)/(this.height + margin.top + margin.bottom); 
     $(window).on("resize", function() { 
      var targetWidth = $(".FCBChart").parent().width(); 
      $(".FCBChart").attr("width", targetWidth); 
      $(".FCBChart").attr("height", targetWidth/aspect); 
     }); 

     this.focus = this.svg.append("g") 
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")") 
      .attr("class","focusGraph"); 
     // barsGroup is for making FCB graph like Bar graph 
     this.barsGroup = this.focus.append("g") 
      .attr('clip-path', 'url(#clip)'); 

     this.context = this.svg.append("g") 
      .attr("class","contextGraph") 
      .attr("transform", "translate(" + margin2.left + "," + margin2.top + ")"); 

     this.setupBrush(); 
     this.renderData(); 
     return this; 

    } 

2.Actual 파일 : 1. 자료 파일 내가 API 호출을 사용하고 그래프로 제공

initialize : function(option){ 
     this.options = $.extend(true, {}, option,this.defaults); 
    }, 
    events : { 
    }, 

    //Set up ranges (a scaling factor to map input data to output in pixels) 
    getXScale : function(){ 
     return d3.time.scale().range([0, this.width]) 
    }, 
    getX2Scale : function(){ 
     return d3.time.scale().range([0, this.width2]) 
    }, 
    getYScale : function(){ 
     return d3.scale.linear().range([this.height, 0]) 
    }, 
    getY2Scale : function(){ 
     return d3.scale.linear().range([this.height2, 0]) 
    }, 

    //Set up and define graph content 
    //----------axis---------- 
    renderAxes : function(){ 
     var that = this; 
     this.xAxis = d3.svg.axis().scale(this.scales.x).orient("bottom"), 
     this.xAxis2 = d3.svg.axis().scale(this.scales.x2).orient("bottom"), 
     this.yAxis = d3.svg.axis().scale(this.scales.y).orient("left"); 

    //----------area fill---------- 
     this.area = d3.svg.area() 
      .x(function(d) { 
       var that1 = that; 
       return that1.scales.x(d.x); }) 
      .y0(this.height) 
      .y1(function(d) { 
       var that1 = that; 
      return that1.scales.y(d.y); }); 
    }, 

    //----------Setup brush----------------------- 
    setupBrush : function(){ 
     var that = this; 
     this.brush = d3.svg.brush() 
      .x(this.scales.x2) 
      .on("brush", function(){ 
       that.brushed(this,that); 
      }); 
    }, 
    brushed : function(d3This, view) { 
     var that = view; 
     that.scales.x.domain(that.brush.empty() ? that.scales.x2.domain() : that.brush.extent()); 
     //For FCB with bar chart 
     that.focusGraph.attr("x", function(d, i) { return that.scales.x(d.XPoint); }); 
     that.focusGraph.attr("width", 20); 
     that.focus.select(".x.axis").call(that.xAxis); 
    }, 


    renderData : function(){ 
     var that = this; 
     var x = this.scales.x, 
     y = this.scales.y, 
     x2 = this.scales.x2, 
     y2 = this.scales.y2, 
     data = this.options.data; 

     data.forEach(function(d) { 
      d.XPoint = d.x; 
      d.values = +d.y; 
     }); 

     // Scale the range of the data 
     x.domain(d3.extent(data, (function(d) { return d.XPoint; }))); 
     y.domain([0, d3.max(data, (function(d) { return d.values; }))]); 

     // Scale the range of the data in context graph too 
     x2.domain(x.domain()); 
     y2.domain(y.domain()); 

     // Add the area graph to focus and context 

     //To get bar chart in FCB 
     this.focusGraph = this.barsGroup.selectAll("rect") 
      .data(data) 
     .enter().append("rect") 
      .attr("x", function(d, i) { return x(d.XPoint); }) 
      .attr("y", function(d) { return y(d.values); }) 
      .attr("width", 5) 
      .attr("height", function(d) { return that.height - y(d.values); }) 
      .style("fill", "steelblue"); 

     this.context.selectAll("rect") 
      .data(data) 
     .enter().append("rect") 
      .attr("x", function(d, i) { return x2(d.XPoint); }) 
      .attr("y", function(d) { return y2(d.values); }) 
      .attr("width", 5) 
      .attr("height", function(d) { return that.height2 - y2(d.values); }) 
      .style("fill", "steelblue"); 


     //Functions called after all json datasets loaded 
     var x0 = x.copy(); 

     //Axis 
     this.focus.append("g") 
      .attr("class", "x axis") 
      .attr("transform", "translate(0," + this.height + ")") 
      .call(this.xAxis); 

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

     this.context.append("g") 
      .attr("class", "x axis") 
      .attr("transform", "translate(0," + this.height2 + ")") 
      .call(this.xAxis2); 

     this.context.append("g") 
      .attr("class", "x brush") 
      .attr("clip-path", "url(#clip)") 
      .call(this.brush) 
     .selectAll("rect") 
      .attr("y", 0) 
      .attr("height", this.height2); 

     this.context.append("text") 
      .attr("class","instructions") 
      .attr('x',"345") 
      .attr('y','70') 
      // .attr("transform", "translate(0," + (this.height2 + 25) + ")") 
      .text('*Click and drag above to zoom/pan the data'); 
    }, 

데이터, 표시된 이미지는 아래에 표시된 것과 같습니다.

enter image description here

우리가 볼 수 있듯이, 2 월 28 일에 총 3 카운트가 있지만 그래프를 축소하고 아래 그림과 같이 주 데이터를 분석하려고 할 때 2 월 28 일의 카운트는 여전히 1로 표시됩니다. 사실은 3

enter image description here

+0

코드를 표시하면 도움이됩니다. –

+0

@LarsKotthoff 내 상황을 제대로 이해하는 데 도움이 될 수있는 코드를 추가했습니다. – shahsank3t

+0

흠, 컨텍스트 차트에 막대를 결합하는 것이 보이지 않습니다. 두 개의 막대가있을 수 있지만 너무 가깝기 때문에 막대처럼 보입니다. –

답변

1

내 생각은 무엇 일어나는 것은 축소 할 때 두 개의 막대도 렌더링된다는 점이다 표시되어야합니다. 그들은 서로의 위에 직접적으로 있거나 아니면 아주 가까이 있습니다. 당신이 원하는 것은 비닝이라고하는 것입니다. Binning은 데이터를 가져 와서 데이터의 출현 빈도를 기준으로 데이터를 저장하는 곳입니다.

이것을 구현하려면 간단한 비닝 함수를 추가하고 확대/축소 할 때마다 호출해야합니다. 함 이제 가을 데이터 포인트의 합을 나타내는 것

var myDomain = zoomedScale.domain(), 
    binSize = (myDomain[1] - myDomain[0])/20, 
    bins = [], 
    binIndex; 

for(var i = 0; i < 20; i++){ 
    bins.push(0); 
} 

for(var i = 0; i < myData.length; i++){ 
    binIndex = Math.floor((myData[i].value - myDomain[0])/binSize); 

    if(binIndex === 20){ 
     binIndex[19] += myData[i].value; 
    } else { 
     bins[binIndex] += myData[i].value; 
    } 
} 

: 가정 당신은 다음과 같이 보일 것입니다, 당신은 20 개 쓰레기통에 데이터를 분할하려는 당신의 확대 도메인을 일치하도록 규모를 조정하고 있음 각 빈에. 그런 다음 플롯을 업데이트하여 원시 데이터가 아닌 저장소를 그릴 수 있습니다.

+0

나는 비닝에 관해 제안한 것을 시도했지만 문제를 푸는데 도움이되지 못했습니다. 적절한 데이터와 이미지로 내 질문을 편집 했으므로 내가하고 싶은 것을 당신과 다른 사람들에게 더 분명하게 알릴 수 있습니다. 감사. – shahsank3t

+1

binning의 경우 시간 기준으로 bin해야합니다. 당신의 경우, 당신은 아마 그 줌 레벨을 위해 매 30 분과 같은 것을 비우고 싶을 것입니다. 30 분 세그먼트에서 발생하는 모든 값을 합산하고 해당 함수를 사용하여 새로운 데이터 배열을 생성하는 함수를 작성해야합니다. – ckersch