mirror of
https://github.com/olehomelchenko/chart-sins.git
synced 2026-08-08 02:22:43 +00:00
Add two sins from wild specimens: category colours, rebinned scales
Both arrived as real charts before the sins existed, which is the better order — the specimen makes the failure mode obvious. "Bar Colours That Contradict Their Labels" (severity 2): categories that already own a colour, drawn in a default palette, so Black is a green bar. Nothing false, which is exactly why it's a 2 under our rubric — but colour is pre-attentive, so the reader pays the cost on every glance. "A Colour Scale That Changed Between Two Charts" (severity 4): the Tagesschau incidence maps, rebinned from 25/50/100/250/500 to 20/35/50/100/200 three weeks apart while being presented as a before-and-after. A colour key is an axis, and moving it mid-comparison is the axis crime we don't police because a legend reads as formatting. The bad chart there needed an explicit independent colour resolve — Vega-Lite merges scales across a concat by default, which silently unified the two legends and destroyed the entire demonstration. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ULE5RRxdQE1ebwefEd2eCM
This commit is contained in:
+14
-8
@@ -17,7 +17,7 @@ runs 2–5 on purpose.
|
|||||||
|
|
||||||
## Sources in play
|
## Sources in play
|
||||||
|
|
||||||
`src/lib/references.ts` currently holds 24 entries, 21 of them cited by the 6
|
`src/lib/references.ts` currently holds 24 entries, 22 of them cited by the 8
|
||||||
published sins. This table is a **convenience, not a scoreboard** — it's here so
|
published sins. This table is a **convenience, not a scoreboard** — it's here so
|
||||||
you can spot a source that fits a sin you're drafting, not so we can drive the
|
you can spot a source that fits a sin you're drafting, not so we can drive the
|
||||||
right-hand column to zero blanks. An uncited reference is not a debt.
|
right-hand column to zero blanks. An uncited reference is not a debt.
|
||||||
@@ -29,28 +29,28 @@ seven sources, give it seven and add whatever's missing to the file.
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `ftvisvocab` | — |
|
| `ftvisvocab` | — |
|
||||||
| `healy` | — |
|
| `healy` | — |
|
||||||
| `schwabish` | — |
|
|
||||||
| `fewdualaxes` | dual-axis-correlation |
|
| `fewdualaxes` | dual-axis-correlation |
|
||||||
| `huff` | truncated-y-axis |
|
| `huff` | truncated-y-axis |
|
||||||
| `isenberg2011` | dual-axis-correlation |
|
| `isenberg2011` | dual-axis-correlation |
|
||||||
| `munzner` | incomplete-pie |
|
| `schwabish` | category-colors |
|
||||||
| `shapeparameter` | aspect-ratio |
|
| `shapeparameter` | aspect-ratio |
|
||||||
| `showmenumbers` | too-many-pie-slices |
|
| `showmenumbers` | too-many-pie-slices |
|
||||||
| `spurious` | dual-axis-correlation |
|
| `spurious` | dual-axis-correlation |
|
||||||
| `swd` | dual-axis-correlation |
|
|
||||||
| `tufte` | truncated-y-axis |
|
| `tufte` | truncated-y-axis |
|
||||||
| `callingbullshit` | inverted-y-axis, truncated-y-axis |
|
|
||||||
| `eagereyes` | aspect-ratio, too-many-pie-slices |
|
| `eagereyes` | aspect-ratio, too-many-pie-slices |
|
||||||
| `fewpies` | incomplete-pie, too-many-pie-slices |
|
| `fewpies` | incomplete-pie, too-many-pie-slices |
|
||||||
| `junkcharts` | inverted-y-axis, too-many-pie-slices |
|
| `junkcharts` | inverted-y-axis, too-many-pie-slices |
|
||||||
|
| `swd` | category-colors, dual-axis-correlation |
|
||||||
| `truthfulart` | aspect-ratio, dual-axis-correlation |
|
| `truthfulart` | aspect-ratio, dual-axis-correlation |
|
||||||
| `vizwtf` | incomplete-pie, inverted-y-axis |
|
|
||||||
| `wilke` | too-many-pie-slices, truncated-y-axis |
|
| `wilke` | too-many-pie-slices, truncated-y-axis |
|
||||||
|
| `callingbullshit` | inverted-y-axis, rebinned-colour-scale, truncated-y-axis |
|
||||||
| `clevelandmcgill` | aspect-ratio, too-many-pie-slices, truncated-y-axis |
|
| `clevelandmcgill` | aspect-ratio, too-many-pie-slices, truncated-y-axis |
|
||||||
| `datatoviz` | dual-axis-correlation, incomplete-pie, too-many-pie-slices |
|
| `datatoviz` | dual-axis-correlation, incomplete-pie, too-many-pie-slices |
|
||||||
| `datawrapper` | dual-axis-correlation, incomplete-pie, too-many-pie-slices |
|
|
||||||
| `economistmistakes` | dual-axis-correlation, inverted-y-axis, truncated-y-axis |
|
| `economistmistakes` | dual-axis-correlation, inverted-y-axis, truncated-y-axis |
|
||||||
| `howchartslie` | aspect-ratio, dual-axis-correlation, inverted-y-axis, truncated-y-axis |
|
| `munzner` | category-colors, incomplete-pie, rebinned-colour-scale |
|
||||||
|
| `vizwtf` | category-colors, incomplete-pie, inverted-y-axis, rebinned-colour-scale |
|
||||||
|
| `datawrapper` | category-colors, dual-axis-correlation, incomplete-pie, rebinned-colour-scale, too-many-pie-slices |
|
||||||
|
| `howchartslie` | aspect-ratio, dual-axis-correlation, inverted-y-axis, rebinned-colour-scale, truncated-y-axis |
|
||||||
|
|
||||||
The citation lists in each entry below are **starting points, not budgets** —
|
The citation lists in each entry below are **starting points, not budgets** —
|
||||||
add to them while drafting.
|
add to them while drafting.
|
||||||
@@ -237,6 +237,12 @@ rediscovering them:
|
|||||||
|
|
||||||
## Two new levers, as of the first wild specimen
|
## Two new levers, as of the first wild specimen
|
||||||
|
|
||||||
|
Three specimens are live so far (`incomplete-pie`, `category-colors`,
|
||||||
|
`rebinned-colour-scale`), all collected from WTF Visualizations. Each one
|
||||||
|
arrived before its sin existed, which is turning out to be the healthier order:
|
||||||
|
a real chart in hand makes the failure mode obvious and stops us inventing sins
|
||||||
|
nobody commits.
|
||||||
|
|
||||||
- **`wild`** — real examples, credited and linked, rendered above the citations.
|
- **`wild`** — real examples, credited and linked, rendered above the citations.
|
||||||
Zero, one or many per sin. A specimen that commits two sins belongs on both
|
Zero, one or many per sin. A specimen that commits two sins belongs on both
|
||||||
pages, with a different note on each; that's a reason to split sins, not merge
|
pages, with a different note on each; that's a reason to split sins, not merge
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"title": "Cars Sold by Colour",
|
||||||
|
"width": 340,
|
||||||
|
"height": 220,
|
||||||
|
"data": {
|
||||||
|
"values": [
|
||||||
|
{ "colour": "White", "sold": 34 },
|
||||||
|
{ "colour": "Black", "sold": 27 },
|
||||||
|
{ "colour": "Grey", "sold": 18 },
|
||||||
|
{ "colour": "Blue", "sold": 9 },
|
||||||
|
{ "colour": "Red", "sold": 7 },
|
||||||
|
{ "colour": "Green", "sold": 3 },
|
||||||
|
{ "colour": "Yellow", "sold": 2 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mark": "bar",
|
||||||
|
"encoding": {
|
||||||
|
"x": {
|
||||||
|
"field": "colour",
|
||||||
|
"type": "nominal",
|
||||||
|
"sort": null,
|
||||||
|
"axis": { "labelAngle": 0, "title": null }
|
||||||
|
},
|
||||||
|
"y": { "field": "sold", "type": "quantitative", "scale": { "zero": true }, "title": "Cars sold" },
|
||||||
|
"color": {
|
||||||
|
"field": "colour",
|
||||||
|
"type": "nominal",
|
||||||
|
"sort": null,
|
||||||
|
"legend": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"title": "Cars Sold by Colour",
|
||||||
|
"width": 340,
|
||||||
|
"height": 220,
|
||||||
|
"data": {
|
||||||
|
"values": [
|
||||||
|
{ "colour": "White", "sold": 34 },
|
||||||
|
{ "colour": "Black", "sold": 27 },
|
||||||
|
{ "colour": "Grey", "sold": 18 },
|
||||||
|
{ "colour": "Blue", "sold": 9 },
|
||||||
|
{ "colour": "Red", "sold": 7 },
|
||||||
|
{ "colour": "Green", "sold": 3 },
|
||||||
|
{ "colour": "Yellow", "sold": 2 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mark": { "type": "bar", "stroke": "#8d8d8d", "strokeWidth": 0.6 },
|
||||||
|
"encoding": {
|
||||||
|
"x": {
|
||||||
|
"field": "colour",
|
||||||
|
"type": "nominal",
|
||||||
|
"sort": null,
|
||||||
|
"axis": { "labelAngle": 0, "title": null }
|
||||||
|
},
|
||||||
|
"y": { "field": "sold", "type": "quantitative", "scale": { "zero": true }, "title": "Cars sold" },
|
||||||
|
"color": {
|
||||||
|
"field": "colour",
|
||||||
|
"type": "nominal",
|
||||||
|
"sort": null,
|
||||||
|
"scale": {
|
||||||
|
"domain": ["White", "Black", "Grey", "Blue", "Red", "Green", "Yellow"],
|
||||||
|
"range": ["#ffffff", "#161616", "#8d8d8d", "#0f62fe", "#da1e28", "#198038", "#f1c21b"]
|
||||||
|
},
|
||||||
|
"legend": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,237 @@
|
|||||||
|
{
|
||||||
|
"title": "7-Day Incidence by District",
|
||||||
|
"data": {
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"district": "A",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "17 March",
|
||||||
|
"value": 22
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "B",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "17 March",
|
||||||
|
"value": 38
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "C",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "17 March",
|
||||||
|
"value": 45
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "D",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "17 March",
|
||||||
|
"value": 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "E",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "17 March",
|
||||||
|
"value": 85
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "F",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "17 March",
|
||||||
|
"value": 110
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "G",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "17 March",
|
||||||
|
"value": 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "H",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "17 March",
|
||||||
|
"value": 190
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "A",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "9 April",
|
||||||
|
"value": 45
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "B",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "9 April",
|
||||||
|
"value": 70
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "C",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "9 April",
|
||||||
|
"value": 88
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "D",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "9 April",
|
||||||
|
"value": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "E",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "9 April",
|
||||||
|
"value": 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "F",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "9 April",
|
||||||
|
"value": 175
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "G",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "9 April",
|
||||||
|
"value": 210
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "H",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "9 April",
|
||||||
|
"value": 260
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hconcat": [
|
||||||
|
{
|
||||||
|
"title": "17 March",
|
||||||
|
"width": 190,
|
||||||
|
"height": 46,
|
||||||
|
"transform": [
|
||||||
|
{
|
||||||
|
"filter": "datum.when === '17 March'"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mark": {
|
||||||
|
"type": "rect",
|
||||||
|
"stroke": "#e0e0e0",
|
||||||
|
"strokeWidth": 0.5
|
||||||
|
},
|
||||||
|
"encoding": {
|
||||||
|
"x": {
|
||||||
|
"field": "district",
|
||||||
|
"type": "nominal",
|
||||||
|
"sort": null,
|
||||||
|
"axis": {
|
||||||
|
"labelAngle": 0,
|
||||||
|
"title": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"field": "row",
|
||||||
|
"type": "nominal",
|
||||||
|
"axis": {
|
||||||
|
"title": null,
|
||||||
|
"labels": false,
|
||||||
|
"ticks": false,
|
||||||
|
"domain": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color": {
|
||||||
|
"field": "value",
|
||||||
|
"type": "quantitative",
|
||||||
|
"scale": {
|
||||||
|
"type": "threshold",
|
||||||
|
"domain": [
|
||||||
|
25,
|
||||||
|
50,
|
||||||
|
100,
|
||||||
|
250
|
||||||
|
],
|
||||||
|
"range": [
|
||||||
|
"#fff1f1",
|
||||||
|
"#ffb3b8",
|
||||||
|
"#fa4d56",
|
||||||
|
"#da1e28",
|
||||||
|
"#750e13"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"legend": {
|
||||||
|
"title": "cases / 100k",
|
||||||
|
"orient": "bottom",
|
||||||
|
"direction": "horizontal"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "9 April",
|
||||||
|
"width": 190,
|
||||||
|
"height": 46,
|
||||||
|
"transform": [
|
||||||
|
{
|
||||||
|
"filter": "datum.when === '9 April'"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mark": {
|
||||||
|
"type": "rect",
|
||||||
|
"stroke": "#e0e0e0",
|
||||||
|
"strokeWidth": 0.5
|
||||||
|
},
|
||||||
|
"encoding": {
|
||||||
|
"x": {
|
||||||
|
"field": "district",
|
||||||
|
"type": "nominal",
|
||||||
|
"sort": null,
|
||||||
|
"axis": {
|
||||||
|
"labelAngle": 0,
|
||||||
|
"title": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"field": "row",
|
||||||
|
"type": "nominal",
|
||||||
|
"axis": {
|
||||||
|
"title": null,
|
||||||
|
"labels": false,
|
||||||
|
"ticks": false,
|
||||||
|
"domain": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color": {
|
||||||
|
"field": "value",
|
||||||
|
"type": "quantitative",
|
||||||
|
"scale": {
|
||||||
|
"type": "threshold",
|
||||||
|
"domain": [
|
||||||
|
20,
|
||||||
|
35,
|
||||||
|
50,
|
||||||
|
100,
|
||||||
|
200
|
||||||
|
],
|
||||||
|
"range": [
|
||||||
|
"#fff1f1",
|
||||||
|
"#ffd7d9",
|
||||||
|
"#ffb3b8",
|
||||||
|
"#fa4d56",
|
||||||
|
"#da1e28",
|
||||||
|
"#750e13"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"legend": {
|
||||||
|
"title": "cases / 100k",
|
||||||
|
"orient": "bottom",
|
||||||
|
"direction": "horizontal"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"resolve": {
|
||||||
|
"scale": {
|
||||||
|
"color": "independent"
|
||||||
|
},
|
||||||
|
"legend": {
|
||||||
|
"color": "independent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
{
|
||||||
|
"title": "7-Day Incidence by District",
|
||||||
|
"data": {
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"district": "A",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "17 March",
|
||||||
|
"value": 22
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "B",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "17 March",
|
||||||
|
"value": 38
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "C",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "17 March",
|
||||||
|
"value": 45
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "D",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "17 March",
|
||||||
|
"value": 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "E",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "17 March",
|
||||||
|
"value": 85
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "F",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "17 March",
|
||||||
|
"value": 110
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "G",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "17 March",
|
||||||
|
"value": 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "H",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "17 March",
|
||||||
|
"value": 190
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "A",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "9 April",
|
||||||
|
"value": 45
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "B",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "9 April",
|
||||||
|
"value": 70
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "C",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "9 April",
|
||||||
|
"value": 88
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "D",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "9 April",
|
||||||
|
"value": 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "E",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "9 April",
|
||||||
|
"value": 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "F",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "9 April",
|
||||||
|
"value": 175
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "G",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "9 April",
|
||||||
|
"value": 210
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"district": "H",
|
||||||
|
"row": "7-day incidence",
|
||||||
|
"when": "9 April",
|
||||||
|
"value": 260
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hconcat": [
|
||||||
|
{
|
||||||
|
"title": "17 March",
|
||||||
|
"width": 190,
|
||||||
|
"height": 46,
|
||||||
|
"transform": [
|
||||||
|
{
|
||||||
|
"filter": "datum.when === '17 March'"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mark": {
|
||||||
|
"type": "rect",
|
||||||
|
"stroke": "#e0e0e0",
|
||||||
|
"strokeWidth": 0.5
|
||||||
|
},
|
||||||
|
"encoding": {
|
||||||
|
"x": {
|
||||||
|
"field": "district",
|
||||||
|
"type": "nominal",
|
||||||
|
"sort": null,
|
||||||
|
"axis": {
|
||||||
|
"labelAngle": 0,
|
||||||
|
"title": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"field": "row",
|
||||||
|
"type": "nominal",
|
||||||
|
"axis": {
|
||||||
|
"title": null,
|
||||||
|
"labels": false,
|
||||||
|
"ticks": false,
|
||||||
|
"domain": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color": {
|
||||||
|
"field": "value",
|
||||||
|
"type": "quantitative",
|
||||||
|
"scale": {
|
||||||
|
"type": "threshold",
|
||||||
|
"domain": [
|
||||||
|
25,
|
||||||
|
50,
|
||||||
|
100,
|
||||||
|
250
|
||||||
|
],
|
||||||
|
"range": [
|
||||||
|
"#fff1f1",
|
||||||
|
"#ffb3b8",
|
||||||
|
"#fa4d56",
|
||||||
|
"#da1e28",
|
||||||
|
"#750e13"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"legend": {
|
||||||
|
"title": "cases / 100k",
|
||||||
|
"orient": "bottom",
|
||||||
|
"direction": "horizontal"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "9 April",
|
||||||
|
"width": 190,
|
||||||
|
"height": 46,
|
||||||
|
"transform": [
|
||||||
|
{
|
||||||
|
"filter": "datum.when === '9 April'"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mark": {
|
||||||
|
"type": "rect",
|
||||||
|
"stroke": "#e0e0e0",
|
||||||
|
"strokeWidth": 0.5
|
||||||
|
},
|
||||||
|
"encoding": {
|
||||||
|
"x": {
|
||||||
|
"field": "district",
|
||||||
|
"type": "nominal",
|
||||||
|
"sort": null,
|
||||||
|
"axis": {
|
||||||
|
"labelAngle": 0,
|
||||||
|
"title": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"field": "row",
|
||||||
|
"type": "nominal",
|
||||||
|
"axis": {
|
||||||
|
"title": null,
|
||||||
|
"labels": false,
|
||||||
|
"ticks": false,
|
||||||
|
"domain": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color": {
|
||||||
|
"field": "value",
|
||||||
|
"type": "quantitative",
|
||||||
|
"scale": {
|
||||||
|
"type": "threshold",
|
||||||
|
"domain": [
|
||||||
|
25,
|
||||||
|
50,
|
||||||
|
100,
|
||||||
|
250
|
||||||
|
],
|
||||||
|
"range": [
|
||||||
|
"#fff1f1",
|
||||||
|
"#ffb3b8",
|
||||||
|
"#fa4d56",
|
||||||
|
"#da1e28",
|
||||||
|
"#750e13"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"legend": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"resolve": {
|
||||||
|
"scale": {
|
||||||
|
"color": "shared"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
---
|
||||||
|
title: "Bar Colours That Contradict Their Labels"
|
||||||
|
summary: "The categories already have colours. The chart assigned different ones anyway."
|
||||||
|
poke: "Your categories are colours, and you let the software pick different ones. The reader now has to override what they can see in order to read the chart."
|
||||||
|
category: "Sloppy Craft"
|
||||||
|
severity: 2
|
||||||
|
tags: ["bar chart", "colour", "encoding"]
|
||||||
|
badChart: "category-color-bad"
|
||||||
|
fixedChart: "category-color-fixed"
|
||||||
|
related:
|
||||||
|
- too-many-pie-slices
|
||||||
|
date: 2026-08-05
|
||||||
|
wild:
|
||||||
|
- caption: "“Shoe color Frequency” — a survey of shoe colours, plotted in a spreadsheet's default palette."
|
||||||
|
note: "Every category is a colour, and not one bar matches. Black is a green bar. White is a blue bar. Red is orange, grey is teal, brown is navy. The numbers are all correct — you simply cannot use the fastest channel on the chart, because it has been filled with an answer to a different question."
|
||||||
|
image: "shoe-colour-frequency.jpg"
|
||||||
|
alt: "Bar chart titled Shoe color Frequency with categories White, Blue, Yellow, Black, Red, Grey and Brown, each drawn in an unrelated default palette colour."
|
||||||
|
credit: "Spotted in the wild; original author unknown"
|
||||||
|
sourceLabel: "collected by WTF Visualizations"
|
||||||
|
sourceUrl: "https://viz.wtf/post/639589038118125568"
|
||||||
|
citations:
|
||||||
|
- key: munzner
|
||||||
|
note: "Colour is a channel that carries meaning whether or not you meant it to. Spending it on nothing is the cheap version of this mistake; spending it on the wrong thing is this one."
|
||||||
|
- key: swd
|
||||||
|
note: "Knaflic's rule that every colour on a chart should be a decision, made for a reason the reader can recover."
|
||||||
|
- key: datawrapper
|
||||||
|
note: "Their colour writing keeps returning to the same point: a palette is a mapping to data, not decoration."
|
||||||
|
- key: vizwtf
|
||||||
|
note: "Where the specimen above was collected."
|
||||||
|
- key: schwabish
|
||||||
|
note: "Chart-by-chart on matching the encoding to what the reader is actually being asked to do."
|
||||||
|
---
|
||||||
|
|
||||||
|
## The sin
|
||||||
|
|
||||||
|
Some categories arrive with a colour already attached. Colours themselves,
|
||||||
|
obviously — but also political parties, sports teams, brands, traffic-light
|
||||||
|
statuses, the red and black of a balance sheet. The reader knows the mapping
|
||||||
|
before they reach your chart.
|
||||||
|
|
||||||
|
Ignore it and you don't get a neutral chart. You get one that argues with
|
||||||
|
itself: a bar labelled *Black* drawn in green, one labelled *White* drawn in
|
||||||
|
blue. Every value is correct and the chart is still exhausting, because the
|
||||||
|
fastest channel on the page is now pointing somewhere the labels aren't.
|
||||||
|
|
||||||
|
## Why it deceives
|
||||||
|
|
||||||
|
"Deceives" is generous — nothing here is false, which is why this is a two and
|
||||||
|
not a four. But colour is pre-attentive: it lands before you've read a single
|
||||||
|
label. So the chart hands the reader an answer, the labels hand them a different
|
||||||
|
one, and the reader has to consciously suppress the first to trust the second.
|
||||||
|
|
||||||
|
That cost is paid on every glance, by every reader, forever, to save the author
|
||||||
|
one minute. And it doesn't stay merely annoying: anyone skimming, quoting from
|
||||||
|
memory, or looking at a thumbnail will take the colour at face value.
|
||||||
|
|
||||||
|
## The repentance
|
||||||
|
|
||||||
|
If a category has a natural colour, use it. The fix above is not clever — it
|
||||||
|
assigns each bar the colour it is named after, and adds a hairline stroke so
|
||||||
|
white and yellow keep an edge against the canvas.
|
||||||
|
|
||||||
|
If your categories *don't* have natural colours, the other repentance applies:
|
||||||
|
don't colour them at all. Seven bars in seven hues where a single hue would do
|
||||||
|
is the same mistake wearing a nicer coat — colour that encodes nothing but is
|
||||||
|
read as though it encodes something. One colour, or one highlighted bar and the
|
||||||
|
rest in grey.
|
||||||
|
|
||||||
|
The test is simple. Ask what a reader would conclude from the colours alone. If
|
||||||
|
the answer is "something wrong," or "nothing," change them.
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
---
|
||||||
|
title: "A Colour Scale That Changed Between Two Charts"
|
||||||
|
summary: "Two snapshots of the same measure, placed side by side, with the legend quietly rebinned in between."
|
||||||
|
poke: "You changed the bins between the before and the after. Whatever the reader sees in that comparison, some of it is your legend, not your data."
|
||||||
|
category: "Misleading Scales"
|
||||||
|
severity: 4
|
||||||
|
tags: ["colour scale", "comparison", "heatmap", "map"]
|
||||||
|
badChart: "rebinned-scale-bad"
|
||||||
|
fixedChart: "rebinned-scale-fixed"
|
||||||
|
compareNote: "The same readings in both, at both dates. Only the legend changed."
|
||||||
|
related:
|
||||||
|
- truncated-y-axis
|
||||||
|
- aspect-ratio
|
||||||
|
date: 2026-08-05
|
||||||
|
wild:
|
||||||
|
- caption: "Tagesschau, 7-day COVID incidence by German district: 17 March 2021 beside 9 April 2021."
|
||||||
|
note: "The two maps are the same measure three weeks apart, and they invite exactly one reading: look how much worse it got. But the legend was rebinned in between. On 17 March the bands ran to 25 / 50 / 100 / 250 / 500; on 9 April, to 20 / 35 / 50 / 100 / 200 / above. A district sitting at 150 is a mid-orange on the left map and near-black on the right, without having moved at all. Incidence did rise over those three weeks — that part is real. What the pair can't tell you is how much of the colour change is the virus and how much is the key."
|
||||||
|
image: "tagesschau-incidence-bins.jpg"
|
||||||
|
alt: "Two choropleth maps of Germany showing 7-day COVID incidence by district, dated 17 March and 9 April 2021, each with a different set of legend thresholds."
|
||||||
|
credit: "Maps by Tagesschau (ARD), data from the Robert Koch-Institut"
|
||||||
|
sourceLabel: "collected by WTF Visualizations"
|
||||||
|
sourceUrl: "https://viz.wtf/post/648282602475798528/german-main-news-website-changed-the-categrories"
|
||||||
|
citations:
|
||||||
|
- key: howchartslie
|
||||||
|
note: "Cairo on how the encoding, not the data, does the arguing when a reader compares two pictures."
|
||||||
|
- key: callingbullshit
|
||||||
|
note: "The axis-crime module, generalised: a colour key is an axis, and rescaling it mid-comparison is the same move."
|
||||||
|
- key: datawrapper
|
||||||
|
note: "Their colour-scale series is explicit that where you put the bin edges changes what readers see — the choice is never neutral."
|
||||||
|
- key: munzner
|
||||||
|
note: "Binning a continuous quantity is an encoding decision, and comparability depends on holding it fixed."
|
||||||
|
- key: vizwtf
|
||||||
|
note: "Where the specimen above was collected."
|
||||||
|
---
|
||||||
|
|
||||||
|
## The sin
|
||||||
|
|
||||||
|
Two panels, same measure, different dates, side by side. The reader is being
|
||||||
|
invited to compare them — that's the only reason to publish them together.
|
||||||
|
|
||||||
|
Then the bins move. The right-hand panel above uses a key that turns darker,
|
||||||
|
sooner, than the left one. Its districts are genuinely worse, but the picture
|
||||||
|
overstates by however much the key was tightened, and there is no way to
|
||||||
|
separate the two by looking.
|
||||||
|
|
||||||
|
## Why it deceives
|
||||||
|
|
||||||
|
A colour key is an axis. It maps a number to something the eye reads directly,
|
||||||
|
and every intuition about not moving the y-axis between two charts applies to it
|
||||||
|
unchanged — we just police it less, because a legend looks like a formatting
|
||||||
|
choice rather than a scale.
|
||||||
|
|
||||||
|
It's also uniquely hard to catch. A shifted y-axis leaves visible evidence: the
|
||||||
|
numbers are right there, and a suspicious reader can check them. Here the reader
|
||||||
|
would have to hold five thresholds from one legend in their head, read the five
|
||||||
|
in the other, and notice they differ — while looking at two attractive maps that
|
||||||
|
are clearly telling a story.
|
||||||
|
|
||||||
|
And rebinning is often *innocent* in origin, which is what keeps it alive. As a
|
||||||
|
number climbs, the old bands stop discriminating — everything saturates at the
|
||||||
|
top colour and the map goes flat. Rescaling genuinely does make each map, on its
|
||||||
|
own, more informative. The sin is doing it and still presenting the pair as a
|
||||||
|
before-and-after.
|
||||||
|
|
||||||
|
## The repentance
|
||||||
|
|
||||||
|
One key for every panel you want compared. That's the whole rule, and the fixed
|
||||||
|
version above does nothing else: same data, same dates, one legend serving both.
|
||||||
|
The rise is still perfectly visible — it just isn't amplified.
|
||||||
|
|
||||||
|
If the range really has outgrown the old bands, you have two honest options.
|
||||||
|
Rebin *both* panels, so the comparison stays like-for-like. Or keep the panels
|
||||||
|
separate, drop the side-by-side framing, and let each map be a snapshot rather
|
||||||
|
than half of an implied trend.
|
||||||
|
|
||||||
|
And if the two panels must carry different keys, say so loudly, in the chart —
|
||||||
|
not in a caption underneath, where it will be cropped off the first time anyone
|
||||||
|
shares it.
|
||||||
Reference in New Issue
Block a user