HighChart:带下拉菜单的堆积柱形图

HighChart: Stacked Column Chart with dropdown

我正在尝试使用 d3.js

复制 this example

到目前为止,我已经设法构建了一个显示所有数据的堆叠条形图,但我的目的是根据用户在组合中选择的国家/地区过滤 csv 并将新数据绑定到我的图表。

这是我的代码:

  var outerWidth = 500;
  var outerHeight = 250;
  var margin = { left: 90, top: 30, right: 30, bottom: 40 };
  var barPadding = 0.2;
  var xColumn = "City";
  var yColumn = "Population";
  var colorColumn = "Year";
  var layerColumn = colorColumn;
  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 + ")");
  var xAxisG = g.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + innerHeight + ")");
  var yAxisG = g.append("g")
    .attr("class", "y axis");
  var xScale = d3.scale.ordinal().rangeBands([0, innerWidth], barPadding);
  var yScale = d3.scale.linear().range([innerHeight, 0]);
  var colorScale = d3.scale.category10();
  // Use a modified SI formatter that uses "B" for Billion.
  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);
  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[yColumn]; })
      .values(function (d){ return d.values; });
    var layers = stack(nested);
    xScale.domain(layers[0].values.map(function (d){
      return d[xColumn];
    }));
    yScale.domain([
      0,
      d3.max(layers, function (layer){
        return d3.max(layer.values, function (d){
          return d.y0 + d.y;
        });
      })
    ]);
    colorScale.domain(layers.map(function (layer){
      return layer.key;
    }));
    xAxisG.call(xAxis);
    yAxisG.call(yAxis);
    var layerGroups = g.selectAll(".layer").data(layers);
    layerGroups.enter().append("g").attr("class", "layer");
    layerGroups.exit().remove();
    layerGroups.style("fill", function (d){
      return colorScale(d.key);
    });
    var bars = layerGroups.selectAll("rect").data(function (d){
      return d.values;
    });
    bars.enter().append("rect")
    bars.exit().remove();
    bars
      .attr("x", function (d){ return xScale(d[xColumn]); })
      .attr("y", function (d){ return yScale(d.y0 + d.y); })
      .attr("width", xScale.rangeBand())
      .attr("height", function (d){ return innerHeight - yScale(d.y); })
  }
  function type(d){
    d.Population = +d.Population;
    return d;
  }
  d3.csv("data.csv", type, render);
</script>

这是 data.csv

中的示例数据
Country City Year Population

US     Dallas 2010 1000
US     Dallas 2011 1200
UK     London 2010 700
UK     London 2011  850
US     Chicago  2010 1250
US     Chicago  2011  1300

可以通过重新加载和重新解析CSV和使用Highcharts官方data module来重新加载图表的数据,但是加载和解析一次数据效率更高,下一个update/rebuild图表需要的时候。

Example: http://jsfiddle.net/1wgqyyg9/

// Emulate get
$.get = function(id, fn) {
  fn(document.getElementById(id).innerHTML);
};

$(function() {
  var chartOptions = {
      chart: {
        type: 'column'
      },
      xAxis: {
        type: 'category'
      },
      yAxis: {
        min: 0,
        title: {
          text: 'Total population over years'
        },
        stackLabels: {
          enabled: true,
          style: {
            fontWeight: 'bold',
            color: (Highcharts.theme && Highcharts.theme.textColor) || 'gray'
          }
        }
      },
      legend: {
        align: 'right',
        x: -30,
        verticalAlign: 'top',
        y: 25,
        floating: true,
        backgroundColor: (Highcharts.theme && Highcharts.theme.background2) || 'white',
        borderColor: '#CCC',
        borderWidth: 1,
        shadow: false
      },
      tooltip: {
        headerFormat: '<b>{series.name}</b><br/>',
        pointFormat: '{point.name}: {point.y}<br/>Total: {point.stackTotal}'
      },
      plotOptions: {
        column: {
          stacking: 'normal',
          dataLabels: {
            enabled: true,
            color: (Highcharts.theme && Highcharts.theme.dataLabelsColor) || 'white',
            style: {
              textShadow: '0 0 3px black'
            }
          }
        }
      },
      series: []
    };

  //load CSV - emulated infile to show in JSFiddle
  $.get('data.csv', function(data) {
    // Split the lines
    var lines = data.split('\n'),
       countries = {};

    // Iterate over the lines and create data sets - countries
    $.each(lines, function(lineNo, line) {
      var items = line.split(','),
        yearFound = false,
        country, city, year, population;

      if (lineNo === 0) {  // header line containes info
      }          // rest of lines contain data
      else {
       country = items[0],
        city = items[1],
        year = items[2],
        population = items[3];
       // check if new country
        if(countries[country] === undefined) {
         countries[country] = [{
           name: year,
            data: [{name: city, y: parseInt(population)}]
          }];
        } else {
         $.each(countries[country], function(yearNo, countryYear){
           if(year === countryYear.name) {
             yearFound = true;
              countryYear.data.push({name: city, y: parseInt(population)});
             return false; //exit this each loop
            }
          });
          if(!yearFound) { // new year
           countries[country].push({
             name: year,
             data: [{name: city, y: parseInt(population)}]
            });
          }
        }
      }
    });
    // sort cities in year series
    $.each(countries, function(countryName,country){
     $('#selectCountry')
         .append($("<option></option>")
         .attr("value",countryName)
         .text(countryName)); 
         
     $.each(country, function(j,year){
       year.data.sort(function(a,b){
         a.name > b.name;
        });
      });
    });
    
    $("#selectCountry").change(function(){ 
        var selected = this.value;
        if(selected) {
          chartOptions.series = $.extend(true, [], countries[selected]); // do deep copy to keep original data
          Highcharts.chart('container',chartOptions);
        }
    });
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>

<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>

<select id="selectCountry">
  <option>Select country</option>
</select>

<pre id="data.csv" style="display: none">Country,City,Year,Population
US,Dallas,2010,1000
US,Dallas,2011,1200
UK,London,2010,700
UK,London,2011,850
US,Chicago,2010,1250
US,Chicago,2011,1300</pre>