Skip to content

Instantly share code, notes, and snippets.

@dm-p
Created August 20, 2022 05:24
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save dm-p/3a4c154a61bb54599b7c7a7c00dff562 to your computer and use it in GitHub Desktop.
Save dm-p/3a4c154a61bb54599b7c7a7c00dff562 to your computer and use it in GitHub Desktop.
Reproduction of "The Mundigl Bullets" using Deneb & Vega-Lite
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"usermeta": {
"deneb": {
"build": "1.3.0.0",
"metaVersion": 1,
"provider": "vegaLite",
"providerVersion": "5.2.0"
},
"interactivity": {
"tooltip": true,
"contextMenu": true,
"selection": false,
"highlight": false,
"dataPointLimit": 50
},
"information": {
"name": "Mundigl Bullet Chart",
"description": "An interpretation of the bullet chart variations by Robert Mundigl: https://www.clearlyandsimply.com/clearly_and_simply/2017/07/variations-of-alternative-bullet-graphs-in-excel.html",
"author": "Daniel Marsh-Patrick (inspired by Robert Mundigl)",
"uuid": "c7017285-b3cb-4633-b9ef-cb3cefa8c2fb",
"generated": "2022-08-20T05:23:38.967Z",
"previewImageBase64PNG": "data:image/png;base64,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"
},
"dataset": [
{
"key": "__0__",
"name": "Category",
"description": "Category column for each bar",
"type": "text",
"kind": "column"
},
{
"key": "__1__",
"name": "Value",
"description": "Main bar value",
"type": "numeric",
"kind": "measure"
},
{
"key": "__2__",
"name": "Target",
"description": "Target value",
"type": "numeric",
"kind": "measure"
},
{
"key": "__3__",
"name": "Gap",
"description": "Gap to target",
"type": "numeric",
"kind": "measure"
}
]
},
"config": {
"view": {"stroke": "transparent"},
"font": "Segoe UI",
"axis": {
"ticks": false,
"grid": false,
"domain": false,
"title": "",
"labelColor": "#605E5C",
"labelFontSize": 12,
"titleFont": "wf_standard-font, helvetica, arial, sans-serif",
"titleColor": "#252423",
"titleFontSize": 16,
"titleFontWeight": "normal",
"labelPadding": 10
},
"tick": {
"stroke": "white",
"strokeWidth": 1,
"thickness": 3
},
"point": {
"filled": true,
"size": 50,
"opacity": 1
},
"style": {
"target": {"color": "#cd3838"},
"value_label": {
"color": "white",
"align": "left",
"dx": 5
}
}
},
"data": {"name": "dataset"},
"height": {"step": 30},
"encoding": {
"x": {
"type": "quantitative",
"axis": null
},
"y": {
"field": "__0__",
"sort": {
"field": "__1__",
"op": "sum",
"order": "descending"
}
}
},
"params": [
{
"name": "bar_color",
"value": "#aaa"
}
],
"transform": [
{
"calculate": "datum['__3__'] < 0",
"as": "gap_is_negative"
},
{
"calculate": "datum['gap_is_negative'] ? '#fff' : bar_color",
"as": "gap_color"
},
{
"calculate": "datum['gap_is_negative'] ? bar_color : '#fff'",
"as": "gap_color_inverse"
},
{
"calculate": "datum['__2__'] - (datum['__3__'] / 2)",
"as": "gap_label_x"
}
],
"layer": [
{
"description": "Main value bar",
"mark": {
"type": "bar",
"color": {"expr": "bar_color"},
"height": {"band": 0.8}
},
"encoding": {
"x": {"field": "__1__"}
}
},
{
"description": "Target tick",
"mark": {
"type": "tick",
"style": ["target"],
"height": {
"expr": "bandwidth('y')"
}
},
"encoding": {
"x": {"field": "__2__"}
}
},
{
"description": "Value label",
"mark": {
"type": "text",
"style": ["value_label"]
},
"encoding": {
"x": {"datum": 0},
"text": {
"field": "__1__",
"format": "#,###,",
"formatType": "pbiFormat"
}
}
},
{
"description": "Target label",
"layer": [
{
"description": "Target label foreground",
"mark": {
"type": "text",
"style": ["target"],
"align": {
"expr": "datum['gap_is_negative'] ? 'right' : 'left'"
},
"dx": {
"expr": "datum['gap_is_negative'] ? -10 : 10"
}
}
}
],
"encoding": {
"x": {"field": "__2__"},
"text": {
"field": "__2__",
"format": "#,###,",
"formatType": "pbiFormat"
}
}
},
{
"description": "Gap arrow main layer",
"encoding": {
"color": {
"value": {
"expr": "datum['gap_color']"
}
}
},
"layer": [
{
"description": "Gap arrow body",
"mark": {"type": "rule"},
"encoding": {
"x": {"field": "__1__"},
"x2": {"field": "__2__"}
}
},
{
"description": "Gap arrow head (left)",
"mark": {
"type": "point",
"shape": "triangle-left",
"xOffset": 3
},
"encoding": {
"x": {
"value": {
"expr": "scale('x', datum['gap_is_negative'] ? datum['__2__'] : datum['__1__'])"
}
}
}
},
{
"description": "Gap arrow head (right)",
"mark": {
"type": "point",
"shape": "triangle-right",
"xOffset": -3
},
"encoding": {
"x": {
"value": {
"expr": "scale('x', datum['gap_is_negative'] ? datum['__1__'] : datum['__2__'])"
}
}
}
}
]
},
{
"description": "Gap label main layer",
"encoding": {
"x": {"field": "gap_label_x"},
"text": {
"field": "__3__",
"format": "#,###,;#,##,",
"formatType": "pbiFormat"
}
},
"layer": [
{
"description": "Gap label halo (background)",
"mark": {
"type": "text",
"stroke": {
"expr": "datum['gap_color_inverse']"
},
"strokeWidth": 4
}
},
{
"description": "Gap label halo (background)",
"mark": {
"type": "text",
"color": {
"expr": "datum['gap_color']"
}
}
}
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment