D3. js код не отображает требуемый выход (гистограмма) - PullRequest
0 голосов
/ 01 мая 2020

Я новичок в d3. js. Я написал этот код и отлаживал его на vizhub по этой ссылке

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Tab.1(3)- Items of general project funding in 2018 (by nature of institutions)</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.1.0/d3-legend.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.6.3/d3-tip.min.js"></script>
    <link
      rel="stylesheet"
      type="text/css"
    />
    <style>
      body {
        overflow: hidden;
      }
      text {
        fill: #444;
      }
      .axis text {
        font-family: 'Calibri', sans-serif;
        font-size: 14pt;
      }

      .axis path,
      .axis line {
        fill: none;
        stroke: #888;
        shape-rendering: crispEdges;
      }
      .color-legend text {
        font-family: 'Calibri', sans-serif;
        font-size: 19pt;
      }

      .d3-tip {
        font-family: 'Calibri', sans-serif;
        font-size: 19pt;
        line-height: 1;
        padding: 7px;
        background: black;
        color: lightgray;
        border-radius: 20px;
      }
    </style>
  </head>
  <body>
    <script>
      var outerWidth = 960;
      var outerHeight = 500;
      var margin = { left: 80, top: 8, right: 30, bottom: 150 };
      var barPadding = 0.2;

        var xColumn = 'Sub-Category';
      var xColumnTooltip = 'Segment';
      var yColumn = 'Sales';
      var colorColumn = 'Segment';
      var layerColumn = colorColumn;

      var hoveredColorValue;
      var hoveredStrokeColor = 'black';

      var innerWidth = outerWidth - margin.left - margin.right;
      var innerHeight = outerHeight - margin.top - margin.bottom;

      var svg = d3
        .select('body')
        .append('svg')
        .attr('width', outerWidth)
        .attr('height', outerHeight);
      var g = svg
        .append('g')
        .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

      // This is the layer where the bars are drawn.
      var baseBarLayer = g.append('g');

      // This layer contains a semi-transparent overlay that fades out the base bars.
      var overlayRect = g
        .append('g')
        .append('rect')
        .attr('width', innerWidth)
        .attr('height', innerHeight)
        .attr('fill', 'none')
        .style('pointer-events', 'none');

      // This contains the subset of bars rendered on top when you hover over the entries in the color legend.
      var foregroundBarLayer = g.append('g');

      var xAxisG = g
        .append('g')
        .attr('class', 'x axis')
        .attr('transform', 'translate(0,' + innerHeight + ')');
      var yAxisG = g.append('g').attr('class', 'y axis');
      var colorLegendG = g
        .append('g')
        .attr('class', 'color-legend')
        .attr('transform', 'translate(435, 0)');

      var xScale = d3.scale.ordinal().rangeBands([0, innerWidth], barPadding);
      var yScale = d3.scale.linear().range([innerHeight, 0]);
      var colorScale = d3.scale.category10();

      var tipNumberFormat = d3.format(',');

      // Use a tip offset to ensure the tooltip is not rendered off-screen or over the y-axis labels.
      var tip = d3
        .tip()
        .attr('class', 'd3-tip')
        .offset([60, 0])
        .html(function(d) {
          return [
            d[colorColumn],
            ' in ',
            d[yColumn],
            ': ',
            tipNumberFormat(d[xColumnTooltip]),
            ' item(s)'
          ].join('');
        });
      g.call(tip);

      // Use a modified SI formatter that uses "B" for billions.
      var siFormat = d3.format('s');
      var customTickFormat = function(d) {
        return siFormat(d).replace('G', 'B');
      };

      var xAxis = d3.svg
        .axis()
        .scale(xScale)
        .orient('bottom')               
        .outerTickSize(0);
      var yAxis = d3.svg
        .axis()
        .scale(yScale)
        .orient('left')
        .ticks(5) 
        .tickFormat(customTickFormat)
        .outerTickSize(0);

      var colorLegend = d3.legend
        .color()
        .scale(colorScale)
        .shapePadding(6.24)
        .shapeWidth(25)
        .shapeHeight(25)
        .labelOffset(10);

      function render(data) {
        var nested = d3
          .nest()
          .key(function(d) {
            return d[layerColumn];
          })
          .entries(data);

        var stack = d3.layout
          .stack()
          .y(function(d) {
            return d[xColumn];
          })
          .values(function(d) {
            return d.values;
          });

        var layers = stack(nested);
                console.log(layers)

        const maxY = d3.max(layers, function(layer) {
            return d3.max(layer.values, function(d) {
              return d.y + d.y0;
            });
          })
        yScale.domain([0, maxY]);

        xScale.domain(
          layers[0].values.map(function(d) {
            return d[yColumn];
          })
        );

        colorScale.domain(
          layers.map(function(layer) {
            return layer.key;
          })
        );

        colorScale.range(['#69acc8','#4a4ed8','#5700a3']);

        xAxisG
          .call(xAxis)
                .selectAll("text")  
            .style("text-anchor", "end")
            .attr("dx", "-.8em")
            .attr("dy", ".15em")
            .attr("transform", function(d) {
                return "rotate(-65)" 
                });
        yAxisG.call(yAxis);

        renderBars(baseBarLayer, layers);

        if (hoveredColorValue) {
          setOverlayTransparency(0.7);
          renderBars(
            foregroundBarLayer,
            layers.filter(function(layer) {
              return layer.key === hoveredColorValue;
            })
          );
        } else {
          setOverlayTransparency(0.0);
          renderBars(foregroundBarLayer, []);
        }

        colorLegendG.call(colorLegend);

        // Move the legend text down a bit.
        colorLegendG.selectAll('text').attr('y', 3);

        listenForHover(colorLegendG.selectAll('rect'), data);
        listenForHover(colorLegendG.selectAll('text'), data);
      }

      function renderBars(g, layers) {
        var layerGs = g.selectAll('.layer').data(layers);
        layerGs
          .enter()
          .append('g')
          .attr('class', 'layer');
        layerGs.exit().remove();

        const maxY = d3.max(layers, function(layer) {
            return d3.max(layer.values, function(d) {
              return d.y + d.y0;
            });
          })
                console.log(maxY)
        layerGs.style('fill', function(d) {
          return colorScale(d.key);
        });

        var bars = layerGs.selectAll('rect').data(function(d) {
          return d.values;
        });
        bars
          .enter()
          .append('rect')
          .on('mouseover', function(d) {
            tip.show(d);

            // Ensure the tooltip is printed in the same x position (so it is not rendered off-screen to the left).
            d3.select('.d3-tip').style('left', '200px');
          })
          .on('mouseout', tip.hide);
        bars.exit().remove();
        bars
          .attr('x', function(d) {
            return xScale(d[yColumn]);
          })
          .attr('y', function(d) {
            return yScale(d.total_items - d.y0);
          })
          .attr('height', function(d) {
            return yScale(maxY - d.y);
          })
          .attr('width', xScale.rangeBand());
      }

      function listenForHover(selection, data) {
        selection
          .on('mouseover', function(d) {
            hoveredColorValue = d;
            render(data);
          })
          .on('mouseout', function(d) {
            hoveredColorValue = null;
            render(data);
          })
          .style('cursor', 'pointer');
      }

      function setOverlayTransparency(alpha) {
        overlayRect
          .transition()
          .duration(400)
          .attr('fill', 'rgba(255, 255, 255, ' + alpha + ')');
      }

      function type(d) {
        //d.revenue = +d.revenue;
        d.items = +d.items;
        //d.revenue_billions = +d.revenue_billions;
        d.total_items = +d.total_items;
        //d.year_created = +d.year_created;

        return d;
      }
      const dataUrl = 'items1(3).csv';


      d3.csv(dataUrl, type, render);
    </script>
  </body>
