【问题标题】:How do I create a multi-line chart in d3 with a dropdown menu?如何在 d3 中使用下拉菜单创建多折线图?
【发布时间】:2021-07-20 01:48:44
【问题描述】:

我之前发布了一个可能过于具体的问题。我正在尝试在 d3 中创建一个带有下拉菜单的多折线图,类似于this

我已经取消了 v7 所需的明显更改,但仍然遇到麻烦,我相信var initialGraph 之后的 if/else 语句,但我不是 100% 确定。也可能是因为我的 d3.groups 没有正确设置/引用。

我收到的当前错误是:

Uncaught (in promise) TypeError: Cannot read property 'year' of undefined
    at <anonymous>:23:33
    at a (d3.v7.min.js:2)
    at initialGraph (<anonymous>:83:18)
    at <anonymous>:89:3

我的数据集有四个值:年份、状态、wvalues 和 lvalues。 state 和 lvalues 是字符串,year 和 wvalues 是数字。到目前为止,这是我的代码:

  var margin = { top: 50, right: 50, bottom: 50, left: 50 }
  var h = 500 - margin.top - margin.bottom
  var w = 700 - margin.left - margin.right
  var formatDecimal = d3.format('.2')

d3.csv('15/data.csv').then(function (data) {

// Scales
var x = d3.scaleLinear()
  .range([0,w])
var y = d3.scaleLinear()
  .range([h,0])

  y.domain([
    d3.min([0,d3.min(data,function (d) { return d.wvalue })]),
    d3.max([0,d3.max(data,function (d) { return d.wvalue })])
    ]);
  x.domain([1968, 2016])
  
// Define the line
var valueLine = d3.line()
    .x(function(d) { return x(d.year); })
    .y(function(d) { return y(d.wvalue); })

// Create the svg canvas in the "d3block" div
var svg = d3.select("#d3block")
        .append("svg")
        .style("width", w + margin.left + margin.right + "px")
        .style("height", h + margin.top + margin.bottom + "px")
        .attr("width", w + margin.left + margin.right)
        .attr("height", h + margin.top + margin.bottom)
        .append("g")
        .attr("transform","translate(" + margin.left + "," + margin.top + ")")
        .attr("class", "svg");

//nest variable
var nest = d3.groups(data, 
  d => d.state, d => d.lvalue)

  // X-axis
  var xAxis = d3.axisBottom()
    .scale(x)
    .tickFormat(formatDecimal)
    .ticks(7)
  // Y-axis
  var yAxis = d3.axisLeft()
    .scale(y)
    .tickFormat(formatDecimal)
    .ticks(5)
    // Create a dropdown
    var legisMenu = d3.select("#legisDropdown")

    legisMenu
    .append("select")
    .selectAll("option")
        .data(nest)
        .enter()
        .append("option")
        .attr("value", ([key, ]) => key)
        .text(([key, ]) => key)

  // Function to create the initial graph
  var initialGraph = function(legis){

    // Filter the data to include only state of interest
    var selectLegis = nest.filter(([key, ]) => key == legis)

      var selectLegisGroups = svg.selectAll(".legisGroups")
        .data(selectLegis, function(d){
          return d ? d.key : this.key;
        })
        .enter()
        .append("g")
        .attr("class", "legisGroups")

    var initialPath = selectLegisGroups.selectAll(".line")
      .data(([, values]) => values)
      .enter()
      .append("path")

    initialPath
      .attr("d", valueLine(([, values]) => values))
      .attr("class", "line")

  }

  // Create initial graph
  initialGraph("Alabama")


  // Update the data
  var updateGraph = function(legis){

    // Filter the data to include only state of interest
    var selectLegis = nest.filter(([key, ]) => key == legis)

    // Select all of the grouped elements and update the data
      var selectLegisGroups = svg.selectAll(".legisGroups")
        .data(selectLegis)

        // Select all the lines and transition to new positions
            selectLegisGroups.selectAll("path.line")
               .data(([, values]) => values)
                .transition()
                  .duration(1000)
                  .attr("d", valueLine(([, values ]) => values))
  }

  // Run update function when dropdown selection changes
  legisMenu.on('change', function(){

    // Find which state was selected from the dropdown
    var selectedLegis = d3.select(this)
            .select("select")
            .property("value")

        // Run update function with the selected state
        updateGraph(selectedLegis)

    });
      
  // X-axis
  svg.append('g')
      .attr('class','axis')
      .attr('id','xAxis')
      .attr('transform', 'translate(0,' + h + ')')
      .call(xAxis)
    .append('text') // X-axis Label
      .attr('id','xAxisLabel')
      .attr('fill','black')
      .attr('y',-10)
      .attr('x',w)
      .attr('dy','.71em')
      .style('text-anchor','end')
      .text('')
  // Y-axis
  svg.append('g')
      .attr('class','axis')
      .attr('id','yAxis')
      .call(yAxis)
    .append('text') // y-axis Label
      .attr('id', 'yAxisLabel')
      .attr('fill', 'black')
      .attr('transform','rotate(-90)')
      .attr('x',0)
      .attr('y',5)
      .attr('dy','.71em')
      .style('text-anchor','end')
      .text('wvalue')
})

【问题讨论】:

  • initialPath.attr("d", valueLine(([, values]) =&gt; values)) 替换为initialPath.attr("d", f =&gt; valueLine(d))。有帮助吗?
  • Michael,很遗憾没有,我得到了这个错误:Uncaught (in promise) ReferenceError: d is not defined at SVGPathElement.&lt;anonymous&gt; (&lt;anonymous&gt;:83:33) at SVGPathElement.&lt;anonymous&gt; (d3.v7.min.js:2)
  • 对不起,我的错误:应该是initialPath.attr("d", d =&gt; valueLine(d))
  • 更接近了,现在我得到了:Error: &lt;path&gt; attribute d: Expected number, "MNaN,NaNLNaN,NaN". 我添加了以下内容以确保数据格式正确(过去不必这样做,但认为值得一试) ``` data.forEach(function(d) { d.year = +d.year; d.state = d.state; d.wvalue = +d.wvalue; d.lvalue = d.value }); ``` 并且仍然收到相同的错误。我猜这是我创建组/数组的方式,但是当我 console.log 时,一切看起来都井井有条。

标签: javascript svg d3.js


【解决方案1】:

我做了更多的挖掘,找到了答案here

我不得不将初始路径属性.attr("d", valueLine(([, values]) =&gt; values)) 替换为.attr('d', (d) =&gt; valueLine(Array.from(d.values())[1]))。我还必须在selectLegisGroups .attr 下的updateGraph 函数中进一步替换代码,以使其正确更新。

【讨论】:

    猜你喜欢
    • 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
    相关资源
    最近更新 更多