如何使用从csv文件过滤的数据创建多重系列折线图?

时间:2018-01-15 14:01:58

标签: javascript d3.js data-visualization linechart

我这样做了一个data.csv文件:

CODE,YEAR,MODALITY,VALUE
AB,2000,first,15
AB,2000,second,
AB,2000,third,33
AB,2001,first,20
AB,2001,second,25
AB,2001,third,87
AB,2002,first,6
AB,2002,second,
AB,2002,third,16
AB,2003,first,50
AB,2003,second,50
AB,2003,third,10
AB,2004,first,20
AB,2004,second,55
AB,2004,third,8
AC,2000,first,
AC,2000,second,97
AC,2000,third,77
AC,2001,first,42
AC,2001,second,5
AC,2001,third,81
AC,2002,first,
AC,2002,second,63
AC,2002,third,14
AC,2003,first,5
AC,2003,second,7
AC,2003,third,0
AC,2004,first,5
AC,2004,second,7
AC,2004,third,0
AD,2000,first,11
AD,2000,second,2
AD,2000,third,36
AD,2001,first,95
AD,2001,second,78
AD,2001,third,88
AD,2002,first,89
AD,2002,second,32
AD,2002,third,79
AD,2003,first,5
AD,2003,second,32
AD,2003,third,9
AD,2004,first,7
AD,2004,second,32
AD,2004,third,91
AE,2000,first,15
AE,2000,second,78
AE,2000,third,1
AE,2001,first,5
AE,2001,second,2
AE,2001,third,64
AE,2002,first,44
AE,2002,second,51
AE,2002,third,
AE,2003,first,40
AE,2003,second,52
AE,2003,third,85
AE,2004,first,45
AE,2004,second,50
AE,2004,third,80

