d3.js – 当我的小组大小不一时,制作一个分组的条形图?

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

district,candidate,Votes
dist 1,Leticia Putte,3580
dist 2,David Barron,1620
dist 2,John Higginson,339
dist 2,Walter Bannister,2866
[...]

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

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

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

解决方法

我的回答类似于@GerardoFurtado,但我使用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);

areaD成为您放置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);
      });

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

完整运行代码

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,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",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");

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

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

<!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,left: 110
      },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"," + m.top + ")");

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


    d3.csv(url,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,function(d) {
        return d.Votes;
      })]);

      svg.append("g")
        .attr("class","x axis")
        .attr("transform"," + 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",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>

相关文章

前言 做过web项目开发的人对layer弹层组件肯定不陌生,作为l...
前言 前端表单校验是过滤无效数据、假数据、有毒数据的第一步...
前言 图片上传是web项目常见的需求,我基于之前的博客的代码...
前言 导出Excel文件这个功能,通常都是在后端实现返回前端一...
前言 众所周知,js是单线程的,从上往下,从左往右依次执行,...
前言 项目开发中,我们可能会碰到这样的需求:select标签,禁...