Home > Software design >  multiple piecharts, one legend
multiple piecharts, one legend

Time:12-06

I've created a piechart for 4 different dataframes (the four investment portfolio's in my household).

fig=plt.figure(figsize=(20,20), dpi= 100, facecolor='w', edgecolor='k')
fig, axs = plt.subplots(2, 2)
axs[0, 0].pie(m.newvalue, labels = m.name, labeldistance=None)
axs[0, 0].set_title('Axis [0, 0]')
axs[0, 1].pie(j.newvalue, labels = j.name, labeldistance=None)
axs[0, 1].set_title('Axis [0, 1]')
axs[1, 0].pie(h.newvalue, labels = h.name, labeldistance=None)
axs[1, 0].set_title('Axis [1, 0]')
axs[1, 1].pie(x.newvalue, labels = x.name, labeldistance=None)
axs[1, 1].set_title('Axis [1, 1]')

for ax in axs.flat:
    ax.set()

axs[0,0].legend(bbox_to_anchor=(0, 1.5))

The problem is that the legend only shows the composition of the first dataframe. But it should represent the components for the other 3 piecharts as well. Most of the components are the same, but every df has a unique asset as well. The green in [1,0] is something else than the green in [0,0].

How do I do this?

enter image description here

CodePudding user response:

Here is an approach:

  • make a set containing all the names
  • make a dictionary to map each unique name to a color
  • use the dictionary to map the names in each pie to the corresponding color
  • use the dictionary to create the appropriate legend
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

m = pd.DataFrame({'newvalue': np.random.randint(10, 20, 4), 'name': [*'ABDE']})
j = pd.DataFrame({'newvalue': np.random.randint(10, 20, 4), 'name': [*'ACEF']})
h = pd.DataFrame({'newvalue': np.random.randint(10, 20, 4), 'name': [*'BDEG']})
x = pd.DataFrame({'newvalue': np.random.randint(10, 20, 4), 'name': [*'ABCE']})

# let colors be a list of unique colors, at least one for each name
colors = plt.get_cmap('tab10').colors
# make a set of all the names
all_names = {*m.name, *j.name, *h.name, *x.name}
# map each of the unique names to a color
name_to_color = {name: color for name, color in zip(all_names, colors)}

fig, axs = plt.subplots(2, 2, figsize=(9, 12), dpi=100, facecolor='w', edgecolor='k')
axs[0, 0].pie(m.newvalue, labels=m.name, labeldistance=None, colors=m.name.map(name_to_color))
axs[0, 0].set_title('Axis [0, 0]')
axs[0, 1].pie(j.newvalue, labels=j.name, labeldistance=None, colors=j.name.map(name_to_color))
axs[0, 1].set_title('Axis [0, 1]')
axs[1, 0].pie(h.newvalue, labels=h.name, labeldistance=None, colors=h.name.map(name_to_color))
axs[1, 0].set_title('Axis [1, 0]')
axs[1, 1].pie(x.newvalue, labels=x.name, labeldistance=None, colors=x.name.map(name_to_color))
axs[1, 1].set_title('Axis [1, 1]')

handles = [plt.Rectangle((0, 0), 0, 0, color=name_to_color[name], label=name) for name in name_to_color]
axs[0, 0].legend(handles=handles, bbox_to_anchor=(0.2, 1.1), loc='lower left')

plt.tight_layout()
plt.show()

pie charts with common legend

PS: plt.figure() should not be used together with plt.subplots(), as plt.subplots() also creates a new figure, leading to a dummy empty plot. The parameters for plt.figure() can directly go to plt.subplots().

When creating a legend with bbox_to_anchor it is important to also set the loc. The loc is the point on the legend where the anchor is placed. Default, loc='best', which depends on the subplot contents, and can be different e.g. when the subplot is a bit smaller or larger.

  • Related