mirror of
https://github.com/olehomelchenko/chart-sins.git
synced 2026-08-08 02:22:43 +00:00
Add inverted-axis and aspect-ratio sins; rename slugs
Writes two sins off the backlog. The inverted y-axis is the egregious one: the fill hangs from the top and the line descends while the values climb, with every element individually defensible. Aspect ratio is the quiet one: same twelve numbers, same y-range, only the shape of the box changes. Adds Cleveland, McGill & McGill 1988 to the canon — banking to 45 comes from the shape-parameter paper, not the 1984 ranking already cited, so citing the latter for it would have misattributed the claim. Also renames sin slugs to match their retitled pages, and rewrites the summaries that had started echoing those titles. Gives line-chart points their mark color explicitly; "point": true was falling back to Vega's default blue on a red or green line. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ULE5RRxdQE1ebwefEd2eCM
This commit is contained in:
+93
-64
@@ -5,21 +5,29 @@ condemned by something already in the canon** (`src/lib/references.ts`). We
|
|||||||
don't invent authority, so the backlog is mined *from* the sources rather than
|
don't invent authority, so the backlog is mined *from* the sources rather than
|
||||||
brainstormed and back-filled with citations.
|
brainstormed and back-filled with citations.
|
||||||
|
|
||||||
|
The list deliberately mixes registers. Some sins are subtle enough that a
|
||||||
|
competent analyst commits them by accident; others are arithmetically void and
|
||||||
|
indefensible on sight. Both belong — the egregious ones are the most *sendable*
|
||||||
|
pages we can write, because there's nothing to argue about, and the subtle ones
|
||||||
|
are what keep the site useful to people who already know the basics. Severity
|
||||||
|
runs 2–5 on purpose.
|
||||||
|
|
||||||
## Canon coverage
|
## Canon coverage
|
||||||
|
|
||||||
Which references are already doing work, after the first three sins:
|
Which references are doing work, after the first five sins:
|
||||||
|
|
||||||
| Reference | Cited by |
|
| Reference | Cited by |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `callingbullshit` | truncated-y-axis |
|
| `callingbullshit` | truncated-y-axis, inverted-y-axis |
|
||||||
| `clevelandmcgill` | truncated-y-axis, pie-chart-overload |
|
| `clevelandmcgill` | truncated-y-axis, too-many-pie-slices, aspect-ratio |
|
||||||
| `tufte` | truncated-y-axis |
|
| `tufte` | truncated-y-axis |
|
||||||
| `howchartslie` | truncated-y-axis, dual-axis-deception |
|
| `howchartslie` | truncated-y-axis, two-y-axes, inverted-y-axis, aspect-ratio |
|
||||||
| `datatoviz` | dual-axis-deception, pie-chart-overload |
|
| `datatoviz` | two-y-axes, too-many-pie-slices |
|
||||||
| `spurious` | dual-axis-deception |
|
| `spurious` | two-y-axes |
|
||||||
| `swd` | dual-axis-deception |
|
| `swd` | two-y-axes |
|
||||||
| `fewpies` | pie-chart-overload |
|
| `fewpies` | too-many-pie-slices |
|
||||||
| `junkcharts` | pie-chart-overload |
|
| `junkcharts` | too-many-pie-slices, inverted-y-axis |
|
||||||
|
| `shapeparameter` | aspect-ratio |
|
||||||
| **`munzner`** | — |
|
| **`munzner`** | — |
|
||||||
| **`wilke`** | — |
|
| **`wilke`** | — |
|
||||||
| **`healy`** | — |
|
| **`healy`** | — |
|
||||||
@@ -41,8 +49,7 @@ sits on. The backlog is ordered to bring them all in.
|
|||||||
The trend runs one way over five years and the other way over five months, so
|
The trend runs one way over five years and the other way over five months, so
|
||||||
the chart shows the five months. Nothing about the chart is technically false —
|
the chart shows the five months. Nothing about the chart is technically false —
|
||||||
the axis starts at zero, the labels are honest — which is what makes it the
|
the axis starts at zero, the labels are honest — which is what makes it the
|
||||||
nastiest sin on this list and a good counterweight to the three scale/type sins
|
nastiest sin on this list.
|
||||||
we already have.
|
|
||||||
|
|
||||||
**Charts:** one series, two views. Bad = the flattering slice. Fixed = the full
|
**Charts:** one series, two views. Bad = the flattering slice. Fixed = the full
|
||||||
record with the slice shaded in place. Literally the same dataset filtered, so
|
record with the slice shaded in place. Literally the same dataset filtered, so
|
||||||
@@ -52,21 +59,64 @@ we can build.
|
|||||||
**Citations:** `huff` (the original con), `callingbullshit`, `howchartslie`,
|
**Citations:** `huff` (the original con), `callingbullshit`, `howchartslie`,
|
||||||
`junkcharts`.
|
`junkcharts`.
|
||||||
|
|
||||||
### 2. Group Averages Shown Without the Distribution — *Hiding the Data*, severity 4
|
### 2. Pie Slices That Sum to More Than 100% — *Impossible Wholes*, severity 5
|
||||||
|
|
||||||
|
**Poke:** "Your slices add up to 180%. A pie has one job — dividing a whole —
|
||||||
|
and this data has no whole to divide."
|
||||||
|
|
||||||
|
The multi-select survey is the classic source: "which of these tools do you
|
||||||
|
use?", respondents tick three each, and the results get poured into a pie. The
|
||||||
|
wedges are now sized as fractions of a total that doesn't exist, so every one of
|
||||||
|
them is drawn wrong, and the "share" each appears to hold is pure artifact.
|
||||||
|
|
||||||
|
Unlike most of this list, there's no judgment call — the chart is arithmetically
|
||||||
|
void. That's the appeal: nothing to argue about, and no way to defend it in a
|
||||||
|
meeting.
|
||||||
|
|
||||||
|
**Charts:** bad = pie of multi-select response counts, wedges summing past the
|
||||||
|
circle. Fixed = bar chart, one bar per option, each labelled "% of respondents"
|
||||||
|
with the base stated. Same counts.
|
||||||
|
|
||||||
|
**Citations:** `fewpies`, `datawrapper` (their pie-chart guidance covers when the
|
||||||
|
form applies at all), `datatoviz`, `junkcharts`.
|
||||||
|
|
||||||
|
### 3. A Pie Chart of Rates or Averages — *Impossible Wholes*, severity 5
|
||||||
|
|
||||||
|
**Poke:** "You made a pie out of averages. Adding them together produces a
|
||||||
|
number that means nothing, and that number is your denominator."
|
||||||
|
|
||||||
|
The other half of the same mistake, and the one people defend for longer.
|
||||||
|
Average deal size by region, conversion rate by channel, satisfaction score by
|
||||||
|
team — relative metrics, each with its own denominator, stacked into wedges as
|
||||||
|
if they were parts of a shared total. A region with a high *average* takes a big
|
||||||
|
slice regardless of how many deals it actually did.
|
||||||
|
|
||||||
|
Worth its own page rather than folding into the entry above: the tell is
|
||||||
|
different (these numbers *can* sum to 100% by coincidence), and the person who
|
||||||
|
needs sending here is making a different error — a category mistake about what
|
||||||
|
their metric is, not a counting mistake.
|
||||||
|
|
||||||
|
**Charts:** bad = pie of average order value by region. Fixed = bar chart of the
|
||||||
|
same averages, plus the volume behind each one so the weighting is visible.
|
||||||
|
|
||||||
|
**Citations:** `fewpies`, `munzner` (part-to-whole is a composition of a whole,
|
||||||
|
or it is nothing), `schwabish`, `ftvisvocab`.
|
||||||
|
|
||||||
|
### 4. Group Averages Shown Without the Distribution — *Hiding the Data*, severity 4
|
||||||
|
|
||||||
**Poke:** "Four bars, four averages, and no idea whether the difference is real."
|
**Poke:** "Four bars, four averages, and no idea whether the difference is real."
|
||||||
|
|
||||||
The bar-of-means: every group collapsed to one number, so wildly different
|
The bar-of-means: every group collapsed to one number, so wildly different
|
||||||
distributions draw identical bars. Wilke devotes a section to exactly this and
|
distributions draw identical bars. Wilke devotes a section to exactly this and
|
||||||
Healy makes the perceptual case; it's the natural home for three of our six idle
|
Healy makes the perceptual case; it's the natural home for three idle references
|
||||||
references at once.
|
at once.
|
||||||
|
|
||||||
**Charts:** bad = grouped bar of means. Fixed = strip/box plot over the same
|
**Charts:** bad = grouped bar of means. Fixed = strip/box plot over the same
|
||||||
observations, means still marked.
|
observations, means still marked.
|
||||||
|
|
||||||
**Citations:** `wilke`, `healy`, `schwabish`, `datatoviz`.
|
**Citations:** `wilke`, `healy`, `schwabish`, `datatoviz`.
|
||||||
|
|
||||||
### 3. Bubbles Sized by Radius Instead of Area — *Perceptual Traps*, severity 4
|
### 5. Bubbles Sized by Radius Instead of Area — *Perceptual Traps*, severity 4
|
||||||
|
|
||||||
**Poke:** "You doubled the number and quadrupled the ink."
|
**Poke:** "You doubled the number and quadrupled the ink."
|
||||||
|
|
||||||
@@ -82,7 +132,28 @@ position does the work.
|
|||||||
|
|
||||||
**Citations:** `munzner`, `clevelandmcgill`, `tufte`, `howchartslie`.
|
**Citations:** `munzner`, `clevelandmcgill`, `tufte`, `howchartslie`.
|
||||||
|
|
||||||
### 4. Categories Sorted Alphabetically Instead of by Value — *Sloppy Craft*, severity 2
|
### 6. Stacked Segments Without a Shared Baseline — *Perceptual Traps*, severity 3
|
||||||
|
|
||||||
|
**Poke:** "Only the bottom band of a stacked chart has a straight edge to
|
||||||
|
measure against. The rest are guesses."
|
||||||
|
|
||||||
|
In a stacked column chart, every segment except the bottom one starts at a
|
||||||
|
baseline that moves from column to column, so readers can compare the total and
|
||||||
|
the bottom series — and nothing else. The series you actually care about is
|
||||||
|
usually floating somewhere in the middle. Datawrapper's guidance is blunt about
|
||||||
|
it: bring the most important value to the bottom, because readers can only
|
||||||
|
compare values that share a baseline.
|
||||||
|
|
||||||
|
Ubiquitous in business dashboards, which is what earns it a slot despite the
|
||||||
|
milder severity.
|
||||||
|
|
||||||
|
**Charts:** bad = stacked columns with the interesting series stranded in the
|
||||||
|
middle. Fixed = the same numbers as small multiples (or the key series pulled
|
||||||
|
out to its own zero baseline), totals preserved.
|
||||||
|
|
||||||
|
**Citations:** `datawrapper`, `wilke`, `munzner`, `ftvisvocab`.
|
||||||
|
|
||||||
|
### 7. Categories Sorted Alphabetically Instead of by Value — *Sloppy Craft*, severity 2
|
||||||
|
|
||||||
**Poke:** "Your categories are sorted by name. Nobody wants to know which
|
**Poke:** "Your categories are sorted by name. Nobody wants to know which
|
||||||
region starts with A."
|
region starts with A."
|
||||||
@@ -96,48 +167,6 @@ sorted by value.
|
|||||||
|
|
||||||
**Citations:** `schwabish`, `ftvisvocab`, `swd`, `fewpies`.
|
**Citations:** `schwabish`, `ftvisvocab`, `swd`, `fewpies`.
|
||||||
|
|
||||||
### 5. Stacked Segments Without a Shared Baseline — *Perceptual Traps*, severity 3
|
|
||||||
|
|
||||||
**Poke:** "Only the bottom band of a stacked chart has a straight edge to
|
|
||||||
measure against. The rest are guesses."
|
|
||||||
|
|
||||||
In a stacked column chart, every segment except the bottom one starts at a
|
|
||||||
baseline that moves from column to column, so readers can compare the total and
|
|
||||||
the bottom series — and nothing else. The series you actually care about is
|
|
||||||
usually floating somewhere in the middle. Datawrapper's guidance is blunt about
|
|
||||||
it: bring the most important value to the bottom, because readers can only
|
|
||||||
compare values that share a baseline.
|
|
||||||
|
|
||||||
Ubiquitous in business dashboards and highly sendable, which is why it outranks
|
|
||||||
the two below despite a milder severity.
|
|
||||||
|
|
||||||
**Charts:** bad = stacked columns with the interesting series stranded in the
|
|
||||||
middle. Fixed = the same numbers as small multiples (or the key series pulled
|
|
||||||
out to its own zero baseline), totals preserved.
|
|
||||||
|
|
||||||
**Citations:** `datawrapper`, `wilke`, `munzner`, `ftvisvocab`.
|
|
||||||
|
|
||||||
### 6. An Inverted Y-Axis — *Misleading Scales*, severity 5
|
|
||||||
|
|
||||||
**Poke:** "Down means up. You flipped the axis and the trend flipped with it."
|
|
||||||
|
|
||||||
The famous one. High drama, trivial to author (`scale: {reverse: true}`), and it
|
|
||||||
slots into an existing category. Ranked below the others only because the canon
|
|
||||||
support overlaps almost entirely with sins we've already published.
|
|
||||||
|
|
||||||
**Citations:** `callingbullshit`, `howchartslie`, `junkcharts`.
|
|
||||||
|
|
||||||
### 7. Aspect Ratio That Exaggerates a Trend — *Misleading Scales*, severity 3
|
|
||||||
|
|
||||||
**Poke:** "Same data, twice as tall. Aspect ratio is not a styling choice."
|
|
||||||
|
|
||||||
Cleveland's banking-to-45° argument: the identical series reads as a crisis or a
|
|
||||||
plateau depending on the box you draw it in. Cheap to author — one dataset, two
|
|
||||||
`width`/`height` pairs — and a rare sin where the bad chart contains no false
|
|
||||||
statement at all.
|
|
||||||
|
|
||||||
**Citations:** `clevelandmcgill`, `howchartslie`, `wilke`.
|
|
||||||
|
|
||||||
## Not yet buildable
|
## Not yet buildable
|
||||||
|
|
||||||
Real sins, but our pipeline can't prove them honestly today. Recorded so we stop
|
Real sins, but our pipeline can't prove them honestly today. Recorded so we stop
|
||||||
@@ -149,12 +178,12 @@ rediscovering them:
|
|||||||
- **Counts on a choropleth instead of rates** (Cairo's set-piece) — needs
|
- **Counts on a choropleth instead of rates** (Cairo's set-piece) — needs
|
||||||
TopoJSON geometry and a build-time data dependency we don't have yet. Strong
|
TopoJSON geometry and a build-time data dependency we don't have yet. Strong
|
||||||
candidate the moment we take that on.
|
candidate the moment we take that on.
|
||||||
- **Rainbow color scales for continuous data** (Wilke, Munzner, and now
|
- **Rainbow color scales for continuous data** (Wilke, Munzner, and Datawrapper's
|
||||||
Datawrapper's four-part color-scale series — the best free treatment of this
|
four-part color-scale series — the best free treatment of this anywhere) — the
|
||||||
anywhere) — the canon backing is no longer the constraint. What still blocks
|
canon backing is no longer the constraint. What still blocks it is ours: the
|
||||||
it is ours: the fixed chart needs a sequential ramp validated on
|
fixed chart needs a sequential ramp validated on `--chart-canvas` in both
|
||||||
`--chart-canvas` in both themes. Do the palette work first (`dataviz` skill +
|
themes. Do the palette work first (`dataviz` skill + its validator), then this
|
||||||
its validator), then this is ready to write.
|
is ready to write.
|
||||||
|
|
||||||
## Conventions reminder
|
## Conventions reminder
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"title": "Median Response Time (ms)",
|
||||||
|
"width": 150,
|
||||||
|
"height": 330,
|
||||||
|
"data": {
|
||||||
|
"values": [
|
||||||
|
{ "month": "Jan", "ms": 182 },
|
||||||
|
{ "month": "Feb", "ms": 190 },
|
||||||
|
{ "month": "Mar", "ms": 196 },
|
||||||
|
{ "month": "Apr", "ms": 205 },
|
||||||
|
{ "month": "May", "ms": 212 },
|
||||||
|
{ "month": "Jun", "ms": 219 },
|
||||||
|
{ "month": "Jul", "ms": 228 },
|
||||||
|
{ "month": "Aug", "ms": 234 },
|
||||||
|
{ "month": "Sep", "ms": 241 },
|
||||||
|
{ "month": "Oct", "ms": 250 },
|
||||||
|
{ "month": "Nov", "ms": 259 },
|
||||||
|
{ "month": "Dec", "ms": 268 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mark": { "type": "line", "color": "#da1e28", "strokeWidth": 2, "point": { "color": "#da1e28" } },
|
||||||
|
"encoding": {
|
||||||
|
"x": {
|
||||||
|
"field": "month",
|
||||||
|
"type": "ordinal",
|
||||||
|
"sort": null,
|
||||||
|
"axis": { "labelAngle": 90, "title": null, "values": ["Jan", "Apr", "Jul", "Oct"] }
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"field": "ms",
|
||||||
|
"type": "quantitative",
|
||||||
|
"scale": { "domain": [170, 280], "zero": false },
|
||||||
|
"title": "ms"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"title": "Median Response Time (ms)",
|
||||||
|
"width": 330,
|
||||||
|
"height": 200,
|
||||||
|
"data": {
|
||||||
|
"values": [
|
||||||
|
{ "month": "Jan", "ms": 182 },
|
||||||
|
{ "month": "Feb", "ms": 190 },
|
||||||
|
{ "month": "Mar", "ms": 196 },
|
||||||
|
{ "month": "Apr", "ms": 205 },
|
||||||
|
{ "month": "May", "ms": 212 },
|
||||||
|
{ "month": "Jun", "ms": 219 },
|
||||||
|
{ "month": "Jul", "ms": 228 },
|
||||||
|
{ "month": "Aug", "ms": 234 },
|
||||||
|
{ "month": "Sep", "ms": 241 },
|
||||||
|
{ "month": "Oct", "ms": 250 },
|
||||||
|
{ "month": "Nov", "ms": 259 },
|
||||||
|
{ "month": "Dec", "ms": 268 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mark": { "type": "line", "color": "#198038", "strokeWidth": 2, "point": { "color": "#198038" } },
|
||||||
|
"encoding": {
|
||||||
|
"x": {
|
||||||
|
"field": "month",
|
||||||
|
"type": "ordinal",
|
||||||
|
"sort": null,
|
||||||
|
"axis": { "labelAngle": 0, "title": null, "values": ["Jan", "Apr", "Jul", "Oct"] }
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"field": "ms",
|
||||||
|
"type": "quantitative",
|
||||||
|
"scale": { "domain": [170, 280], "zero": false },
|
||||||
|
"title": "ms"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,13 +19,13 @@
|
|||||||
},
|
},
|
||||||
"layer": [
|
"layer": [
|
||||||
{
|
{
|
||||||
"mark": { "type": "line", "color": "#fa4d56", "point": true },
|
"mark": { "type": "line", "color": "#fa4d56", "point": { "color": "#fa4d56" } },
|
||||||
"encoding": {
|
"encoding": {
|
||||||
"y": { "field": "icecream", "type": "quantitative", "title": "Ice cream sales", "axis": { "titleColor": "#fa4d56" } }
|
"y": { "field": "icecream", "type": "quantitative", "title": "Ice cream sales", "axis": { "titleColor": "#fa4d56" } }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"mark": { "type": "line", "color": "#1192e8", "point": true },
|
"mark": { "type": "line", "color": "#1192e8", "point": { "color": "#1192e8" } },
|
||||||
"encoding": {
|
"encoding": {
|
||||||
"y": { "field": "sharks", "type": "quantitative", "title": "Shark attacks", "axis": { "titleColor": "#1192e8" } }
|
"y": { "field": "sharks", "type": "quantitative", "title": "Shark attacks", "axis": { "titleColor": "#1192e8" } }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
{
|
{
|
||||||
"width": 360,
|
"width": 360,
|
||||||
"height": 110,
|
"height": 110,
|
||||||
"mark": { "type": "line", "color": "#fa4d56", "point": true },
|
"mark": { "type": "line", "color": "#fa4d56", "point": { "color": "#fa4d56" } },
|
||||||
"encoding": {
|
"encoding": {
|
||||||
"x": { "field": "month", "type": "ordinal", "sort": null, "axis": { "title": null, "labels": false } },
|
"x": { "field": "month", "type": "ordinal", "sort": null, "axis": { "title": null, "labels": false } },
|
||||||
"y": { "field": "icecream", "type": "quantitative", "scale": { "zero": true }, "title": "Ice cream" }
|
"y": { "field": "icecream", "type": "quantitative", "scale": { "zero": true }, "title": "Ice cream" }
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
{
|
{
|
||||||
"width": 360,
|
"width": 360,
|
||||||
"height": 110,
|
"height": 110,
|
||||||
"mark": { "type": "line", "color": "#1192e8", "point": true },
|
"mark": { "type": "line", "color": "#1192e8", "point": { "color": "#1192e8" } },
|
||||||
"encoding": {
|
"encoding": {
|
||||||
"x": { "field": "month", "type": "ordinal", "sort": null, "axis": { "title": null, "labelAngle": 0 } },
|
"x": { "field": "month", "type": "ordinal", "sort": null, "axis": { "title": null, "labelAngle": 0 } },
|
||||||
"y": { "field": "sharks", "type": "quantitative", "scale": { "zero": true }, "title": "Shark attacks" }
|
"y": { "field": "sharks", "type": "quantitative", "scale": { "zero": true }, "title": "Shark attacks" }
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"title": "Reported Safety Incidents per Year",
|
||||||
|
"width": 340,
|
||||||
|
"height": 240,
|
||||||
|
"data": {
|
||||||
|
"values": [
|
||||||
|
{ "year": "2017", "incidents": 41 },
|
||||||
|
{ "year": "2018", "incidents": 45 },
|
||||||
|
{ "year": "2019", "incidents": 52 },
|
||||||
|
{ "year": "2020", "incidents": 58 },
|
||||||
|
{ "year": "2021", "incidents": 64 },
|
||||||
|
{ "year": "2022", "incidents": 71 },
|
||||||
|
{ "year": "2023", "incidents": 76 },
|
||||||
|
{ "year": "2024", "incidents": 83 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mark": {
|
||||||
|
"type": "area",
|
||||||
|
"color": "#da1e28",
|
||||||
|
"opacity": 0.9,
|
||||||
|
"line": { "color": "#da1e28", "strokeWidth": 2 }
|
||||||
|
},
|
||||||
|
"encoding": {
|
||||||
|
"x": {
|
||||||
|
"field": "year",
|
||||||
|
"type": "ordinal",
|
||||||
|
"sort": null,
|
||||||
|
"axis": { "labelAngle": 0, "title": null }
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"field": "incidents",
|
||||||
|
"type": "quantitative",
|
||||||
|
"scale": { "zero": true, "reverse": true },
|
||||||
|
"title": "Incidents"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"title": "Reported Safety Incidents per Year",
|
||||||
|
"width": 340,
|
||||||
|
"height": 240,
|
||||||
|
"data": {
|
||||||
|
"values": [
|
||||||
|
{ "year": "2017", "incidents": 41 },
|
||||||
|
{ "year": "2018", "incidents": 45 },
|
||||||
|
{ "year": "2019", "incidents": 52 },
|
||||||
|
{ "year": "2020", "incidents": 58 },
|
||||||
|
{ "year": "2021", "incidents": 64 },
|
||||||
|
{ "year": "2022", "incidents": 71 },
|
||||||
|
{ "year": "2023", "incidents": 76 },
|
||||||
|
{ "year": "2024", "incidents": 83 }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mark": {
|
||||||
|
"type": "area",
|
||||||
|
"color": "#198038",
|
||||||
|
"opacity": 0.9,
|
||||||
|
"line": { "color": "#198038", "strokeWidth": 2 }
|
||||||
|
},
|
||||||
|
"encoding": {
|
||||||
|
"x": {
|
||||||
|
"field": "year",
|
||||||
|
"type": "ordinal",
|
||||||
|
"sort": null,
|
||||||
|
"axis": { "labelAngle": 0, "title": null }
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"field": "incidents",
|
||||||
|
"type": "quantitative",
|
||||||
|
"scale": { "zero": true },
|
||||||
|
"title": "Incidents"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
---
|
||||||
|
title: "Aspect Ratio That Exaggerates a Trend"
|
||||||
|
summary: "Drawn tall and narrow it's a cliff; drawn wide it's a shrug. Same series, both times."
|
||||||
|
poke: "You didn't change a number — you changed the shape of the box, and the trend changed with it."
|
||||||
|
category: "Misleading Scales"
|
||||||
|
severity: 3
|
||||||
|
tags: ["line chart", "aspect ratio", "exaggeration"]
|
||||||
|
badChart: "aspect-ratio-bad"
|
||||||
|
fixedChart: "aspect-ratio-fixed"
|
||||||
|
date: 2026-08-05
|
||||||
|
citations:
|
||||||
|
- key: shapeparameter
|
||||||
|
note: "The paper that made this measurable: a graph's width-to-height ratio changes how slopes are judged, and gave us “bank the average slope toward 45°.”"
|
||||||
|
- key: clevelandmcgill
|
||||||
|
note: "Slope is one of the elementary perceptual tasks they ranked — and it's the one the shape of the box distorts."
|
||||||
|
- key: howchartslie
|
||||||
|
note: "Cairo on proportions as a storytelling lever, not a neutral container."
|
||||||
|
---
|
||||||
|
|
||||||
|
## The sin
|
||||||
|
|
||||||
|
Two panels, twelve identical numbers, one identical y-range of 170–280 ms. The
|
||||||
|
only difference is the shape of the plotting area: one is tall and narrow, one
|
||||||
|
is a conventional landscape rectangle.
|
||||||
|
|
||||||
|
The narrow panel reads like an emergency. The wide one reads like a system
|
||||||
|
drifting slowly and predictably. Both are the same 47% rise.
|
||||||
|
|
||||||
|
## Why it deceives
|
||||||
|
|
||||||
|
The eye judges a trend by the **angle** of the line, and angle is a product of
|
||||||
|
the data *and* the box you draw it in. Squeeze the width and every segment tilts
|
||||||
|
toward vertical; stretch it and everything flattens toward nothing. Cleveland
|
||||||
|
and colleagues named this the graph's *shape parameter* and showed the effect is
|
||||||
|
real and measurable, not a matter of taste.
|
||||||
|
|
||||||
|
What makes this one slippery is that the sinner usually isn't sinning. Nobody
|
||||||
|
sets out to pick a dishonest aspect ratio — they drop a chart into the space a
|
||||||
|
slide template left them, or a dashboard tile stretches it to fill a column.
|
||||||
|
The distortion arrives by default, which is exactly why it survives review: the
|
||||||
|
numbers are right, the axis is right, and there is nothing to point at.
|
||||||
|
|
||||||
|
## The repentance
|
||||||
|
|
||||||
|
Treat the ratio as an encoding decision, because that's what it is. The classic
|
||||||
|
guidance is to shape the plot so the average slope of the line sits somewhere
|
||||||
|
near 45° — steep enough to read, shallow enough not to shout. Neither extreme
|
||||||
|
above qualifies; the wide panel is the more honest of the two, and it could
|
||||||
|
stand to be a little taller still.
|
||||||
|
|
||||||
|
Then hold the shape **constant across charts you want compared**. A dashboard
|
||||||
|
where each tile stretches to fill its slot has silently given every series a
|
||||||
|
different exaggeration factor, and no reader will know which trends are real.
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
---
|
||||||
|
title: "An Inverted Y-Axis"
|
||||||
|
summary: "The values climb, the line descends, and the reader believes the shape."
|
||||||
|
poke: "Your y-axis runs downward, so every increase draws as a decline. Nobody checks which way the axis points before believing a shape."
|
||||||
|
category: "Misleading Scales"
|
||||||
|
severity: 5
|
||||||
|
tags: ["axis", "area chart", "direction"]
|
||||||
|
badChart: "inverted-axis-bad"
|
||||||
|
fixedChart: "inverted-axis-fixed"
|
||||||
|
date: 2026-08-05
|
||||||
|
citations:
|
||||||
|
- key: callingbullshit
|
||||||
|
note: "Their “Misleading Axes” module collects the axis manipulations, this one included."
|
||||||
|
- key: howchartslie
|
||||||
|
note: "Cairo's case that readers absorb a chart's shape long before they audit its axis."
|
||||||
|
- key: junkcharts
|
||||||
|
note: "Fung has redrawn this exact trick more than once — the fix is always just turning the axis back over."
|
||||||
|
---
|
||||||
|
|
||||||
|
## The sin
|
||||||
|
|
||||||
|
Both panels plot the same eight numbers, rising every year. The left one runs
|
||||||
|
its y-axis downward, so the highest value sits at the bottom of the plot and the
|
||||||
|
filled region hangs from the top edge. The line descends across the page while
|
||||||
|
the quantity it represents goes up.
|
||||||
|
|
||||||
|
Nothing here is falsified. The axis is labelled, the ticks are honest, the
|
||||||
|
numbers are correct. You could defend every element in a meeting.
|
||||||
|
|
||||||
|
## Why it deceives
|
||||||
|
|
||||||
|
Reading a chart's shape is close to automatic; auditing its axis is a
|
||||||
|
deliberate act that most readers never perform. "Up means more" is not a
|
||||||
|
convention people consciously check — it's the assumption that lets them skip
|
||||||
|
reading the axis at all. Invert it and you get the reader's instant, confident,
|
||||||
|
exactly wrong conclusion for free, while keeping a labelled axis to point at
|
||||||
|
if anyone objects.
|
||||||
|
|
||||||
|
That deniability is what makes this worse than a truncated baseline. A chopped
|
||||||
|
axis exaggerates a real trend. A flipped axis reverses it.
|
||||||
|
|
||||||
|
## The repentance
|
||||||
|
|
||||||
|
Point the y-axis up. There is no legitimate case for inverting a quantitative
|
||||||
|
axis to make "more" appear lower down.
|
||||||
|
|
||||||
|
The one genuine exception is a scale whose *meaning* is already inverted —
|
||||||
|
rankings, where 1st place is the best, or golf scores. Even then, label it
|
||||||
|
loudly (`1st` at the top, "lower is better" in the title), because you are
|
||||||
|
spending the reader's assumption and you owe them the warning.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "Too Many Slices in a Pie Chart"
|
title: "Too Many Slices in a Pie Chart"
|
||||||
summary: "Eight near-identical slices no human eye can rank. A bar chart was right there."
|
summary: "Near-identical wedges nobody can rank by eye, when a sorted bar chart would have answered instantly."
|
||||||
poke: "More slices than anyone can rank by eye. A bar chart was right there."
|
poke: "More slices than anyone can rank by eye. A bar chart was right there."
|
||||||
category: "Chart-Type Abuse"
|
category: "Chart-Type Abuse"
|
||||||
severity: 3
|
severity: 3
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "Truncated Y-Axis on a Bar Chart"
|
title: "Truncated Y-Axis on a Bar Chart"
|
||||||
summary: "Chopping the baseline off a bar chart to make small differences look enormous."
|
summary: "A few points of difference, redrawn until they tower over the page."
|
||||||
poke: "Your bars don't start at zero. That's not a bar chart — it's a magic trick."
|
poke: "Your bars don't start at zero. That's not a bar chart — it's a magic trick."
|
||||||
category: "Misleading Scales"
|
category: "Misleading Scales"
|
||||||
severity: 4
|
severity: 4
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: "Two Y-Axes Scaled to Look Correlated"
|
title: "Two Y-Axes on One Chart"
|
||||||
summary: "Two unrelated series on two independent axes, engineered to look correlated."
|
summary: "Two unrelated series, each handed its own scale, until the lines trace the same suggestive path."
|
||||||
poke: "Two y-axes, scaled until the lines overlap — and suddenly anything 'causes' anything."
|
poke: "Two scales, tuned until the lines overlap — and suddenly anything 'causes' anything."
|
||||||
category: "False Relationships"
|
category: "False Relationships"
|
||||||
severity: 5
|
severity: 5
|
||||||
tags: ["dual axis", "correlation", "line chart"]
|
tags: ["dual axis", "correlation", "line chart"]
|
||||||
@@ -115,6 +115,14 @@ export const REFERENCES = {
|
|||||||
kind: 'paper',
|
kind: 'paper',
|
||||||
blurb: 'The experiments proving people judge position accurately and angle/area poorly — the root of most of these sins.',
|
blurb: 'The experiments proving people judge position accurately and angle/area poorly — the root of most of these sins.',
|
||||||
},
|
},
|
||||||
|
shapeparameter: {
|
||||||
|
authors: 'William S. Cleveland, Marylyn E. McGill & Robert McGill',
|
||||||
|
title: 'The Shape Parameter of a Two-Variable Graph',
|
||||||
|
work: 'Journal of the American Statistical Association',
|
||||||
|
year: 1988,
|
||||||
|
kind: 'paper',
|
||||||
|
blurb: 'Made aspect ratio measurable: the width-to-height ratio changes how slopes are judged, and “bank to 45°” came from it.',
|
||||||
|
},
|
||||||
schwabish: {
|
schwabish: {
|
||||||
authors: 'Jonathan Schwabish',
|
authors: 'Jonathan Schwabish',
|
||||||
title: 'Better Data Visualizations',
|
title: 'Better Data Visualizations',
|
||||||
|
|||||||
Reference in New Issue
Block a user