本文介绍了d3 v5在缩放轴时使用嵌套值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

限时删除!!

我有以下代码根据区域和日期嵌套我的数据.我遇到的问题是我不知道如何定义yScale来动态绘制轴,以便返回嵌套数据的最大和(嵌套数据的最大val高于数据集bc中的最大val).它是汇总的).因此,我的yAxis被截断了,并且图表未显示所有数据.

I have the following code that nests my data based on region and date. The problem I am having is that I don't know how to define yScale to dynamically draw the axis so that the max sum from the nested data is returned (the max val of the nested data is higher than the max val in the dataset bc it is aggregated). Thus my yAxis is truncated and the chart doesn't show all the data.

在代码中,如果我将域硬编码为.domain([0,3500]),则该轴是正确的,否则它是不正确的.我不想对域进行硬编码.如何引用嵌套值?

In the code, if I hardcode the domain to .domain([0, 3500]) then the axis is correct, but otherwise it is not correct. I don't want to hardcode the domain. How do I reference the nested values?

已编辑以显示注释中提供的代码,当脚本在整个数据集上运行时,这样做有帮助,但不能完全解决.请参阅底部的图片.

EDITED to show code provided in comments, which helps but doesn't entirely fix when the script is run on the entire dataset. Please see pic at bottom.

      var yScale = d3.scaleLinear()
               .domain([0, d3.max(dataset, function(d) {
                  return parseInt(d.count,10);
                })])
               .range([h - padding, padding])

      // var yScale = d3.scaleLinear()
      //   .domain([0, 3500])
      //   .range([h - padding, padding]) //not supposed to hard code the scale but it is not working
<!DOCTYPE html>
<html lang="en">

  <head>
    <meta charset="utf-8">
    <title>Nested Chart</title>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <style type="text/css">
      .pagebreak {
        page-break-before: always;
      }

      .axis path,
      .axis line {
        fill: none;
        stroke: black;
        shape-rendering: crispEdges;
      }

      .axis text {
        font-family: sans-serif;
        font-size: 11px;
      }

      .point {
        fill: none;
        size: 2px
      }

      .dot {
        fill: #ffab00;
        stroke: #fff;
        }



    </style>
  </head>

  <div style="width:800px; margin:0 auto;" class='body'></div>
  <div class="pagebreak"> </div>

  <body>

    <script type="text/javascript">
      var parseTime = d3.timeParse("%Y");


      var margin = {
          top: 20,
          right: 20,
          bottom: 30,
          left: 50
        },
        w = 960 - margin.left - margin.right,
        h = 500 - margin.top - margin.bottom;

      var padding = 20;


