【问题标题】:How to create a multiseries line chart using data filtered from a csv file?如何使用从 csv 文件过滤的数据创建多系列折线图?
【发布时间】:2018-01-15 14:01:58
【问题描述】:

我有一个这样完成的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 个圆圈,代码为 ABACADAE)和 3 个单选按钮(firstsecondthird)。

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

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

我想做的是一个折线图,它代表基于用户所做选择的数据。

示例:

每个选定的代码都有一行,值是与选定的单选按钮对应的值。

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

这是我的代码。最初管理圆的选择并创建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由多个元素组成,显示更多行的情况,我不知道如何处理。

我可以考虑修改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 代码。

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

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

【问题讨论】:

  • 我有一个新的解决方案,但是 csv 看起来像 this,这样可以吗?
  • @RobertAndersson 不,所以不好。实际上,可能的代码真的很多(> 500),具有这种结构的文件在我看来很难管理。
  • @RobertAndersson 我很高兴,因为我终于解决了。我只有一个小问题,你能帮帮我吗?我已经更新了主要信息。
  • 请删除您之前的问题

标签: javascript d3.js data-visualization linechart


【解决方案1】:

问题是您的createAxis() 中的"transform", "translate(" 将您的轴移到了一边。

所以我加了

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

到您原来的svg 变量,使其与轴对齐并删除

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

来自createAxis()

这是链接:Plunker

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多