0

I am trying to put multiple matplotlib subplots into a big axis, where tick labels on the big axis correspond to some parameter values for which the data in each subplot has been obtained. Here's an example,

import matplotlib.pyplot as plt
data = {}
data[(10, 10)] = [0.45, 0.30, 0.25]
data[(10, 20)] = [0.2, 0.5, 0.3]
data[(20, 10)] = [0.1, 0.3, 0.6]
data[(20, 20)] = [0.6, 0.15, 0.25]
data[(30, 10)] = [0.4, 0.35, 0.25]
data[(30, 20)] = [0.5, 0.1, 0.4]

# x and y coordinates for the big plot
x_coords = list(set([k[0] for k in data.keys()]))
y_coords = list(set([k[1] for k in data.keys()]))

labels = ['Frogs', 'Hogs', 'Dogs']
explode = (0.05, 0.05, 0.05)  #
colors = ['gold', 'beige', 'lightcoral']

fig, axes = plt.subplots(len(y_coords), len(x_coords))

for row_topToDown in range(len(y_coords)):
    row = (len(y_coords)-1) - row_topToDown
    for col in range(len(x_coords)):
        axes[row][col].pie(data[(x_coords[col], y_coords[row_topToDown])], explode=explode, colors = colors, \
        autopct=None, pctdistance = 1.4, \
        shadow=True, startangle=90, radius=0.7, \
        wedgeprops = {'linewidth':1, 'edgecolor':'Black'}
                                     )
        axes[row][col].axis('equal')  # Equal aspect ratio ensures that pie is drawn as a circle.
        axes[row][col].set_title('(' + str(x_coords[col]) + ', ' + str(y_coords[row_topToDown]) + ')')

fig.tight_layout()        
plt.show()

and here's how I'd like the output to look like: enter image description here