/////////////////get the data//////////////////
      const csv = `state,region,year,count
        Alabama,South,2010,1
        Alabama,South,2011,1
        Alabama,South,2012,0
        Alabama,South,2013,0
        Alabama,South,2014,2
        Alabama,South,2015,6
        Alaska,West,2010,2245
        Alaska,West,2011,1409
        Alaska,West,2012,1166
        Alaska,West,2013,1329
        Alaska,West,2014,1296
        Alaska,West,2015,1575
        Connecticut,Northeast,2010,0
        Connecticut,Northeast,2011,0
        Connecticut,Northeast,2012,0
        Connecticut,Northeast,2013,0
        Connecticut,Northeast,2014,0
        Connecticut,Northeast,2015,1
        Missouri,Midwest,2010,2
        Missouri,Midwest,2011,3
        Missouri,Midwest,2012,2
        Missouri,Midwest,2013,0
        Missouri,Midwest,2014,1
        Missouri,Midwest,2015,5
        California,West,2010,546
        California,West,2012,243
        California,West,2013,240
        Wyoming,West,2015,198
        California,West,2011,195
        California,West,2014,191`;

      const dataset = d3.csvParse(csv);

      dataset.forEach(function(d) {
        d.date = parseTime(d.year);
        d.region = d['region'];
        d.state = d['state'];
        d.count = d['count'];
        //console.log(d)
      });

      /////////////////scales the data//////////////////
      var xScale = d3.scaleTime()
        .domain([d3.min(dataset, function(d) {
          return d.date
        }), d3.max(dataset, function(d) {
          return d.date
        })]).range([padding, w - padding * 2])

      var yScale = d3.scaleLinear()
        .domain([0, d3.max(dataset, function(d) {
          console.log(d.count)
          return d.count ///ERROR HERE
        })]).range([h - padding, padding])

      // var yScale = d3.scaleLinear()
      //   .domain([0, 3500])
      //   .range([h - padding, padding]) //not supposed to hard code the scale but it is not working otherwise...commented out above

      var xAxis = d3.axisBottom().scale(xScale);

      var yAxis = d3.axisLeft().scale(yScale);


      /////////////////charts start here//////////////////

      var svg = d3.select("body").append("svg")
        .attr("width", w + margin.left + margin.right)
        .attr("height", h + margin.top + margin.bottom)
        .append("g")
        .attr("transform",
          "translate(" + margin.left + "," + margin.top + ")");

      var svg1 = d3.select("body").append("svg")
        .attr("width", w + margin.left + margin.right)
        .attr("height", h + margin.top + margin.bottom)
        .append("g")
        .attr("transform",
          "translate(" + margin.left + "," + margin.top + ")");

      //Define the line
      var valueLine = d3.line()
        .x(function(d) {
          return xScale(new Date(d.key));
        })
        .y(function(d) {
          return yScale(d.value);
        })


      var nest = d3.nest()
        .key(function(d) {
          return d.region;
        })
        .key(function(d) {
          return d.date;
        })
        .rollup(function(leaves) {
          return d3.sum(leaves, function(d) {
            return (d.count)
          });
        })
        .entries(dataset)

        console.log(nest)


      // Set the color scheme
        var colors = d3.scaleOrdinal()
          .domain(["South", "West", "Northeast","Midwest"])
          .range(["#EF5285", "#88F284" , "#5965A3","#900C3F"]);


      var regYear = svg.selectAll(".regYear")
        .data(nest)
        .enter()
        .append("g")
        .attr("stroke", function(d){ return colors(d.key)}); // Adding color!

      // console.log(regYear)

      var paths = regYear.selectAll(".line")
        .data(function(d) {
          return [d.values]
        })
        .enter()
        .append("path");

      // Draw the line
      paths
        .attr("d", function(d) {
          return valueLine(d)
        })
        .attr("class", "line")
        .style("fill", "none");



    svg.selectAll(".dot")
        .data(dataset)
        .enter().append("circle") // Uses the enter().append() method
        .attr("class", "dot") // Assign a class for styling
        .attr("cx", function(d, i) { return xScale(i) })
        .attr("cy", function(d) { return yScale(d.count) })//this is not working
        .attr("r", 5);


      svg.append("g").attr("class", "axis").attr("transform", "translate(0," + (h - padding) + ")").call(xAxis);
      //draw Y axis
      svg.append("g").attr("class", "axis").attr("transform", "translate(" + padding + ",0)").call(yAxis);
      // add label
      svg.append("text").attr("x", (w / 2)).attr("y", h + 30).attr("text-anchor", "middle").text("Year");
      svg.append("text").attr("x", padding).attr("y", padding - 20).attr("text-anchor", "middle").text("# of Events");
      //add title
      svg.append("text").attr("x", (w / 2)).attr("y", padding).attr("text-anchor", "middle").text("Events per Year by Category");
      // add legend
      var legend = svg.append("g")
        .attr("class", "legend")
        .attr("x", w - 65)
        .attr("y", 25)
        .attr("height", 100)
        .attr("width", 100);




      ////////////////////////////////////END///////////////////////////

    </script>

  </body>

</html>


推荐答案

更新:

yAxis的最大值小于nest数据的实际最大值,因此将其截断.

The max value of yAxis is less than the actual max value of the nest data, so it's truncated.

您必须在yScale计算中使用您的nest数据,而不是使用原始的dataset数据.

