The grand tours¶
When the ticks are the slowest, the median and the fastest winner, the axis tells the story and the caption can go.
A century of winners' average speeds at the Tour de France, the Giro d'Italia and the Vuelta a España, one race per panel. The data are the editions' published averages, compiled in examples/data/grand_tour_speeds.csv with its sources. This lesson builds the figure one call at a time, as the resonance peak did; the script is examples/tutorial_grand_tours.py.
The data and the default figure¶
Each race gets a column in the file and a row of NaN where no edition was held, so the gaps stay gaps rather than a line drawn across them.
table = np.genfromtxt(
DATA / "grand_tour_speeds.csv", delimiter=",", names=True, skip_header=5
)
first, last = int(table["year"][0]), int(table["year"][-1])
years = np.arange(first, last + 1)
dates = [dt.date(year, 7, 1) for year in years]
speeds_of = {}
for race in JERSEYS:
speeds = np.full(years.size, np.nan)
speeds[table["year"].astype(int) - first] = table[race]
speeds_of[race] = speeds
fig, axes = plt.subplots(3, 1, figsize=(5, 4.5))
fig.subplots_adjust(hspace=0.45)
for ax, (race, (label, color)) in zip(axes, JERSEYS.items(), strict=True):
ax.plot(dates, speeds_of[race], color=color, linewidth=1.2, label=label)
Step one. Three boxes, three legends, three y ranges and the same century of years spelled out three times. Nothing here reads across the panels.
Treat the three panels as one figure¶
vzs.small_multiples(
axes,
compare="column",
frame="data",
spacing=(5, 4),
ylabel="winner's average\nspeed (km/h)",
)
Step two. One shared scale, one year axis under the bottom panel, one axis label for the column, and each box reduced to two spines.
small_multiples treats the three axes together rather than one at a time. compare="column" scopes the time axis to the column, and since there is one column the three panels come out on one shared scale: every left spine now runs from the slowest winner in any of the three races to the fastest in any of them. The inner furniture goes with it. Only the bottom panel keeps the year axis and only the left column keeps the axis label, because that is where a reader looks for them.
Tick each race at its own extremes¶
for ax, speeds in zip(axes, speeds_of.values(), strict=True):
ax.yaxis.set_major_locator(
vzs.SummaryLocator(speeds, [np.nanmin, np.nanmedian, np.nanmax])
)
ax.yaxis.set_major_formatter("{x:.1f}")
Step three. The round numbers give way to three numbers per race: its slowest winner, its median and its fastest.
SummaryLocator reduces one axis's own values, here with nanmin, nanmedian and nanmax, so each panel is ticked by its own race while the scale behind it stays common to all three.Because the scale is shared, the comparison is on the page as well as in the numbers: the Tour's top tick sits higher than the Giro's, and the gap between them is to scale. Read the ticks straight down and the question a reader brings is answered: which race is fastest, and by how much.
Replace the legends with labels¶
for ax in axes:
vzs.line_labels(ax)
Step four. Each race is named at the end of its line, in its jersey's colour, and the three legends go.
The colour is the jersey's: yellow, pink and red are the data here, not decoration, which is why these three lines carry colour when most vanzelfsprekend figures stay in ink.
What the frame says without a caption¶
The y ticks answer the question a reader brings. Which race is fastest? The top ticks say so. Is the Giro's median higher than the Vuelta's? Two numbers, one glance. No round number sits between them to be read past.
The gaps are data too. Both world wars are holes in every record, because no race was run and the line is not interpolated across the years. The Vuelta starts late, in 1935, and its first decade is broken, which the frame reports by starting the line where the data starts rather than at a round 1900.
Going further¶
The scope is compare's to set. "row" would give each row its own y scale, so each race would be framed and ticked by its own record alone; every line would then fill its panel and nothing could be read across the three. The small multiples how-to has the three scopes.
That is the tutorial. The how-to pages answer one question each, and the gallery has the full progression of figures.