Explain Codes LogoExplain Codes Logo

Secondary axis with twinx(): how to add to legend

python
matplotlib
data-visualization
legend
Alex KataevbyAlex Kataev·Aug 17, 2024
TLDR

Here goes the turbo version: To combine primary and secondary axes legends in Matplotlib with twinx(), simply collect the legends from each and merge them. Here's the code to do that:

import matplotlib.pyplot as plt # You know, just some super random data x, y1, y2 = range(10), [i**0.8 for i in x], [i**2 for i in x] fig, ax_primary = plt.subplots() # The primary axis wearing a green dress today ax_primary.plot(x, y1, 'g-', label='Y1') ax_secondary = ax_primary.twinx() # The secondary axis decided to go blue ax_secondary.plot(x, y2, 'b-', label='Y2') # Collect legends from both axes, like one big happy family handles, labels = sum(zip(*[ax.get_legend_handles_labels() for ax in (ax_primary, ax_secondary)]), ()) # Display combined legend ax_primary.legend(handles, labels) plt.show() # Ta-da!

This ensures both axes are represented in your legend, giving a proper narrative to your plot.

Level-up your legend game

Well, we've got the basics covered. Ready to turn this from a basic grocery list...into a Michelin-star menu?

Controlling legend location

Move around the legend like you do in your own house. The location is totally up to you!

ax_primary.legend(handles, labels, loc='upper left')

Just pick a location key or use an (x, y) tuple, and your legend will find its way there.

Giving legends a makeover

If there was a Project Runway for legends, you'd want to work on those colors and fonts:

ax_primary.legend(handles, labels, frameon=False, title='Legend', title_fontsize='large', fontsize='small', shadow=True)

Spruce up your legends with frameon, title, and play around with fontsize and title_fontsize.

Inviting error bars to the legend party

Plot error bars like a boss and include them in your legend — because everybody loves a humblebrag:

# Because no data is perfect, just like my mother-in-law's apple pie ax_primary.errorbar(x, y1, yerr=y1_error, fmt='g-', label='Y1 with error')

This subtly tells whoever's looking at your plot that your data is honest and modest.

Whoa, wait. There's auto-labeling?

Reduce congestion in your code by assigning labels as you plot. It's like traffic control for your code:

# Plotting and labeling and...coffee? for i, (y, color, label) in enumerate(zip(ys, colors, labels)): ax.plot(x, y, color=color, label=label)

Matplotlib will do the heavy lifting and automatically generate the legend entries.

Say it with markers

Markers are like spices. They add flavor to your data. And who doesn't like flavor?

# Shapes speak louder than words ax_primary.plot(x, y1, 'g^', label='Y1') # Green triangles ax_secondary.plot(x, y2, 'bs', label='Y2') # Blue squares

These provide an extra layer of distinction on top of colors.

Keeping clarity on the twin axes

The power of 'twinx()' comes with a price — possible confusion. Avoid it by making a clear distinction between the primary and secondary axes:

# And today's color is...blue ax_secondary.spines['right'].set_color('blue') ax_secondary.yaxis.label.set_color('blue') ax_secondary.tick_params(axis='y', colors='blue')