График не работает: столбчатая диаграмма с накоплением — дата в сравнении со значениями

#javascript #arrays #d3.js #bar-chart

#javascript #массивы #d3.js #столбчатая диаграмма

Вопрос:

Я нигде не могу найти пример этого, и я потратил недели (включительно), пытаясь разобраться в этом. Проще говоря, я хочу отобразить данные в виде графика в виде стека с ‘x’ в качестве даты и ‘y’ в качестве числового значения, используя D3js. Я смог заставить работать только стек, и я смог заставить работать столбчатую диаграмму, но их объединение стало головной болью.

Моя последняя итерация использует этот пример здесь [http://chimera.labs.oreilly.com/books/1230000000345/ch11.html#_stack_layout ] и сокращает его до самого простого, что только может быть. Все, кроме самих данных (и, я думаю, цветов), жестко запрограммировано, чтобы устранить как можно больше проблем. Ниже приведен исходный ‘dataset’ и мои различные итерации моих собственных наборов данных, пытающихся обнаружить корень проблемы. В какой-то момент у меня был правильный график (см. Ссылку JSBin ниже), за исключением того, что он не укладывался должным образом. Кажется, что x и y подавались для построения графика, но y0 не был установлен. Ошибки out были минимальными и никоим образом не помогли.

JSBin к графику без правильной укладки: http://jsbin.com/bekanowo/1/edit

   var dataset = [
    [
      { x: 0, y: 5 },
      { x: 1, y: 4 },
      { x: 2, y: 2 },
      { x: 3, y: 7 },
      { x: 4, y: 23 }
    ],
    [
      { x: 0, y: 10 },
      { x: 1, y: 12 },
      { x: 2, y: 19 },
      { x: 3, y: 23 },
      { x: 4, y: 17 }
    ],
    [
      { x: 0, y: 22 },
      { x: 1, y: 28 },
      { x: 2, y: 32 },
      { x: 3, y: 35 },
      { x: 4, y: 43 }
    ]
  ];

var backupArray = [
[{y: 50, x: 2014-06-15},{y: 40, x: 2014-06-16},{y: 30, x: 2014-06-18}],[{y: 30, x: 2014-06-19}],
[{y: 35, x: 2014-06-17}],
[{y: 45, x: 2014-06-15}],
[{y: 85, x: 2014-06-19},{y: 65, x: 2014-06-20},{y: 75, x: 2014-06-21},{y: 60, x: 2014-06-22}]
];

var staticArray = [
[{date: "2014-06-15", value: 50},{date: "2014-06-16", value: 40},{date: "2014-06-18", value: 30}],
[{date: "2014-06-15", value: 30}],
[{date: "2014-06-17", value: 35}],
[{date: "2014-06-15", value: 45}],
[{date: "2014-06-19", value: 85},{date: "2014-06-20", value: 65},{date: "2014-06-21", value: 75},{date: "2014-06-22", value: 60}]
];

parseDate = d3.time.format("%Y-%m-%d").parse
var staticArray2 = [
{ category: "test1", values: [{date: parseDate("2014-06-15"), value: 50},{date: parseDate("2014-06-16"), value: 40},{date: parseDate("2014-06-18"), value: 30}] },
{ category: "test1", values: [{date: parseDate("2014-06-15"), value: 30}] },
{ category: "test1", values: [{date: parseDate("2014-06-17"), value: 35}] },
{ category: "test1", values: [{date: parseDate("2014-06-15"), value: 45}] },
{ category: "test1", values: [{date: parseDate("2014-06-19"), value: 85},{date: parseDate("2014-06-20"), value: 65},{date: parseDate("2014-06-21"), value: 75},{date: parseDate("2014-06-22"), value: 60}] }
];

var staticArray3 = [
{ name: "test1", values: [{x: parseDate("2014-06-15"), y: 50},{x: parseDate("2014-06-16"), y: 40},{x: parseDate("2014-06-18"), y: 30}] },
{ name: "test2", values: [{x: parseDate("2014-06-15"), y: 30}] },
{ name: "test3", values: [{x: parseDate("2014-06-17"), y: 35}] },
{ name: "test4", values: [{x: parseDate("2014-06-15"), y: 45}] },
{ name: "test5", values: [{x: parseDate("2014-06-19"), y: 85},{x: parseDate("2014-06-20"), y: 65},{x: parseDate("2014-06-21"), y: 75},{x: parseDate("2014-06-22"), y: 60}] }
];
  

Ниже приведен исходный код (без наборов данных). Я не хотел терять ни один из своих образцов таблиц данных, поэтому я просто оставил их в том виде, в каком я выполнял итерации по тестам, и просто установил текущий массив в несколько строк.

     <script type="text/javascript">

  //Width and height
  var w = 500;
  var h = 300;

  //set test array
  var dataset = staticArray3;

  //Set up stack method
  var stack = d3.layout.stack()
    .offset("zero")
    .y(function(d) { return d.values; });

  //Set up scales
  var xScale = d3.time.scale()
    .domain([parseDate("2014-06-15"), d3.time.day.offset(parseDate("2014-06-22"), 1)])
    .rangeRound([0, w], 0.05);

  var yScale = d3.scale.linear()
    .domain([0,100])
    .range([0, h]);

  //Easy colors accessible via a 10-step ordinal scale
  var colors = d3.scale.category10();

  //Create SVG element
  var svg = d3.select("body")
        .append("svg")
        .attr("width", w)
        .attr("height", h);

  // Add a group for each row of data
  var groups = svg.selectAll("g")
    .data(stack(dataset))
    .enter()
    .append("g")
    .style("fill", function(d, i) { return colors(i); });

  // Add a rect for each data value
  var rects = groups.selectAll("rect")
    .data(function(d) { return d; })
    .enter()
    .append("rect")
    .attr("x", function(d) { return xScale(d.date); })
    .attr("y", function(d) { return yScale(d.y0); })
    .attr("height", function(d) { return yScale(d.value); })
    .attr("width", 60);

</script>
  

Ответ №1:

проблема может заключаться в том, что вы неправильно используете макет стека, из-за чего ваш набор данных остается без y0. причина, по которой это, похоже, работает, заключается в том, что ваш код рисует прямоугольники, начиная с отсутствующего y0 и заканчивая высотой ваших значений.

во-первых, вы должны попытаться получить согласованный набор данных, что означает, что каждый массив должен содержать все даты, просто установите отсутствующие значения равными нулю. и, похоже, вам нужно изменить «значение» на «y»:

 var staticArray2 = [
[{date: parseDate("2014-06-15"), y: 50},{date: parseDate("2014-06-16"), y: 40},{date: parseDate("2014-06-17"), y: 0},{date: parseDate("2014-06-18"), y: 30},{date: parseDate("2014-06-19"), y: 0},{date: parseDate("2014-06-20"), y: 0},{date: parseDate("2014-06-21"), y: 0},{date: parseDate("2014-06-22"), y: 0}],
[{date: parseDate("2014-06-15"), y: 30},{date: parseDate("2014-06-16"), y: 0},{date: parseDate("2014-06-17"), y: 0},{date: parseDate("2014-06-18"), y: 0},{date: parseDate("2014-06-19"), y: 0},{date: parseDate("2014-06-20"), y: 0},{date: parseDate("2014-06-21"), y: 0},{date: parseDate("2014-06-22"), y: 0}],
[{date: parseDate("2014-06-15"), y: 0},{date: parseDate("2014-06-16"), y: 0},{date: parseDate("2014-06-17"), y: 35},{date: parseDate("2014-06-18"), y: 0},{date: parseDate("2014-06-19"), y: 0},{date: parseDate("2014-06-20"), y: 0},{date: parseDate("2014-06-21"), y: 0},{date: parseDate("2014-06-22"), y: 0}],
[{date: parseDate("2014-06-15"), y: 45},{date: parseDate("2014-06-16"), y: 0},{date: parseDate("2014-06-17"), y: 0},{date: parseDate("2014-06-18"), y: 0},{date: parseDate("2014-06-19"), y: 0},{date: parseDate("2014-06-20"), y: 0},{date: parseDate("2014-06-21"), y: 0},{date: parseDate("2014-06-22"), y: 0}],
[{date: parseDate("2014-06-15"), y: 0},{date: parseDate("2014-06-16"), y: 0},{date: parseDate("2014-06-17"), y: 0},{date: parseDate("2014-06-18"), y: 0},{date: parseDate("2014-06-19"), y: 85},{date: parseDate("2014-06-20"), y: 65},{date: parseDate("2014-06-21"), y: 75},{date: parseDate("2014-06-22"), y: 60}]];
  

затем используйте

 var stack = d3.layout.stack();
var dataset = stack(staticArray2);
  

и, наконец, измените свой d.value на

  .attr("height", function(d) { return yScale(d.y); })
  

вывод может выглядеть немного странно, потому что многие из ваших значений равны нулю, поэтому некоторые стеки начинаются с базовой линии, но поскольку они не являются «стеком 0», они имеют другой — соответствующий — цвет.

смотрите jsbin здесь, я добавил наведение курсора мыши для записи значений столбцов в консоль: http://jsbin.com/bekanowo/9 /

надеюсь, это поможет.

Комментарии:

1. Большое вам спасибо! Короче говоря, стек требует, чтобы каждая дата (в данном случае) существовала, даже если значение равно 0? Есть ли какой-либо способ обойти это, поскольку кажется неуклюжим иметь массив, который потенциально содержит так много пустых данных.

2. хотя я не уверен на 10%, что я так думаю, да — это необходимо, чтобы функция могла расположить данные правильным образом. что вы могли бы сделать, так это взять исходные данные и выполнить цикл по ним, сохраняя каждую дату в массиве. затем возьмите этот массив и повторите цикл с вашими данными еще раз, и если в одном из «подмассивов» отсутствует дата, просто добавьте к нему y:0 и дату. таким образом, вам не придется исправлять это самостоятельно.