当我的团体大小不一时,制作一个分组的条形图?

Ama*_*nda 3 d3.js

我试图从一些分组数据中制作一个条形图.这是虚拟数据,但结构基本相同.数据:选举结果包括一群候选人,组织到他们所在的地区,以及总投票数:

district,candidate,votes
Dist 1,Leticia Putte,3580
Dist 2,David Barron,1620
Dist 2,John Higginson,339
Dist 2,Walter Bannister,2866
[...]
Run Code Online (Sandbox Code Playgroud)

我想创建一个条形图或柱形图(老实说,虽然我的最终目标是水平的),按地区划分候选人.

迈克博斯托克有一个很棒的演示,但我无法根据我的目的智能地翻译它.我开始在https://jsfiddle.net/97ur6cwt/6/上取笑它,但我的数据组织有所不同 - 而不是行,按组,我有一个列设置类别.可能只有一名候选人或可能会有一些候选人.

如果组的大小不同,我可以对项目进行分组吗?

Mar*_*ark 6

我的答案类似于@GerardoFurtado,但我使用a d3.nest来构建每个区域的域名.这消除了对硬编码值的需要并将其清理一下:

y0.domain(data.map(function(d) { return d.district; }));

var districtD = d3.nest()
  .key(function(d) { return d.district; })
  .rollup(function(d){
    return d3.scale.ordinal()
        .domain(d.map(function(c){return c.candidate}))
      .rangeRoundBands([0, y0.rangeBand()], pad);
  }).map(data);
Run Code Online (Sandbox Code Playgroud)

districtD 成为您放置rects时使用的y轴的域映射:

  svg.selectAll("bar")
      .data(data)
      .enter().append("rect")
      .style("fill", function(d,i) {
          return color(d.district);
      })
      .attr("x", 0)
      .attr("y", function(d) { return y0(d.district) + districtD[d.district](d.candidate); })
      .attr("height", function(d){
        return districtD[d.district].rangeBand();
      })
      .attr("width", function(d) {
        return x(d.votes);
      });
Run Code Online (Sandbox Code Playgroud)

我要开会了,但下一步是清理轴并在那里找到候选人名字.


完整运行代码:

var url = "https://gist.githubusercontent.com/amandabee/edf73bc0bbe131435c952f5ed47524a6/raw/99febb9971f76e36af06f1b99913fcaa645ecb3e/election.csv"
var m = {top: 10, right: 10, bottom: 50, left: 110},
  w = 800 - m.left - m.right,
  h = 500 - m.top - m.bottom,
  pad = .1;

var x = d3.scale.linear().range([0, w]);
y0 = d3.scale.ordinal().rangeRoundBands([0, h], pad);

var color = d3.scale.category20c();

var yAxis = d3.svg.axis()
    .scale(y0)
    .orient("left");

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom")
    .ticks(5)
    .tickFormat(d3.format("$,.0f"));


var svg = d3.select("#chart").append("svg")
  .attr("width", w + m.right + m.left + 100)
  .attr("height", h + m.top + m.bottom)
  .append("g")
  .attr("transform",
        "translate(" + m.left + "," + m.top + ")");

        // This moves the SVG over by m.left(110)
        // and down by m.top (10)


  d3.csv(url, function(error, data) {

    data.forEach(function(d) {
      d.votes = +d.votes;
    });
    
    y0.domain(data.map(function(d) { return d.district; }));
    districtD = d3.nest()
    	.key(function(d) { return d.district; })
      .rollup(function(d){
      	console.log(d);
        return d3.scale.ordinal()
        	.domain(d.map(function(c){return c.candidate}))
          .rangeRoundBands([0, y0.rangeBand()], pad);
      })
      .map(data);    
		
    x.domain([0, d3.max(data, function(d) {
        return d.votes;
      })]);

      svg.append("g")
          .attr("class", "x axis")
          .attr("transform", "translate(0," + h + ")")
          .call(xAxis)
          .selectAll("text")
          .style("text-anchor", "middle");

      svg.append("g")
          .attr("class", "y axis")
          .call(yAxis)
          .append("text");

      svg.selectAll("bar")
          .data(data)
          .enter().append("rect")
          .style("fill", function(d,i) {
              return color(d.district);
          })
          .attr("x", 0)
          .attr("y", function(d) { return y0(d.district) + districtD[d.district](d.candidate); })
          .attr("height", function(d){
          	return districtD[d.district].rangeBand();
          })
          .attr("width", function(d) {
            return x(d.votes);
            });

      svg.selectAll(".label")
  			   .data(data)
  			   .enter().append("text")
  			   .text(function(d) {
             return (d.votes);
             })
  			   .attr("text-anchor", "start")
           .attr("x", function(d) { return x(d.votes)})
           .attr("y", function(d) { return y0(d.district) +  districtD[d.district](d.candidate) + districtD[d.district].rangeBand()/2;})
  			   .attr("class", "axis");

  });
