Рисунок несколько диаграмм, используя те же данные


В данном примере (буду размещать весь код ниже) я рисую два разных графика, используя тот же CSV данных.

Что я в принципе хочу знать, если есть что-то, что я делаю "плохо" или если есть более эффективный способ рисовать два графика в этой дороге.

Вот весь код:

<!DOCTYPE html>
<html>
<head>
<meta charset ="utf-8">
<script src="//d3js.org/d3.v4.min.js"></script>

<style>

body {
  margin:auto;
  width:850px;
  font:10px arial;
  padding:25px;
}
select {
  -moz-appearance: none;
  -webkit-appearance:none;
  border:none;
  border-bottom: 1px solid #333;
  padding:3px;
  font:10px arial;
  font-weight: bold;
}

</style>
</head>

<body>

Select Category: 
<select id="category">
  <option value=" 1">Category 1</option>
  <option value=" 2">Category 2</option>
</select> 

<!-- Chart -->
<div id="chart"></div>

<script>

let formatValue = d3.format(",.0f");

let margin = {top: 35, right: 35, bottom: 35, left: 35},
    width = 550 - margin.left - margin.right,
    height = 400 - margin.top - margin.bottom;

let durations = 0;

let afterLoad = () => durations = 750;

// ===== Monthly bars init values =====

let x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
    y = d3.scaleLinear().rangeRound([height, 0]);

let xAxis = d3.axisBottom(x),
    yAxis = d3.axisLeft(y)

let g = d3.select("#chart").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 + ")");

g.append("g")
    .attr("class", "axis axis--x")
    .attr("transform", "translate(0," + height + ")");
g.append("g")
    .attr("class", "axis axis--y");

// ===== Total bar init values =====

let totalG = d3.select("#chart").append("svg")
    .attr("width", (width/4) + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform","translate(" + margin.left + "," + margin.top + ")");

let xTotal = d3.scaleBand().rangeRound([0, (width/4)]).padding(0.1),
    yTotal = d3.scaleLinear().rangeRound([height, 0]);

let xAxisTotal = d3.axisBottom(xTotal),
    yAxisTotal = d3.axisLeft(yTotal);

totalG.append("g")
    .attr("class", "axis axis--xTotal")
    .attr("transform", "translate(0," + height + ")");
totalG.append("g")
    .attr("class", "axis axis--yTotal");

d3.queue()
    .defer(d3.csv, "data.csv", d => d)
    .await(ready);

function ready(error, data) {

  if (error) throw error;

  var INT;

  // Event handlers
  d3.select("#category").on('change', update);

  update();

  function update() {

    INT = d3.select('#category').property('value');

    var totalSum = d3.sum(data, d => d["Category"  + INT])

    // ========= Monthly bars =========

    y.domain([0, d3.max(data, d => d["Category" + INT]) ]).nice();

    x.domain(data.map( d => d.month ));

    g.selectAll(".axis.axis--y").transition()
      .duration(durations)
      .call(yAxis);

    g.selectAll(".axis.axis--x").transition()
      .duration(durations)
      .call(xAxis);

    let bars = g.selectAll(".barEnter")
      .data(data, d => d.month);

    bars = bars
      .enter()
    .append("rect")
      .attr("class", "barEnter")
      .attr("fill","steelblue")
      .attr("x", d => x(d.month))
      .attr("width", x.bandwidth())
      .merge(bars);

    bars.transition()
      .duration(durations)
      .attr("y", d => y(d["Category" + INT]))
      .attr("height", d => height - y(d["Category" + INT]))

    bars.exit().remove();

    // ========= Total bar =========

    yTotal.domain([0, totalSum]).nice();

    xTotal.domain(["Total"]);

    totalG.selectAll(".axis.axis--yTotal").transition()
      .duration(durations)
      .call(yAxisTotal);

    totalG.selectAll(".axis.axis--xTotal").transition()
      .duration(durations)
      .call(xAxisTotal);

    let totalBar = totalG.selectAll(".totalBar")
      .data(["empty"]); 

    totalBar = totalBar
      .enter()
    .append("rect")
      .attr("class", "totalBar")
      .attr("fill","#ccc")
      .attr("x", xTotal(["Total"]) )
      .attr("width", xTotal.bandwidth())
      .merge(totalBar)

    totalBar.transition()
      .duration(durations)
      .attr("y", yTotal(totalSum))
      .attr("height", height - yTotal(totalSum))

  } // End of update

  afterLoad()

} // End of ready

</script>

</body>
</html> 


1119
4
задан 28 января 2018 в 01:01 Источник Поделиться
Комментарии
1 ответ

В целом ваш код выглядит хорошо, это идиоматическое Д3 и он не имеет каких-либо серьезных проблем (например, добавление элементов в цикле). Я бы поставил два графика в одном SVG с разных <g> элементов, но это вопрос личного выбора (наличие двух Свгс может быть полезна, если вы хотите, например, переставить их в HTML).

Я предлагаю 3 изменений, хотя:


  1. У вас есть только 1 файл в формате CSV. Таким образом, можно удалить d3.queue, который обычно используется для борьбы с несколько (то есть более одного) асинхронных задач. Используйте простой d3.csv вместо:

    d3.csv("data.csv", function (error, data) {
    //etc...

    Редактировать: в новый D3 версия 5 d3.csv использует обещания. Следовательно, приведенный выше фрагмент будет:

    d3.csv("data.csv").then(function (data) {
    //etc...

  2. Не использовать переменные внешней функции, чтобы определить поведение этой функции: трудно понять, где ценность и отлаживать труднее.

    Вместо этого, передать эти переменные в качестве аргументов. В случае Вашего update функции, передать значение из выпадающего списка:

    var category = d3.select('#category').property('value');

    d3.select("#category").on('change', function(){
    update(this.value)
    });

    update(category);

    function update(INT) {
    //etc...
    }


  3. Вам не нужно, что afterLoad функция... просто изменить durations в обработчик события!

    d3.select("#category").on('change', function(){
    durations = 750;
    update(this.value)
    });

Вот обновленный Plunker с этими изменениями: https://plnkr.co/edit/P5r3D7JRbhB9yv8dmwzg?p=preview

2
ответ дан 29 января 2018 в 12:01 Источник Поделиться