子弹图

子弹图的简介

子弹图的样子很像子弹射出后带出的轨道,所以称为子弹图。子弹图的发明是为了取代仪表盘上常见的那种里程表,时速表等基于圆形的信息表达方式。子弹图的特点如下:

  • 每一个单元的子弹图只能显示单一的数据信息源
  • 通过添加合理的度量标尺可以显示更精确的阶段性数据信息
  • 通过优化设计还能够用于表达多项同类数据的对比
  • 可以表达一项数据与不同目标的校对结果

子弹图无修饰的线性表达方式使我们能够在狭小的空间中表达丰富的数据信息,线性的信息表达方式与我们习以为常的文字阅读相似,相对于圆形构图的信息表达,在信息传递上有更大的效能优势。

英文名:Bullet graph

子弹图的构成

图表类型 子弹图
适合的数据 列表:一个分类数据字段、一个连续数据字段、一个范围数组字段、一个目标字段
功能 对比分类数据的数值大小、所处区间以及是否达标
数据与图形的映射 分类数据字段映射到分类轴位置
连续数据字段映射到数据条的长度
目标字段映射到测量标记的刻度轴位置
范围数组映射到背景色条的大小
适合的数据条数 不超过10条数据

子弹图的应用场景

适合的场景

例子1: 显示阶段性数据信息下图是一个模拟商铺一段时间内的经营情况的数据,一共 5 条数据,分别代表收入(单位:千美元)、利率(单位:%)、平均成交额(单位:美元)、新客户(单位:个)和满意度(1-5)五个方面,每个方面都有代表好、中、差的 3 个范围和预先设定的目标。

title ranges actual target subtitle
Revenue [150,225,300] 270 250 US$, in thousands
... ... ... ... ...
var data = [ {"title":"Revenue","subtitle":"US$, in thousands","ranges":[150,225,300],"actual":270,"target":250}, {"title":"Profit","subtitle":"%","ranges":[20,25,30],"actual":23,"target":26}, {"title":"Order size","subtitle":"US$, average","ranges":[350,500,600],"actual":100,"target":550}, {"title":"New Customers","subtitle":"count","ranges":[1400,2000,2500],"actual":1650,"target":2100}, {"title":"Satisfaction","subtitle":"out of 5","ranges":[3.5,4.25,5],"actual":3.2,"target":4.4} ]; var chart = new G2.Chart({ id: 'c1', forceFit: true, height : 500, plotCfg: { margin: [100, 150] } }); chart.legend(false); // 不展示图例 var y = 0; var yGap = 0.1; for(var i=0, l = data.length; i < l; i++) { var ranges = data[i].ranges; var view = chart.createView({ index: i, start: { x: 0, y: y }, end: { x: 1, y: y + yGap } }); view.source([data[i]], { actual: { min: 0, max: ranges[2], nice: false }, target: { min: 0, max: ranges[2], nice: false } }); view.coord().transpose(); view.axis('target', false); view.axis('actual', { position: 'right', title: null }); view.axis('title', { title: null }); view.point().position('title*target').color('#5b0101').shape('line').size(12).style({ lineWidth: 2 }); view.interval().position('title*actual').color('#5b0101').size(15); view.guide().rect([-1, 0], [1, ranges[0]], { fill: '#e96e33', fillOpacity: 0.5 }); view.guide().rect([-1, ranges[0]], [1, ranges[1]], { fill: '#f9ca47', fillOpacity: 0.5 }); view.guide().rect([-1, ranges[1]], [1, ranges[2]], { fill: '#88bb34', fillOpacity: 0.5 }); y += yGap + 0.125; } chart.render();

说明:

  • title 字段,用于区分不同的类型
  • ranges 字段,使用背景色条的长度,表示区间范围
  • actual 字段,使用数据条的长度,表示实际数值
  • target 字段,使用测量标记的刻度轴位置,表示目标值

子弹图的扩展