Run Code Online (Sandbox Code Playgroud)
    .axis {
      font: 10px sans-serif;
    }
    .axis path, .axis line {
      fill: none;
      stroke: black;
      shape-rendering: crispEdges;
    }
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="chart"></div>
Run Code Online (Sandbox Code Playgroud)

一个替代版本,它使条形大小相同并适当地缩放外部域:

<!DOCTYPE html>
<html>

<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
  <style>
    .label {
      font: 10px sans-serif;
    }
    
    .axis {
      font: 11px sans-serif;
      font-weight: bold;
    }
    
    .axis path,
    .axis line {
      fill: none;
      stroke: black;
      shape-rendering: crispEdges;
    }
  </style>
</head>

<body>
  <div id="chart"></div>
  <script>
    var url = "https://gist.githubusercontent.com/amandabee/edf73bc0bbe131435c952f5ed47524a6/raw/99febb9971f76e36af06f1b99913fcaa645ecb3e/election.csv"
    var m = {
        top: 10,
        right: 10,
        bottom: 50,
        left: 110
      },
      w = 800 - m.left - m.right,
      h = 500 - m.top - m.bottom,
      pad = .1, padPixel = 5;

    var x = d3.scale.linear().range([0, w]);
    var y0 = d3.scale.ordinal();

    var color = d3.scale.category20c();

    var yAxis = d3.svg.axis()
      .scale(y0)
      .orient("left");

    var xAxis = d3.svg.axis()
      .scale(x)
      .orient("bottom")
      .ticks(5)
      .tickFormat(d3.format("$,.0f"));


    var svg = d3.select("#chart").append("svg")
      .attr("width", w + m.right + m.left + 100)
      .attr("height", h + m.top + m.bottom)
      .append("g")
      .attr("transform",
        "translate(" + m.left + "," + m.top + ")");

    // This moves the SVG over by m.left(110)
    // and down by m.top (10)


    d3.csv(url, function(error, data) {

      data.forEach(function(d) {
        d.votes = +d.votes;
      });

      var barHeight = h / data.length;

      y0.domain(data.map(function(d) {
        return d.district;
      }));
      
      var y0Range = [0];
      districtD = d3.nest()
        .key(function(d) {
          return d.district;
        })
        .rollup(function(d) {
          var barSpace = (barHeight * d.length);
          y0Range.push(y0Range[y0Range.length - 1] + barSpace);
          return d3.scale.ordinal()
            .domain(d.map(function(c) {
              return c.candidate
            }))
            .rangeRoundBands([0, barSpace], pad);
        })
        .map(data);
      
      y0.range(y0Range);
      
      x.domain([0, d3.max(data, function(d) {
        return d.votes;
      })]);

      svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + h + ")")
        .call(xAxis)
        .selectAll("text")
        .style("text-anchor", "middle");

      svg.append("g")
        .attr("class", "y axis")
        .call(yAxis)
        .append("text");

      svg.selectAll("bar")
        .data(data)
        .enter().append("rect")
        .style("fill", function(d, i) {
          return color(d.district);
        })
        .attr("x", 0)
        .attr("y", function(d) {
          return y0(d.district) + districtD[d.district](d.candidate);
        })
        .attr("height", function(d) {
          return districtD[d.district].rangeBand();
        })
        .attr("width", function(d) {
          return x(d.votes);
        });

      var ls = svg.selectAll(".labels")
        .data(data)
        .enter().append("g");
        
      ls.append("text")
        .text(function(d) {
          return (d.votes);
        })
        .attr("text-anchor", "start")
        .attr("x", function(d) {
          return x(d.votes)
        })
        .attr("y", function(d) {
          return y0(d.district) + districtD[d.district](d.candidate) + districtD[d.district].rangeBand() / 2;
        })
        .attr("class", "label");

      ls.append("text")
        .text(function(d) {
          return (d.candidate);
        })
        .attr("text-anchor", "end")
        .attr("x", -2)
        .attr("y", function(d) {
          return y0(d.district) + districtD[d.district](d.candidate) + districtD[d.district].rangeBand() / 2;
        })
        .style("alignment-baseline", "middle")
        .attr("class", "label");

    });
  </script>
</body>

</html>
Run Code Online (Sandbox Code Playgroud)