</html>

Он не отображает никаких выходных данных. Следует нарисовать вертикально сложенную гистограмму. Как я могу заставить это работать. Помогите, пожалуйста, я применил различные методы отладки для этого кода, но он не работает. Например, я консолью. Регистрирую различные параметры и переменные на консоли, но это не дает необходимой гистограммы.

Примечание: к сожалению, vizhub не разветвляются. Поэтому, пожалуйста, скопируйте и вставьте код и CSV-файл в свой vizhub. Или загрузите код по этой ссылке

Обновление :

После Изучая файл data.csv, если я храню набор данных просто в одной строке, я заметил следующее:

1 - если я не оставляю пробела в заголовке строки, он отображает подкатегорию и сегмент ColorColumn, а продажи не отображаются

Строка, Сегмент, Подкатегория, Продажи

0, Потребитель, Книжные шкафы, 261,96 enter image description here

2 - если я даю пробел в заголовке строки между «Строка, сегмент» сегмент ColorColumn исчезает, а его подкатегория и продажи отображаются

Строка, сегмент, подкатегория, продажи

0, потребитель, книжные шкафы, 261,96

enter image description here

Я не нашел Любая причина, почему они не отображаются в одном представлении?

В items1 (3) .csv с полным набором данных:

1 - если я не даю пробела в заголовке csv, ничего не отображается

