Skip to content
wannacode

Honest charts

In short: A misleading chart shows a true table with a false impression (a cut axis, two axes, 3D); an honest one starts bars at zero and states the conclusion in the title.

Problem

“September collapsed”

A colleague sends a chart for the meeting with the manager: the revenue of the last four months. The September bar is almost gone. It looks like a disaster.

Pythondata: the shop tables

September really is weaker: €7,128.80 against €8,812.70 in August. Why, we will find out in the project at the end of the course. Here the question is a different one: how much weaker does it look on the chart, and how much weaker is it really?

The idea

Bars start at zero

The eye reads a bar by its length: a bar twice as long means twice as much. So the length must be the number itself. The colleague's chart starts at 7,000 (set_ylim(7000, 10000)), and that is called a truncated axis. Here are the same numbers with an axis from 7,000 and from 0:

Pythondata: the shop tables

The real fall from August to September is 19.1%. On the left the September bar is 92.9% shorter than the August one: the fall looks 4.9 times bigger. The truncated axis removes the bottom €7,000 the two months share and leaves only the difference.

matplotlib starts bars at 0 by itself. A truncated axis comes from set_ylim or from tools that “zoom in” on the axis on their own. If the difference between the bars does not show from 0, it is small. That is an answer too.

More precisely, if you are curious

A line is different: it is read by its slope and by when it goes up and down, not by length. The axis of a line may start somewhere other than 0 if it is labelled. A line that changes by a few percent looks almost flat on an axis from 0, and the change does not show.

Try it

Fix the chart

Someone drew the revenue by month with an axis from 7,000. Fix the chart: 12 bars with the revenue of each month (monthly, without the cancelled orders), and the y axis starting at 0 and reaching above December.

Pythondata: the shop tables

The idea

Two y axes

Revenue is in thousands of euros, and orders are in hundreds. To put them on one chart, people often give the second line its own axis on the right: ax.twinx() makes a new y axis that shares the x axis with the first. But the scale of each axis is chosen separately, and each line stretches to fill its own. So two lines can look merged or apart, as the author wishes.

The honest alternative is an index: divide every month by the first one and multiply by 100. by_month / by_month.iloc[0] * 100 does it for both columns at once (broadcasting over the columns). Then both lines are on one axis and start at 100:

Pythondata: the shop tables

On the left the lines almost coincide, and revenue and orders seem to move together. On the right you see the difference: in December revenue is 171.2, and orders 136.7. Revenue grew much more than orders, because the December orders are also larger. You saw it in the box plots in the previous lesson too.

Another honest way is two charts one under the other, each with its own axis and label: plt.subplots(2, 1, sharex=True).

The idea

3D, pie charts and pictures

Three things that look impressive and mislead:

  • 3D bars and 3D pie charts. Perspective makes what is near bigger than what is far. The third dimension carries no data; it only distorts.
  • Pie charts. The eye compares angles and areas much worse than lengths. You saw it in the lesson on choosing a chart: sorted bars say the same more clearly.
  • A value in the size of a picture. If the picture grows both in height and in width, its area grows with the square: twice as much looks four times as much.
1. The revenue of one month is twice the revenue of another. In a presentation it is drawn as a bag of money, twice as tall and twice as wide as the other one. How much bigger does it look?

The idea

Colour for the answer

Colour is not decoration: it tells you where to look. One colour for what matters and grey for the rest is the clearest. Here is the revenue by month twice: on the left all bars are the same, on the right December is coloured:

Pythondata: the shop tables

np.where(monthly.index.month == 12, '#E69F00', 'lightgray') gives one colour per month: orange for December, grey for the others. color=colors puts them on the bars.

The title on the right works too: not “Revenue by month”, but the conclusion: “December brings 14% of annual revenue” (14.3% exactly). The reader gets it before looking at the axes.

The orange #E69F00 is from the Okabe-Ito palette: eight colours that people who cannot see some colours can still tell apart. Many people cannot tell red from green. So never separate two things only by red and green. If you need more colours, matplotlib's tab10 palette (the default colours, C0 to C9) is also a good start.

The idea

Labels instead of a legend

Six lines in six colours and a legend in the corner: the eye jumps between the line and the legend to work out which is which. A label right next to the line saves that. Here is the revenue by category, with a legend on the left and with labels on the right:

Pythondata: the shop tables

ax.text(x, y, 'Kitchen') writes text at the point (x, y) of the data. The months are text, and matplotlib puts them at positions 0, 1, 2…, so len(table) - 0.8 is just right of the last month, and 1.3 is next to December. The kitchen label is in the blue of its line, and the kitchen is drawn last and thicker, so it is on top.

In December the kitchen sells €3,240.60, 4.4 times more than its average month in the rest of the year. Mostly pans and knives sell, probably as gifts. On the left chart this is one of six lines, on the right it is the first thing you see. The gift card has no category, so it is not here.

Try it

Colours

1. On a chart growth is green and decline is red, with no other difference. What is the problem?
2. Why are the other five lines grey in the category chart, and the kitchen blue?

Exercise

The chart for the annual report

The manager wants one chart for the annual report: the revenue by month (monthly), December in colour, the other months in grey, the axis from 0, and a title that says how much of the year's revenue December brings. Store the share in share and the colours in colors.

Pythondata: the shop tables

Test

Check yourself

1. Revenue falls by 19.1% from August to September. On bars with an axis from 7,000 the fall looks like 92.9%. Why?
2. A line shows the average price of coffee by month: €13.90 until February and €14.90 from March. Must the y axis of the line start at 0?
3. What is dangerous about two y axes (twinx) on one chart?
4. Which title is better for the chart of revenue by month with December coloured?

Made by Ivo Gergov as part of Evo Tech's campaign for accessible programming education ·