例子1: 反向子弹图

表达负面(消极)数据时,可以将子弹图做方向上的反转。下图用反向子弹图表示开销的多少。

title ranges actual target
Revenue [1000,2000,5000] 1700 1500
var data = [ {"title":"开销","ranges":[1000,2000,5000],"actual":1700,"target":1500} ]; var chart = new G2.Chart({ id: 'c2', forceFit: true, height : 120, plotCfg: { margin: [40, 150] } }); chart.legend(false); // 不展示图例 var y = 0; var yGap = 1; for(var i=0, l = data.length; i < l; i++) { var ranges = data[i].ranges; var view = chart.createView({ index: i, start: { x: 0, y: y }, end: { x: 1, y: y + yGap } }); view.source([data[i]], { actual: { min: 0, max: ranges[2], nice: false }, target: { min: 0, max: ranges[2], nice: false } }); view.coord().transpose().reflect('x'); view.axis('target', false); view.axis('actual', { position: 'right', title: null }); view.axis('title', { title: null }); view.point().position('title*target').color('#5b0101').shape('line').size(12).style({ lineWidth: 2 }); view.interval().position('title*actual').color('#5b0101').size(15); view.guide().rect([-1, 0], [1, ranges[0]], { fill: '#e96e33', fillOpacity: 0.5 }); view.guide().rect([-1, ranges[0]], [1, ranges[1]], { fill: '#f9ca47', fillOpacity: 0.5 }); view.guide().rect([-1, ranges[1]], [1, ranges[2]], { fill: '#88bb34', fillOpacity: 0.5 }); y += yGap + 0.125; } chart.render();

例子2: 层叠子弹图

表达一些阶段性的数据时,例如,我们定义了全年的额定目标,但是每个季度都会阶段性地显示当前完成的进度,此时就需要同时表达每个季度的数据和全年整体的额定目标数据。

State 第一季度 第二季度 第三季度 第四季度 ranges target
年度收益 3820 6080 2930 5390 [12000,15000,20000] 16000
var data = [ {"State":"年度收益","第一季度":3820,"第二季度":6080,"第三季度":2930,"第四季度":5390,"ranges":[12000,15000,20000],"target":16000} ]; var Stat = G2.Stat; var Frame = G2.Frame; var frame = new Frame(data); frame = Frame.combinColumns(frame,["第一季度","第二季度","第三季度","第四季度"],'金额','季度',['State','target']); var chart = new G2.Chart({ id: 'c3', height: 200, forceFit: true, plotCfg: { margin: [60,150,100,150] } }); chart.legend({ position: 'bottom' }); var ranges = data[0].ranges; var view = chart.createView({ start: { x: 0, y: 0 }, end: { x: 1, y: 1 } }); view.source(frame,{ '金额': { min: 0, max: ranges[2], nice: false }, target: { min: 0, max: ranges[2], nice: false } }); view.axis('State', { title: null }); view.axis('target', false); view.axis('金额', { title: null, position: 'right' }); view.coord().transpose(); view.guide().rect([-1, 0], [1, ranges[0]], { fill: '#de4b70', fillOpacity: 0.5 }); view.guide().rect([-1, ranges[0]], [1, ranges[1]], { fill: '#c125c7', fillOpacity: 0.5 }); view.guide().rect([-1, ranges[1]], [1, ranges[2]], { fill: '#c3c34c', fillOpacity: 0.5 }); view.point().position('State*target').color('#5b0101').shape('line').size(10).style({ lineWidth: 2 }); view.intervalStack().position('State*金额').color('季度').size(12); chart.render();

子弹图与其他图表的对比

子弹图和柱状图

  • 柱状图主要用于多个分类间的数据(大小、数值)的对比
  • 子弹图图主要用于分类各自的数值所处状态和与测量标记的对比,突出的是每个分类自身的情况,没有分类间的比较,用于展示各个分类的子弹图单元相对独立。

变型

标签