Идентификатор строки, Сегмент, Подкатегория, Продажи

2 - если я даю пробел в заголовке csv между «Идентификатором строки, Сегмент», то отображаются продажи и Подкатегория, а Сегмент colorColumn нет отображается

Идентификатор строки, Сегмент, Подкатегория, Продажи

enter image description here

Пожалуйста, сделайте это исправление

Обновление 2:

Я очистил свои данные и удалил все строковые значения из «Продажи». Вот обновление кода с очищенным csv . Чистый код еще не работает. 1- Если я просто использую CSV-файл, он ничего не отображает enter image description here 2-Если я помещаю пробел в заголовок, он отображает

enter image description here 3-Если я уберу пробел и введу пустую строку между заголовком и данными, то это colorColumn enter image description here Как я могу исправить это?

1 Ответ

0 голосов
/ 04 мая 2020

Короче говоря, вам нужно очистить ваши данные.

Длинный ответ:

Ваши данные не соответствуют формату?

1006 *enter image description here

enter image description here

Вы должны иметь числовое значение «продажи» для каждой строки?

Почему у некоторых есть то, что кажется быть 2 подкатегориями?

Это плохое форматирование с использованием запятой в названии подкатегории, например "Стулья, закругленные спины"?

Как это нужно исправить, чтобы работа.

Посмотрите на пример, который я дал вам в качестве ответа ({ ссылка }) на ваш предыдущий вопрос: https://vizhub.com/Alex-ley/1ebd1b410b994ce1ad4e754bd7ebf89a?edit=files&file=items1%283%29.csv

Файл .csv имеет хороший формат:

enter image description here

И обратите внимание, что в нем есть столбец total_items. Это необходимо.

Я использовал этот код VBA для очистки ваших данных (укажите заполнитель 123 значение):

Sub reformat()
    For i = 2 To Cells(Rows.Count, 4).End(xlUp).Row
        'Cells(i, 4).Select
        If Len(CStr(Val(Cells(i, 4)))) <> Len(Cells(i, 4)) Then
            Cells(i, 3) = Cells(i, 3) & ";" & Cells(i, 4)
            Cells(i, 4) = 123
        End If
    Next
End Sub

И затем я добавил Sales_totals с помощью:

Sub add_totals()
    'need reference to Microsoft Scripting Runtime (or late binding)
    Dim dict As New Dictionary

    For i = 2 To Cells(Rows.Count, 4).End(xlUp).Row
        'Cells(i, 4).Select
        trimmed = Trim(Cells(i, 2))
        If dict.Exists(trimmed) Then
            dict(trimmed) = dict(trimmed) + Cells(i, 4)
        Else
            'new key
            dict.Add trimmed, Cells(i, 4)
        End If
    Next

    For i = 2 To Cells(Rows.Count, 4).End(xlUp).Row
        If i Mod 1000 = 0 Then Cells(i, 4).Select
        trimmed = Trim(Cells(i, 2))
        Cells(i, 5) = dict(trimmed)
    Next
End Sub

Я использовал VBA только потому, что скачал ваш .csv и открыл его в Excel. Вы можете сделать это в js с nodejs (или на любом другом языке) и т.д. c. легко тоже.

наконец, вам также нужно отсортировать данные в том же порядке для каждой категории.

которые вы можете затем загрузить в свой vizhub.

Мы получаем следующее (упрощенное, подмножество) (items1 (5) .csv):

/*
departments,category,items,total_items
Consumer,Binders,640.516,3033.072
Consumer,Furnishings,360.5,3033.072
Consumer,Phones,2032.056,3033.072
Corporate,Binders,35.448,2561.37
Corporate,Furnishings,9.708,2561.37
Corporate,Phones,2516.214,2561.37
Home Office,Binders,8.226,475.924
Home Office,Furnishings,96.53,475.924
Home Office,Phones,371.168,475.924
*/

