【问题标题】:How to reduce padding between bars in grouped bar chart?如何减少分组条形图中条形之间的填充?
【发布时间】:2019-06-06 08:13:44
【问题描述】:

我有一个基于D3 的分组条形图。有没有办法可以减少每组中的条之间的差距,而不是组之间的差距。我为每个条形设置了固定宽度。

片段如下:

//DATA
var data = [
  {
    category: "test4",
    type1: 100,
    type2: 200,
    type3: 0,
    column: "column1"
  },
  {
    category: "test4",
    type1: 0,
    type2: 0,
    type3: 500,
    column: "column2"
  },
  {
    category: "test3",
    type1: 100,
    type2: 200,
    type3: 0,
    column: "column1"
  },
  {
    category: "test3",
    type1: 0,
    type2: 0,
    type3: 500,
    column: "column2"
  },
  {
    category: "test1",
    type1: 100,
    type2: 200,
    type3: 0,
    column: "column1"
  },
  {
    category: "test1",
    type1: 0,
    type2: 0,
    type3: 500,
    column: "column2"
  },
  {
    category: "test2",
    type1: 150,
    type2: 100,
    type3: 0,
    column: "column1"
  },
  {
    category: "test2",
    type1: 0,
    type2: 0,
    type3: 400,
    column: "column2"
  }
];

var groupData = d3
  .nest()
  .key(function(d) {
    return d.column + d.category;
  })
  .rollup(function(v) {
    return {
      ...v[0],
      total: v[0].type1 + v[0].type2 + v[0].type3
    };
  })
  .entries(data)
  .map(function(d) {
    return { ...d.value };
  });


//INITIAL SETUP
var margin = { top: 30, right: 10, bottom: 50, left: 40 };
var width = groupData.length * 80 - margin.left - margin.right;
var height = 500 - margin.top - margin.bottom;
var barwidth = 40;

var tip = d3
  .tip()
  .attr("class", "d3-tip")
  .html(function(d) {
    return "<span>Test</span>";
  });

var graph = d3
  .select("#chart-area")
  .append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

graph.call(tip);

var x0 = d3
  .scaleBand()
  .rangeRound([0, width])
  .paddingInner(0.1);

var x1 = d3.scaleBand().padding(0.05);

var y = d3.scaleLinear().range([height, 0]);

var y1 = d3.scaleBand();

var z = d3.scaleOrdinal().range(["#98abc5", "#8a89a6"]);

var stack = d3.stack();

x0.domain(
  data.map(function(d) {
    return d.category;
  })
);
x1
  .domain(
    data.map(function(d) {
      return d.column;
    })
  )
  .range([0, x0.bandwidth()])
  .padding(0.2);

z.domain(Object.keys(data[0]).filter(x => x !== "category" && x !== "column"));
var keys = z.domain(); //["type1", "type2", "type3"];

var stackData = stack.keys(keys)(groupData);

y.domain([
  0,
  d3.max(groupData, function(d) {
    return d.total;
  })
]);
var serie = graph
  .selectAll(".serie")
  .data(stackData)
  .enter()
  .append("g")
  .attr("class", "serie")
  .attr("fill", function(d) {
    return z(d.key);
  });

serie
  .selectAll("rect")
  .data(function(d) {
    return d;
  })
  .enter()
  .append("rect")
  .attr("class", "serie-rect")
  .attr("transform", function(d) {
    return "translate(" + x0(d.data.category) + ",0)";
  })
  .attr("x", function(d) {
    return x1(d.data.column) + x1.bandwidth() / 2 - 20;
  })
  .attr("y", function(d) {
    return y(d[1]);
  })
  .attr("height", function(d) {
    return y(d[0]) - y(d[1]);
  })
  .attr("width", barwidth)
  .on("mouseover", tip.show)
  .on("mouseout", tip.hide);

graph
  .append("g")
  .attr("class", "axis")
  .attr("transform", "translate(0," + height + ")")
  .call(d3.axisBottom(x0));

graph
  .append("g")
  .attr("class", "axis")
  .call(d3.axisLeft(y).ticks(null, "s"));
#chart-area {
  width: 600px;
  overflow-x: auto;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.9.1/d3-tip.min.js"></script>
<div id="chart-area"></div>

【问题讨论】:

  • 不要硬编码你的条形宽度,使用scaleBandpadding选项
  • @rioV8。我需要条宽为 40,这很糟糕,但这是一个要求。

标签: javascript d3.js svg


【解决方案1】:

更改innerScale 中的paddingOuter。例如:

x1.paddingOuter(0.5);

这是进行更改的代码。

//DATA
var data = [
  {
    category: "test4",
    type1: 100,
    type2: 200,
    type3: 0,
    column: "column1"
  },
  {
    category: "test4",
    type1: 0,
    type2: 0,
    type3: 500,
    column: "column2"
  },
  {
    category: "test3",
    type1: 100,
    type2: 200,
    type3: 0,
    column: "column1"
  },
  {
    category: "test3",
    type1: 0,
    type2: 0,
    type3: 500,
    column: "column2"
  },
  {
    category: "test1",
    type1: 100,
    type2: 200,
    type3: 0,
    column: "column1"
  },
  {
    category: "test1",
    type1: 0,
    type2: 0,
    type3: 500,
    column: "column2"
  },
  {
    category: "test2",
    type1: 150,
    type2: 100,
    type3: 0,
    column: "column1"
  },
  {
    category: "test2",
    type1: 0,
    type2: 0,
    type3: 400,
    column: "column2"
  }
];

var groupData = d3
  .nest()
  .key(function(d) {
    return d.column + d.category;
  })
  .rollup(function(v) {
    return {
      ...v[0],
      total: v[0].type1 + v[0].type2 + v[0].type3
    };
  })
  .entries(data)
  .map(function(d) {
    return { ...d.value };
  });


//INITIAL SETUP
var margin = { top: 30, right: 10, bottom: 50, left: 40 };
var width = groupData.length * 80 - margin.left - margin.right;
var height = 500 - margin.top - margin.bottom;
var barwidth = 40;

var tip = d3
  .tip()
  .attr("class", "d3-tip")
  .html(function(d) {
    return "<span>Test</span>";
  });

var graph = d3
  .select("#chart-area")
  .append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

graph.call(tip);

var x0 = d3
  .scaleBand()
  .rangeRound([0, width])
  .paddingInner(0.1);

var x1 = d3.scaleBand().padding(0.05);

var y = d3.scaleLinear().range([height, 0]);

var y1 = d3.scaleBand();

var z = d3.scaleOrdinal().range(["#98abc5", "#8a89a6"]);

var stack = d3.stack();

x0.domain(
  data.map(function(d) {
    return d.category;
  })
);
x1
  .domain(
    data.map(function(d) {
      return d.column;
    })
  )
  .range([0, x0.bandwidth()])
  .paddingOuter(0.5);

z.domain(Object.keys(data[0]).filter(x => x !== "category" && x !== "column"));
var keys = z.domain(); //["type1", "type2", "type3"];

var stackData = stack.keys(keys)(groupData);

y.domain([
  0,
  d3.max(groupData, function(d) {
    return d.total;
  })
]);
var serie = graph
  .selectAll(".serie")
  .data(stackData)
  .enter()
  .append("g")
  .attr("class", "serie")
  .attr("fill", function(d) {
    return z(d.key);
  });

serie
  .selectAll("rect")
  .data(function(d) {
    return d;
  })
  .enter()
  .append("rect")
  .attr("class", "serie-rect")
  .attr("transform", function(d) {
    return "translate(" + x0(d.data.category) + ",0)";
  })
  .attr("x", function(d) {
    return x1(d.data.column) + x1.bandwidth() / 2 - 20;
  })
  .attr("y", function(d) {
    return y(d[1]);
  })
  .attr("height", function(d) {
    return y(d[0]) - y(d[1]);
  })
  .attr("width", barwidth)
  .on("mouseover", tip.show)
  .on("mouseout", tip.hide);

graph
  .append("g")
  .attr("class", "axis")
  .attr("transform", "translate(0," + height + ")")
  .call(d3.axisBottom(x0));

graph
  .append("g")
  .attr("class", "axis")
  .call(d3.axisLeft(y).ticks(null, "s"));
#chart-area {
  width: 600px;
  overflow-x: auto;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.9.1/d3-tip.min.js"></script>
<div id="chart-area"></div>

如您所见,这将同时减少分组条之间的距离并增加组之间的距离(因为,显然,我没有更改外部刻度的范围)。此外,与您的问题无关:您正在覆盖内部刻度中的填充。

【讨论】:

  • 只有一组时。条形重叠。
  • 如果只有一组,则不是分组条形图。因此,这是另一个问题,需要另一种解决方案。
猜你喜欢
  • 2018-11-27
  • 1970-01-01
  • 1970-01-01
  • 2022-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-22
相关资源
最近更新 更多