我创建了一个由一些用户可选元素组成的index.html文件(在示例中,为简单起见,我选择了4个代码为 AB AC 的圈子, AD AE )和3个单选按钮(第一个第二个第三个​​

用户可以选择一个或多个圈子,也可以选择一个单选按钮。

所以我有一个数组codes,其中包含所选圈子的代码以及包含所选单选按钮的modalitySelected变量。

我想要做的是一个折线图,它根据用户的选择来表示数据。

实施例: enter image description here

每个所选代码都有一行,其值与所选单选按钮对应。

在这个例子中有一些缺失数据,虚线就是这样。 (目前这并不重要)。

这是我的代码。最初管理圆圈的选择并创建包含所选元素的代码的codes数组。 然后创建折线图。

的index.html

<body>
   <div id="circles">
      <svg>
         <circle id="AB" cx="10" cy="10" r="10" fill="purple" />
         <circle id="AC" cx="60" cy="60" r="5" fill="red" />
         <circle id="AD" cx="110" cy="110" r="15" fill="orange" />
         <circle id="AE" cx="90" cy="50" r="7" fill="yellow" />
      </svg>
   </div>
   <button type="button" id="finalSelection">Finish</button>

   <span style="display:block;margin-top: 10px;">Selected codes: <span class="values"></span></span><br>

   <div id="modality-selector-container">
      <form id="modality-selector">
         <input type="radio" name="modality-selector" id="rb-first" value="first" checked />
         <label for="rb-first">First</label>
         <input type="radio" name="modality-selector" id="rb-second" value="second" />
         <label for="rb-second">Second</label>
         <input type="radio" name="modality-selector" id="rb-third" value="third" />
         <label for="rb-third">Third</label>
      </form>
   </div>

   <div id="line-chart-container"></div>
   <script src="./script.js"></script>
</body>

的script.js

var codes = [];
modalitySelected = document.querySelector('input[name=modality-selector]:checked').value; 
var filtered_data = null;

// set the dimensions and margins of the graph
var margin = {top: 20, right: 20, bottom: 30, left: 50};
var width = 600 - margin.left - margin.right;
var height = 400 - margin.top - margin.bottom;

// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]); 

// parse the date/time
var parseTime = d3.timeParse("%Y"); 

var svg = null;
var valueline = null;

d3.selectAll('#circles svg circle').on('click', function() {
   var id = d3.select(this).attr('id');

   if(d3.select(this).classed('clicked')) { 
      d3.select(this).classed('clicked', false).style('stroke', null);
      codes.splice(codes.indexOf(id), 1); 
   } 
   else { 
      if(codes.length) { 
         if(d3.event.ctrlKey) { 
            d3.select(this).classed('clicked', true).style('stroke', 'blue');
            codes.push(id); 
         }
         else { 
            d3.selectAll(".clicked").classed('clicked', false).style('stroke', null);
            codes = [];
            d3.select(this).classed('clicked', true).style('stroke', 'blue');
            codes.push(id); 
         }
      } 
      else {
         d3.select(this).classed('clicked', true).style('stroke', 'blue');
         codes.push(id);
      }
   }

   $('span.values').html(codes.join(', '));
});

$('button#finalSelection').click(function() {
   $('span.values').html(codes.join(', '));
   console.log("compare: " + codes);
   compareCodes();
});

function compareCodes() {
   // define the line
   valueline = d3.line()
      .x(function(d) {
         return x(d.YEAR); 
      })
      .y(function(d) {
         return y(d.VALUE);  
      });  

   // append the svg obgect to the body of the page
   // appends a 'group' element to 'svg'
   // moves the 'group' element to the top left margin
   svg = d3.select("#line-chart-container").append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
      .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

   getData(); 
}

function getData() {
   d3.queue()
      .defer(d3.csv, './data.csv') 
      .await(makeLineChart); 
}

function makeLineChart(error, data) {
   if(error) {
      console.log(error);
   }

   // radio button change
   d3.selectAll("input[name='modality-selector']")
      .on("change", function(){
         console.log(this.value);
         modalitySelected = this.value;

         // filter data
         filtered_data = data.filter(function(d) {
            return d.MODALITY == modalitySelected && d.CODE == codes[0];
         });

         // format the data
         filtered_data.forEach(function(d) {
            d.YEAR = parseTime(d.YEAR);
            d.VALUE = +d.VALUE;
         });

         updateGraph(filtered_data);
      }); // end radio on change

   // generate initial line chart - filter data
   filtered_data = data.filter(function(d) {
      return d.MODALITY == modalitySelected && d.CODE == codes[0];
   });
   updateGraph(filtered_data);
}

function updateGraph(data) {
   var numTickXaxis = data.length;

   // scale the range of the data
   x.domain(d3.extent(filtered_data, function(d) { 
      return d.YEAR; 
   }));
   y.domain([0, d3.max(filtered_data, function(d) { 
      return d.VALUE; 
   })]);

   // add the valueline path
   svg.append("path")
      .data([filtered_data])
      .attr("class", "line")
      .attr("d", valueline);

   // add the X Axis
   svg.append("g")
      .attr("class", "axis")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x)
         .tickFormat(d3.timeFormat("%Y"))
         .ticks(numTickXaxis))
      .selectAll("text")   
         .style("text-anchor", "end")
         .attr("dx", "-.8em")
         .attr("dy", ".15em")
         .attr("transform", "rotate(-65)");

   // add the Y Axis
   svg.append("g")
      .attr("class", "axis")
      .call(d3.axisLeft(y)); 

   var state = svg.selectAll(".line");
   state.exit().remove();
}

完整代码为HERE

在此代码中有两个问题:

  1. 更改单选按钮选择时,图形将被覆盖(尽管存在state.exit().remove();
  2. 如您所见,该代码仅适用于codes中的第一个元素。我不知道如何处理codes由多个元素组成并显示更多行的情况。
  3. 我可以考虑修改data.csv保持相同内容的结构的想法。 例如,我可以像这样编辑文件:

    CODE,YEAR,FIRST,SECOND,THIRD
    AB,2000,15,50,33
    AB,2001,20,25,87
    AB,2002,6,,16
    AB,2003,50,50,10
    AB,2004,20,55,8
    AC,2000,,97,77
    AC,2001,42,5,81
    AC,2002,,63,14
    AC,2003,5,7,0
    AC,2004,5,7,0
    AD,2000,11,2,36
    AD,2001,95,78,88
    AD,2002,89,32,79
    AD,2003,5,32,9
    AD,2004,7,32,91
    AE,2000,15,78,1
    AE,2001,5,2,64
    AE,2002,44,51,
    AE,2003,40,52,85
    AE,2004,45,50,80
    

    有谁知道如何帮助我?谢谢!

    this example之后,我能够解决问题。

    HERE代码。

    现在我只有一个小的(我希望)图形问题。 在这张图片中,我们可以清楚地看到问题所在。

    enter image description here

    线条在轴之前开始。为什么?

1 个答案:

答案 0 :(得分:1)

问题在于"transform", "translate(" createAxis()将您的轴移到一边。

所以我添加了

.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

到原始svg变量,使其与轴对齐并删除

  .attr("transform", "translate(" + 0 + "," + 0 + ")");

来自createAxis()

这里是链接:Plunker