You have to use your nest data inside yScale calculation rather than using the original dataset data.

实现此目标的步骤:

  1. 首先定义nest
  2. 通过两次平坦化nest来获得totalMax
  3. 使用totalMax计算yScale
  1. define nest first
  2. get the totalMax value by flatting nest twice
  3. use totalMax to calculate the yScale value
var totalMax = Object
                 .entries(nest)
                 .reduce(function(totalMax, [key, regionValue]){
                    const regionMax = Object
                                        .entries(regionValue.values)
                                        .reduce(function(regionMax, [key,yearValue]){
                                           return parseInt(yearValue.value,10) > regionMax ? parseInt(yearValue.value, 10) : regionMax;
                                        }, 0)
                    return parseInt(regionMax, 10) > totalMax ? parseInt(regionMax, 10) : totalMax;
                  }, 0)

var yScale = d3.scaleLinear().domain([0, totalMax]).range([h - padding, padding])

我根据您的代码编写了一个Demo:

I write a Demo base on your code :

   var parseTime = d3.timeParse("%Y");


  var margin = {
      top: 20,
      right: 20,
      bottom: 30,
      left: 50
    },
    w = 960 - margin.left - margin.right,
    h = 500 - margin.top - margin.bottom;

  var padding = 20;


/////////////////get the data//////////////////
  const csv = `state,region,year,count
    Alabama,South,2010,1
    Alabama,South,2011,1
    Alabama,South,2012,0
    Alabama,South,2013,0
    Alabama,South,2014,2
    Alabama,South,2015,6
    Alaska,West,2010,2245
    Alaska,West,2011,1409
    Alaska,West,2012,1166
    Alaska,West,2013,1329
    Alaska,West,2014,1296
    Alaska,West,2015,1575
    Connecticut,Northeast,2010,0
    Connecticut,Northeast,2011,0
    Connecticut,Northeast,2012,0
    Connecticut,Northeast,2013,0
    Connecticut,Northeast,2014,0
    Connecticut,Northeast,2015,1
    Missouri,Midwest,2010,2
    Missouri,Midwest,2011,3
    Missouri,Midwest,2012,2
    Missouri,Midwest,2013,0
    Missouri,Midwest,2014,1
    Missouri,Midwest,2015,5
    California,West,2010,546
    California,West,2012,243
    California,West,2013,240
    Wyoming,West,2015,198
    California,West,2011,195
    California,West,2014,191`;

  const dataset = d3.csvParse(csv);

  dataset.forEach(function(d) {
    d.date = parseTime(d.year);
    d.region = d['region'];
    d.state = d['state'];
    d.count = d['count'];
  });

  var nest = d3.nest()
    .key(function(d) {
      return d.region;
    })
    .key(function(d) {
      return d.date;
    })
    .rollup(function(leaves) {
      return d3.sum(leaves, function(d) {
        return parseInt(d.count, 10);
      });
    })
    .entries(dataset)


  /////////////////scales the data//////////////////
  var xScale = d3.scaleTime()
    .domain([d3.min(dataset, function(d) {
      return d.date
    }), d3.max(dataset, function(d) {
      return d.date
    })]).range([padding, w - padding * 2])

  var totalMax = Object
                .entries(nest)
                .reduce(function(totalMax, [key, regionValue]){
                  const regionMax = Object
                                    .entries(regionValue.values)
                                    .reduce(function(regionMax, [key,yearValue]){
                                       return parseInt(yearValue.value,10) > regionMax ? parseInt(yearValue.value, 10) : regionMax;
                                    }, 0)
                  return parseInt(regionMax, 10) > totalMax ? parseInt(regionMax, 10) : totalMax;
                }, 0)

  var yScale = d3.scaleLinear()
    .domain([0, totalMax]).range([h - padding, padding])

  // var yScale = d3.scaleLinear()
  //   .domain([0, 3500])
  //   .range([h - padding, padding]) //not supposed to hard code the scale but it is not working otherwise...commented out above

  var xAxis = d3.axisBottom().scale(xScale);

  var yAxis = d3.axisLeft().scale(yScale);


  /////////////////charts start here//////////////////

  var svg = d3.select("body").append("svg")
    .attr("width", w + margin.left + margin.right)
    .attr("height", h + margin.top + margin.bottom)
    .append("g")
    .attr("transform",
      "translate(" + margin.left + "," + margin.top + ")");

  var svg1 = d3.select("body").append("svg")
    .attr("width", w + margin.left + margin.right)
    .attr("height", h + margin.top + margin.bottom)
    .append("g")
    .attr("transform",
      "translate(" + margin.left + "," + margin.top + ")");

  //Define the line
  var valueLine = d3.line()
    .x(function(d) {
      return xScale(new Date(d.key));
    })
    .y(function(d) {
      return yScale(d.value);
    })





  // Set the color scheme
  var colors = d3.scaleOrdinal()
      .domain(["South", "West", "Northeast","Midwest"])
      .range(["#EF5285", "#88F284" , "#5965A3","#900C3F"]);


  var regYear = svg.selectAll(".regYear")
    .data(nest)
    .enter()
    .append("g")
    .attr("stroke", function(d){ return colors(d.key)}); // Adding color!

  var paths = regYear.selectAll(".line")
    .data(function(d) {
      return [d.values]
    })
    .enter()
    .append("path");

  // Draw the line
  paths
    .attr("d", function(d) {
      return valueLine(d)
    })
    .attr("class", "line")
    .style("fill", "none");



