带有单选按钮和多个系列

时间:2018-01-13 20:36:21

标签: javascript d3.js data-visualization linechart

我想创建一个line chart like this,区别在于有单选按钮而不是下拉选择器。

这是我的 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
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
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
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,

正如您所看到的,该文件缺少数据,系列不完整但可能有“漏洞”,其中未定义VALUE

通过界面(这里我没有显示它,因为它没用),用户可以选择他想要的CODEs。目的是为每个选定的CODE创建一条线。这些代码保存在codes数组中,该数组可以具有可变长度(可以包含一个或多个元素)。

此外,还有一些单选按钮,用户可以使用它们来选择MODALITY

因此,必须针对MODALITY(单选按钮)和CODE(代码数组)过滤要显示的数据。

这是我的代码:

的index.html

<body>
   <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>

</html>

的script.js

// variable array of codes. It can contain one or more codes, depending on the user
var codes = ['AE', 'AB'];
console.log("Codes: " + codes);
modalitySelected = document.querySelector('input[name=modality-selector]:checked').value; 

// 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;

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

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

// define the line
var 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
var 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 + ")");

d3.queue()
   .defer(d3.csv, './data.csv') 
   .await(makeLineChart); 

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

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

   var numTickXaxis = filtered_data.length;

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

   // 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));
} 

的style.css

#modality-selector-container {
    background-color: lightgreen;
}

#line-chart-container {
    background-color: lightblue;
}

#line-chart-container svg {
    border: solid 1px black;
}

path.line {
    fill: none;
    stroke: #000;
}

Here我部分工作的Plunker。

如您所见,代码仅适用于一组值(在本例中,我选择了AE代码,即数组中的第一个代码)。 但它应该显示两行,一行代表AE代码,另一行代表AB代码。 我不知道如何做到这一点,因为它必须是自适应的,因为数组代码可以在用户的​​意愿长。

另外,当单选按钮更改时,我无法管理更新图表。 我试图按照示例中的代码进行操作,但结果不是很好。

的script.js

// variable array of codes. It can contain one or more codes, depending on the user
var codes = ['AE', 'AB']; 
console.log("Codes: " + 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;

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

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

// define the line
var 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
var 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 + ")");

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 Plunker。

有三个问题:

  1. 在默认选择的情况下(因此第一次加载图表时),折线图未正确构建

  2. 更改单选按钮选择时,图表将被覆盖(尽管存在state.exit().remove();

  3. 当我第二次选择相同的单选按钮时,错误:属性d:预期的数字,“MNaN,0LNaN,341.02 ......”错误出现在控制台中(例如,在开始时选择 First ,我选择 Second ,我选择 Third ,我选择 Second - &gt;误差)。

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

    更新1

    正如RobertAndersson所说,我可以考虑以这种方式保存数据(我认为实际上更聪明)。

    data.csv

    CODE,YEAR,FIRST,SECOND,THIRD
    AB,2000,15,,33
    AB,2001,20,25,87
    AB,2002,6,,16
    AC,2000,,97,77
    AC,2001,42,5,81
    AC,2002,,63,14
    AD,2000,11,2,36
    AD,2001,95,78,88
    AD,2002,89,32,79
    AE,2000,15,78,1
    AE,2001,5,2,64
    AE,2002,44,51,
    

    在这种情况下,我该怎么办?

    更新2

    我希望能更好地解释我的需要。

    我需要的是,使用单选按钮选择(值为第一,第二或第三)和一组数值AB,AC,AD,AE可以在数量和组合方面有所不同。

    这是一个例子。

    在上半部分,用户可以选择一个或多个圆圈(它们是AB,AC,AD和AE代码)。然后单击Finish,创建并打印数组codes

    然后用户可以选择所需的单选按钮(第一,第二或第三)。

    根据这些选择,系统应从csv文件中检索正确的数据并创建折线图。

    例如: enter image description here

    虚线表示数据丢失。

    我已更新文件。

    的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

    /*****************************************************************************************/
    /* CODE FOR SELECT ELEMENTS.
    /*****************************************************************************************/
    
    var codes = [];
    
    d3.selectAll('#circles svg circle').on('click', function() {
       // fetch ID
       var id = d3.select(this).attr('id');
    
       // toggle "clicked" class and push/splice id within the codes array accordingly based on event.metaKey (CTRL)
       if(d3.select(this).classed('clicked')) { // classed add/remove a CSS class from the selection
          d3.select(this).classed('clicked', false).style('stroke', null);
          codes.splice(codes.indexOf(id), 1); // the splice() method adds/removes items to/from an array, and returns the removed item(s)
       } 
       else { // if an item has never been selected
          if(codes.length) { // if the array codes is not empty
             if(d3.event.ctrlKey) { // if CTRL is pressed
                d3.select(this).classed('clicked', true).style('stroke', 'blue');
                codes.push(id); 
             }
             else { // if the item I'm selecting has never been selected and before I had already selected other elements
                // I "remove" all those already selected
                d3.selectAll(".clicked").classed('clicked', false).style('stroke', null);
                codes = [];
                // I consider selected the one actually selected
                d3.select(this).classed('clicked', true).style('stroke', 'blue');
                codes.push(id); 
             }
          } 
          else { // if the array codes is empty
             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);
    });
    
    /*****************************************************************************************/
    /* CODE FOR LINE CHART.
    /*****************************************************************************************/
    
    var parseTime = d3.timeParse("%Y");
    
    // ...
    

    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
    

    Here the plunker

0 个答案:

没有答案