# 可视化D3专题系列(三)柱状图

1,563 阅读4分钟

Author: arcsin1
time: 2020.07.04
info: 这个系列主要结合D3(v4和v5)

开篇认识柱状图

  • 柱状图也是最基本最常见的可视化图形,我们用D3一步步绘制
  • 柱状图,是一种使用矩形条,对不同类别进行数值比较的统计图表。最基础的柱形图,需要一个分类变量和一个数值变量。在柱状图上,分类变量的每个实体都被表示为一个矩形(通俗讲即为“柱子”),而数值则决定了柱子的高度
  • 与“柱状图”相近的概念还有“条形图”,二者在实际使用中可能产生不同理解。例如,在AntV的分类中,条形图是柱状图的子集,专指“横向的柱状图”。而在Excel的图表功能中,柱状图专指纵向、条形图专指横向。
  • 柱状图也衍生出多种多样的图表形式:“分组柱状图”,“簇状柱形图”,“堆叠柱状图”

更多具体请参考:antv的对于柱状图的介绍

注意: 在使用柱状图时,务必要使原点位于0,不要篡改y轴。此外,注意对柱状图进行排序。依据可视化的目的、以及想突出的重点信息,确定合理的排序标准,避免柱状图看起来杂乱无章。

适用场景

  • 柱状图最适合对分类的数据进行比较。尤其是当数值比较接近时,由于人眼对于高度的感知优于其他视觉元素(如面积、角度等),因此,使用柱状图更加合适。
  • 当然最好进行排序也很nice

不适用场景

  • 柱状图要求至少一个分类变量,它们之间是离散的(如一班、二班、三班)(柱子之间有间距)
  • 如何分类是连续的(比如40,50,60,70)请用直方图(柱子之间没有间距)
  • 柱状图最核心的功能是比较,比较的核心是高度。如果人为的改变高度,那么数据间的比例关系会失常;因此,在使用柱状图时,要注意y轴的取值,从0开始。

那么如何用D3绘制柱状图呢?

先看看柱状图的样子:

  我分为5个部分:(目前只介绍svg篇,后续再介绍canvas篇)
  
  1. 画出x轴
  2. 画出y轴
  3. 画出柱子
  4. 画出数据值
  5. 动画(后面专题补上)
### 开始

准备好数据

const data = [
  { year: '1954', sales: 38 },
  { year: '1955', sales: 52 },
  { year: '1956', sales: 61 },
  { year: '1957', sales: 100 },
  { year: '1958', sales: 48 },
  { year: '1959', sales: 38 },
]

我们开始创建一个svg

注意点: svg的元素基本在g里面绘制,当做group

const width = 600;
const height = 400;

 const svg = d3
   .select("body")
   .append("svg")
   .attr("width", width)
   .attr("height", height);
   
 // 创建一个g 当后面元素的group容器,移到(30,30)的位置
 // 定义上下左右边距给坐标轴文字距离
 const m = { top: 30, right: 30, bottom: 30, left: 30 };
 const g = svg.append("g").attr("transform", "translate(30, 30)");
 
 // 实际我们图的高度宽度
 const gW = width - m.left;
 const gH = height - m.top - m.bottom;
定义坐标轴的比例尺,gW为x轴的宽度,gH为y轴的高度:

const x = d3
   .scaleBand()
   .range([0, gW])
   .padding(0.1);  // 柱状图的间距
const y = d3.scaleLinear().range([gH, 0]);
定义好x轴与y轴的定义域:
 x.domain(data.map(item => item.year))
 y.domain([0, d3.max(data, item => item.sales)])
然后就能把x轴,y轴绘制出来了:
g.append("g")
.attr("transform", `translate(0, ${gH})`)
.call(d3.axisBottom(x))
.attr("stroke", "red");

g.append("g").call(d3.axisLeft(y));
我们来绘制柱子吧:
// 然后,再把柱状图的一根根柱子绘制上去,一根柱子就是一个`rect`,
 // 所以要指定它的位置以及长宽:
const barGroup = g
   .selectAll(".bar-gruop")
   .data(data)
   .join("g")
   .attr("class", "bar-gruop");
 // 注释是v4写法
 // .selectAll(".bar-gruop")
 // .data(data)
 // .enter()
 // .append("g")
 // .attr("class", "bar-gruop");

 barGroup
   .append("rect")
   .attr("class", "bar")
   .attr("x", item => x(item.year))
   .attr("y", item => y(item.sales))
   .attr("width", x.bandwidth())
   .attr("height", item => gH - y(item.sales))
   .attr("fill", "steelblue");
画出数据值(当然可以放任意位置)
  barGroup
   .append("text")
   .attr("class", "barText")
   .attr("x", item => x(item.year))
   .attr("y", item => y(item.sales))
   .attr("dx", x.bandwidth() / 2)
   .attr("dy", 20)
   .attr("text-anchor", "middle")
   .text(item => item.sales);

完整柱状图

结尾

看到这里,其实发现绘制一个基本柱状图也就是一个个积木搭建起来的,是不是很有趣,希望你也学会了,去定制更好的。

下一章我们来讲讲饼图的基本绘制。

周末愉快!