svg.selectAll(".dot")
    .data(dataset)
    .enter().append("circle") // Uses the enter().append() method
    .attr("class", "dot") // Assign a class for styling
    .attr("cx", function(d, i) { return xScale(i) })
    .attr("cy", function(d) { return yScale(d.count) })//this is not working
    .attr("r", 5);


  svg.append("g").attr("class", "axis").attr("transform", "translate(0," + (h - padding) + ")").call(xAxis);
  //draw Y axis
  svg.append("g").attr("class", "axis").attr("transform", "translate(" + padding + ",0)").call(yAxis);
  // add label
  svg.append("text").attr("x", (w / 2)).attr("y", h + 30).attr("text-anchor", "middle").text("Year");
  svg.append("text").attr("x", padding).attr("y", padding - 20).attr("text-anchor", "middle").text("# of Events");
  //add title
  svg.append("text").attr("x", (w / 2)).attr("y", padding).attr("text-anchor", "middle").text("Events per Year by Category");
  // add legend
  var legend = svg.append("g")
    .attr("class", "legend")
    .attr("x", w - 65)
    .attr("y", 25)
    .attr("height", 100)
    .attr("width", 100);




  ////////////////////////////////////END///////////////////////////
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<!DOCTYPE html>
<html lang="en">

  <head>
    <meta charset="utf-8">
    <title>Nested Chart</title>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <style type="text/css">
      .pagebreak {
        page-break-before: always;
      }

      .axis path,
      .axis line {
        fill: none;
        stroke: black;
        shape-rendering: crispEdges;
      }

      .axis text {
        font-family: sans-serif;
        font-size: 11px;
      }

      .point {
        fill: none;
        size: 2px
      }

      .dot {
        fill: #ffab00;
        stroke: #fff;
        }



    </style>
  </head>

  <div style="width:800px; margin:0 auto;" class='body'></div>
  <div class="pagebreak"> </div>

  <body>


  </body>

</html>

我注意到您的d.count的数据类型为string,因此最大值将不正确.

I noticed the data type of your d.count is string so that the max won't be correct.

console.log(d3.max(['6','2245'])) // it's 6!

尝试将值转换为number,然后返回它:

Try converting the value to the number before return it :

var yScale = d3.scaleLinear()
               .domain([0, d3.max(dataset, function(d) {
                  return parseInt(d.count,10);
                })])
               .range([h - padding, padding])

这篇关于d3 v5在缩放轴时使用嵌套值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

1403页,肝出来的..

09-07 00:49