, которое отображается как: https://vizhub.com/Alex-ley/1ebd1b410b994ce1ad4e754bd7ebf89a?edit=files&file=index.html

enter image description here

Возможно, вам не нужна каждая подробная подкатегория, а показаны совокупности всех аксессуаров; 3 / упаковка, аксессуары 5 / упаковка et c. в одну подкатегорию аксессуаров?

Для этого я использовал сводную таблицу Excel, снова вы можете использовать js или r или python или что-то еще.

тогда ваш .csv мог быть похожим (items1 (6) .csv):

/*
departments,category,items,total_items
Consumer,Accessories,88813.742,1117761.366
Consumer,Appliances,49371.68,1117761.366
Consumer,Art,26425.896,1117761.366
Consumer,Binders,125785.49,1117761.366
Consumer,Bookcases,42017.5103,1117761.366
Consumer,Chairs,146295.292,1117761.366
Consumer,Copiers,69819.07,1117761.366
Consumer,Envelopes,9675.696,1117761.366
Consumer,Fasteners,4159.24,1117761.366
Consumer,Furnishings,55416.916,1117761.366
Consumer,Labels,5799.714,1117761.366
Consumer,Machines,77412.897,1117761.366
Consumer,Paper,42437.478,1117761.366
Consumer,Phones,170817.884,1117761.366
Consumer,Storage,90960.646,1117761.366
Consumer,Supplies,25741.496,1117761.366
Consumer,Tables,86810.7185,1117761.366
Corporate,Accessories,48872.848,652615.8014
Corporate,Appliances,36037.581,652615.8014
Corporate,Art,16812.594,652615.8014
Corporate,Binders,55421.79,652615.8014
Corporate,Bookcases,15951.5769,652615.8014
Corporate,Chairs,82301.001,652615.8014
Corporate,Copiers,46829.386,652615.8014
Corporate,Envelopes,7132.598,652615.8014
Corporate,Fasteners,1271.652,652615.8014
Corporate,Furnishings,30250.69,652615.8014
Corporate,Labels,3192.782,652615.8014
Corporate,Machines,45886.89,652615.8014
Corporate,Paper,26087.04,652615.8014
Corporate,Phones,91110.204,652615.8014
Corporate,Storage,65843.846,652615.8014
Corporate,Supplies,19435.284,652615.8014
Corporate,Tables,60178.0385,652615.8014
Home Office,Accessories,32447.796,407236.3853
Home Office,Appliances,16014.31,407236.3853
Home Office,Art,7259.502,407236.3853
Home Office,Binders,35040.228,407236.3853
Home Office,Bookcases,6441.1488,407236.3853
Home Office,Chairs,48921.048,407236.3853
Home Office,Copiers,32879.574,407236.3853
Home Office,Envelopes,2947.198,407236.3853
Home Office,Fasteners,1598.254,407236.3853
Home Office,Furnishings,20672.126,407236.3853
Home Office,Labels,2084.156,407236.3853
Home Office,Machines,48500.096,407236.3853
Home Office,Paper,19056.248,407236.3853
Home Office,Phones,67789.114,407236.3853
Home Office,Storage,36324.928,407236.3853
Home Office,Supplies,1496.758,407236.3853
Home Office,Tables,27763.9005,407236.3853
*/

и диаграмму, как:

enter image description here

см. мой последний форк: https://vizhub.com/Alex-ley/5d1b6f11c9a74723b3cc29ce33e3099a?edit=files&file=data%28sorted%20%26%20grouped%29.csv - вам нужно отсортировать и сгруппировать данные и добавить столбец итогов. Вы не можете иметь несколько линий с одним и тем же сегментом и подсегментом, например 22,Consumer,Accessories,90.57``` and 34, Consumer, Accessories, 45`` - вам нужно объединить все потребительские аксессуары в одну линию.

Кроме того, хотите ли вы, чтобы диаграмма имела такую ​​ориентацию:

enter image description here

Или как это (код / ​​данные явно нуждаются в изменении для этой ориентации ):

enter image description here

Конечно, вам нужно преобразовать данные для этого, и тогда это будет выглядеть так:

enter image description here

Данные теперь: https://vizhub.com/Alex-ley/5d1b6f11c9a74723b3cc29ce33e3099a?edit=files&file=data%28sorted%20%26%20grouping%20reversed%29.csv

/*
Row ID,Segment,SubCategory,Sales,Sales_total
1,Accessories,Consumer,84754.742,163246.386
18,Accessories,Corporate,47150.848,163246.386
35,Accessories,Home Office,31340.796,163246.386
2,Appliances,Consumer,42852.68,89615.571
19,Appliances,Corporate,32593.581,89615.571
36,Appliances,Home Office,14169.31,89615.571
3,Art,Consumer,15847.896,30817.992
20,Art,Corporate,10170.594,30817.992
37,Art,Home Office,4799.502,30817.992
4,Binders,Consumer,116251.55,198841.568
21,Binders,Corporate,50009.79,198841.568
38,Binders,Home Office,32580.228,198841.568
5,Bookcases,Consumer,33161.5103,49404.236
22,Bookcases,Corporate,12138.5769,49404.236
39,Bookcases,Home Office,4104.1488,49404.236
6,Chairs,Consumer,131658.292,249227.341
23,Chairs,Corporate,73814.001,249227.341
40,Chairs,Home Office,43755.048,249227.341
7,Copiers,Consumer,69819.07,149528.03
24,Copiers,Corporate,46829.386,149528.03
41,Copiers,Home Office,32879.574,149528.03
8,Envelopes,Consumer,5493.696,12006.492
25,Envelopes,Corporate,4303.598,12006.492
42,Envelopes,Home Office,2209.198,12006.492
9,Fasteners,Consumer,1084.24,2109.146
26,Fasteners,Corporate,656.652,2109.146
43,Fasteners,Home Office,368.254,2109.146
10,Furnishings,Consumer,37827.916,73375.732
27,Furnishings,Corporate,20533.69,73375.732
44,Furnishings,Home Office,15014.126,73375.732
11,Labels,Consumer,4200.714,7755.652
28,Labels,Corporate,2085.782,7755.652
45,Labels,Home Office,1469.156,7755.652
12,Machines,Consumer,76920.897,170446.883
29,Machines,Corporate,45394.89,170446.883
46,Machines,Home Office,48131.096,170446.883
13,Paper,Consumer,33089.478,72406.238
30,Paper,Corporate,22643.04,72406.238
47,Paper,Home Office,16673.72,72406.238
14,Phones,Consumer,168357.884,324428.202
31,Phones,Corporate,89019.204,324428.202
48,Phones,Home Office,67051.114,324428.202
15,Storage,Consumer,77676.646,163363.42
32,Storage,Corporate,54158.846,163363.42
49,Storage,Home Office,31527.928,163363.42
16,Supplies,Consumer,25741.496,46673.538
33,Supplies,Corporate,19435.284,46673.538
50,Supplies,Home Office,1496.758,46673.538
17,Tables,Consumer,83120.7185,166142.6575
34,Tables,Corporate,57226.0385,166142.6575
51,Tables,Home Office,25795.9005,166142.6575
*/

Я также исправил проблему при наведении, которая возникала, когда бары отображались при категория была отображена в легенде и отображалась неправильно (они отображались слишком широко, часто ниже оси X).

https://vizhub.com/Alex-ley/5d1b6f11c9a74723b3cc29ce33e3099a?file=index.html

Требовалось следующее:

из строки 204:

if (hoveredColorValue) {
      setOverlayTransparency(0.7);
      const filteredLayers = layers.filter(function(layer) {
          return layer.key === hoveredColorValue;
        });
      console.log('fl', filteredLayers);
      renderBars(
        foregroundBarLayer,
        filteredLayers,
        layers //important!
      );
} else {
      setOverlayTransparency(0.0);
      renderBars(foregroundBarLayer, []);
}

и из строки 229:

function renderBars(g, layers, masterLayers) {
    var layerGs = g.selectAll('.layer').data(layers);
    layerGs
      .enter()
      .append('g')
      .attr('class', 'layer');
    layerGs.exit().remove();

    const maxLayers = masterLayers || layers; //important!
    const maxY = d3.max(maxLayers, function(layer) {
        return d3.max(layer.values, function(d) {
          return d.y + d.y0;
        });
      })
...