d3 v5 использовать вложенные значения при масштабировании оси - PullRequest
1 голос
/ 28 сентября 2019

У меня есть следующий код, который вкладывает мои данные в зависимости от региона и даты.Проблема, с которой я столкнулся, заключается в том, что я не знаю, как определить yScale для динамического рисования оси, чтобы возвращалась максимальная сумма из вложенных данных (максимальное значение вложенных данных выше, чем максимальное значение в наборе данных bc).это агрегировано).Таким образом, мой yAxis урезан, и диаграмма не показывает все данные.

В коде, если я жестко закодирую домен в .domain ([0, 3500]), то ось верна, но в противном случае она не верна.Я не хочу жестко кодировать домен.Как я могу ссылаться на вложенные значения?

РЕДАКТИРОВАНИЕ, чтобы показать код, предоставленный в комментариях, который помогает, но не полностью исправляет, когда сценарий выполняется для всего набора данных.Пожалуйста, смотрите фото внизу.

      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>


enter image description here

1 Ответ

1 голос
/ 28 сентября 2019

ОБНОВЛЕНИЕ:

Максимальное значение yAxis меньше, чем фактическое максимальное значение данных nest, поэтому оно усекается.

Вам необходимо использовать данные nest внутриy Масштаб вычислений вместо использования исходных данных dataset.

Шаги для достижения этой цели:

  1. определить nest сначала
  2. получить значение totalMaxflatting nest дважды
  3. используйте totalMax для вычисления yScale значения
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 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, поэтому максимальное значение не будет правильным.

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

Попробуйте преобразовать значение в number перед его возвратом:

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

...