user3076813
  • 499
  • 2
  • 6
  • 13
  • I think every information you need is [here](https://stackoverflow.com/questions/17458580/embedding-small-plots-inside-subplots-in-matplotlib) – user8408080 Nov 19 '18 at 21:42
  • In that post the aim is to place a smaller plot inside each subplot, which is not exactly what I want, i.e., to place all subplots inside a bigger axis, wherein each subplot corresponds to a specific xy-coordinate on that big frame. Particularly, I have a hard time setting the ticks and tick labels on the bigger frame to correspond to each subplot. – user3076813 Nov 20 '18 at 00:29

1 Answers1

3

I see two options:

A. use a single axes

You may plot all pie charts to the same axes. Use the center and radius argument to scale the pies in data coordinates. This could look as follows.

import matplotlib.pyplot as plt
data = {}
data[(10, 10)] = [0.45, 0.30, 0.25]
data[(10, 20)] = [0.2, 0.5, 0.3]
data[(20, 10)] = [0.1, 0.3, 0.6]
data[(20, 20)] = [0.6, 0.15, 0.25]
data[(30, 10)] = [0.4, 0.35, 0.25]
data[(30, 20)] = [0.5, 0.1, 0.4]

labels = ['Frogs', 'Hogs', 'Dogs']
explode = [.2]*3
colors = ['gold', 'beige', 'lightcoral']
radius = 4
margin = 2

fig, ax = plt.subplots()

for x,y in data.keys():
    d = data[(x,y)]
    ax.pie(d, explode=explode, colors = colors, center=(x,y), 
            shadow=True, startangle=90, radius=radius, 
            wedgeprops = {'linewidth':1, 'edgecolor':'Black'})

    ax.annotate("({},{})".format(x,y), xy = (x, y+radius), 
                xytext = (0,5), textcoords="offset points", ha="center")

ax.set_frame_on(True)
xaxis = list(set([x for x,y in data.keys()]))
yaxis = list(set([y for x,y in data.keys()]))
ax.set(aspect="equal", 
       xlim=(min(xaxis)-radius-margin,max(xaxis)+radius+margin), 
       ylim=(min(yaxis)-radius-margin,max(yaxis)+radius+margin), 
       xticks=xaxis, yticks=yaxis)
fig.tight_layout()        
plt.show()

enter image description here

B. use inset axes

You can put each pie in its own axes and position the axes in data coordinates. This is facilitated by using mpl_toolkits.axes_grid1.inset_locator.inset_axes. The main difference to the above is that you may use a non-equal aspect of the parent axes, and that it's not possible to use tight_layout.

import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes

data = {}
data[(10, 10)] = [0.45, 0.30, 0.25]
data[(10, 20)] = [0.2, 0.5, 0.3]
data[(20, 10)] = [0.1, 0.3, 0.6]
data[(20, 20)] = [0.6, 0.15, 0.25]
data[(30, 10)] = [0.4, 0.35, 0.25]
data[(30, 20)] = [0.5, 0.1, 0.4]


labels = ['Frogs', 'Hogs', 'Dogs']
explode = [.05]*3
colors = ['gold', 'beige', 'lightcoral']
radius = 4
margin = 2

fig, axes = plt.subplots()

for x,y in data.keys():
    d = data[(x,y)]
    ax = inset_axes(axes, "100%", "100%", 
                    bbox_to_anchor=(x-radius, y-radius, radius*2, radius*2),
                    bbox_transform=axes.transData, loc="center")
    ax.pie(d, explode=explode, colors = colors,
            shadow=True, startangle=90,
            wedgeprops = {'linewidth':1, 'edgecolor':'Black'})

    ax.set_title("({},{})".format(x,y))


xaxis = list(set([x for x,y in data.keys()]))
yaxis = list(set([y for x,y in data.keys()]))
axes.set(aspect="equal", 
       xlim=(min(xaxis)-radius-margin,max(xaxis)+radius+margin), 
       ylim=(min(yaxis)-radius-margin,max(yaxis)+radius+margin), 
       xticks=xaxis, yticks=yaxis)
        
plt.show()

enter image description here


For how to put a legend outside the plot, I would refer you to How to put the legend out of the plot. And for how to create a legend for a pie chart to How to add a legend to matplotlib pie chart?
Also Python - Legend overlaps with the pie chart may be of interest.

Community
  • 1
  • 1
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • Both of your methods work perfectly fine for pie charts since you're working with radius. Is there a more general solution that works for any type of graph where radius is irrelevant (e.g., line plots)? – user3076813 Nov 26 '18 at 21:40
  • Sure, just rename `radius` to something else. – ImportanceOfBeingErnest Nov 26 '18 at 21:43
  • Sorry. I might be talking about something very obvious that is not very clear to me. I meant radius is a keyword argument for function pie, which determines the size of the pie, but we may not have such a single parameter to set the size for other plot types (like a linear). – user3076813 Nov 26 '18 at 21:53
  • Ok, obviously you cannot use the solution A. For solution B. there is no `radius` defined inside of `pie`. Hence you can just ignore it. – ImportanceOfBeingErnest Nov 26 '18 at 21:54
  • Is it possible to get around the problem of not being able to use tight_layout for Method B through using add_axes and set_position instead of using inset_axes? – user3076813 Nov 27 '18 at 18:31
  • No that's not an option, because you want to position the axes in data coordinates and not in figure coordinates. But you can set up the outer axes first, then call tight_layout, then add the inset_axes. – ImportanceOfBeingErnest Nov 27 '18 at 18:46
  • Tried setting up the outer axes first and using tight_layout but everything overlaps if I want to put labels on the pie charts. On a separate note, the ticks do not completely coincide with the center of each pie chart. I thought this is due to using explode but removing it didn't resolve the issue. – user3076813 Nov 30 '18 at 20:05
  • Sure, the labels would then overlap the inner axes, which is anyway nothing tight_layout can do for you. Instead you would adjust the pie in each inner axes, setting limits and radius for the pie, such that the labels have enough space around it. Does adding `loc="center"` to the `inset_axes` call put the pie's centers aligned with the ticks? – ImportanceOfBeingErnest Nov 30 '18 at 20:14
  • Using loc="center" perfectly solved the issue with pie centers! Will try playing with limits and radius to see if I can avoid overlaps. – user3076813 Nov 30 '18 at 21:15