Search code examples
pythonmatplotlibpie-chart

ConnectionPatch() or pyplot.arrow between subplots of pyplot pie charts


I want to connect these piecharts with arrows. I learned about the ConnectionPatch() function but it requires coordinates of axes and pie charts don't have axes, do they?

import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec


labels = 'Good', 'Poor', 'Mediocre'
sizes = [124, 205, 133]

gs = gridspec.GridSpec(2,5)

def make_autopct(sizes):
    def my_autopct(pct):
        total = sum(sizes)
        val = int(round(pct*total/100.0))
        return '{p:.1f}%({v:d})'.format(p=pct,v=val)
    return my_autopct

plt.figure(figsize=(15,8))
plt.subplot(gs[:,2])
plt.axis('equal')
piechart = plt.pie(sizes, radius=3, labels=labels, labeldistance=1.1, textprops={'backgroundcolor':'w', 'fontsize':12}, autopct=make_autopct(sizes), 
pctdistance=0.5, shadow=False, colors='w')[0]
piechart[0].set_hatch('\\\\\\\\\\')
piechart[1].set_hatch('xxxx')
piechart[2].set_hatch('+++')
mpl.rcParams['font.size'] = 20


poor = [201, 25]
mediocre = [97, 32]
good = [83, 30]
label_small = "Private","Government"

plt.subplot(gs[0,0])
plt.axis('equal')
pie_poor = plt.pie(poor, labels=label_small, autopct='%1.1f%%', 
pctdistance=1.5, textprops={'fontsize':8},  shadow=False, colors='w', startangle=0)[0]
pie_poor[0].set_hatch('//////')
pie_poor[1].set_hatch('....')

plt.subplot(gs[0,4])
plt.axis('equal')
pie_good = plt.pie(good, labels=label_small, autopct='%1.1f%%', pctdistance=1.4, textprops={'fontsize':8},  shadow=False, colors='w', startangle=270)[0]
pie_good[0].set_hatch('//////')
pie_good[1].set_hatch('....')

plt.subplot(gs[1,4])
plt.axis('equal')
pie_mediocre = plt.pie(mediocre, labels=label_small, autopct='%1.1f%%', pctdistance=1.3, textprops={'fontsize':8},  shadow=False, colors='w', startangle=180)[0]
pie_mediocre[0].set_hatch('//////')
pie_mediocre[1].set_hatch('....')

plt.draw()

[Output]

I want to connect the big wedge labeled "Poor" to the smaller pie on the left side, "Good" to the smaller pie on the upper right, and "Mediocre" to the smaller pie on the lower right with arrows.


Solution

  • Every plot in matplotlib lives in an axes. To get a handle to the axes, use ax = plt.subplot(..). You can then use a ConnectionPatch to connect the axes.

    enter image description here

    Complete code:

    import matplotlib as mpl
    import matplotlib.pyplot as plt
    import matplotlib.gridspec as gridspec
    import numpy as np
    
    
    labels = 'Good', 'Poor', 'Mediocre'
    sizes = [124, 205, 133]
    
    gs = gridspec.GridSpec(2,5)
    
    def make_autopct(sizes):
        def my_autopct(pct):
            total = sum(sizes)
            val = int(round(pct*total/100.0))
            return '{p:.1f}%({v:d})'.format(p=pct,v=val)
        return my_autopct
    
    plt.figure(figsize=(15,8))
    ax = plt.subplot(gs[:,2])
    plt.axis('equal')
    piechart = plt.pie(sizes, radius=3, labels=labels, labeldistance=1.1, textprops={'backgroundcolor':'w', 'fontsize':12}, autopct=make_autopct(sizes), 
    pctdistance=0.5, shadow=False, colors='w')[0]
    piechart[0].set_hatch('\\\\\\\\\\')
    piechart[1].set_hatch('xxxx')
    piechart[2].set_hatch('+++')
    mpl.rcParams['font.size'] = 20
    
    
    poor = [201, 25]
    mediocre = [97, 32]
    good = [83, 30]
    label_small = "Private","Government"
    
    ax1 = plt.subplot(gs[0,0])
    plt.axis('equal')
    pie_poor = plt.pie(poor, labels=label_small, autopct='%1.1f%%', 
    pctdistance=1.5, textprops={'fontsize':8},  shadow=False, colors='w', startangle=0)[0]
    pie_poor[0].set_hatch('//////')
    pie_poor[1].set_hatch('....')
    
    ax2 = plt.subplot(gs[0,4])
    plt.axis('equal')
    pie_good = plt.pie(good, labels=label_small, autopct='%1.1f%%', pctdistance=1.4, textprops={'fontsize':8},  shadow=False, colors='w', startangle=270)[0]
    pie_good[0].set_hatch('//////')
    pie_good[1].set_hatch('....')
    
    ax3 = plt.subplot(gs[1,4])
    plt.axis('equal')
    pie_mediocre = plt.pie(mediocre, labels=label_small, autopct='%1.1f%%', pctdistance=1.3, textprops={'fontsize':8},  shadow=False, colors='w', startangle=180)[0]
    pie_mediocre[0].set_hatch('//////')
    pie_mediocre[1].set_hatch('....')
    
    from matplotlib.patches import ConnectionPatch
    xy = (1, 1)
    con = ConnectionPatch(xyA=(0,0), xyB=xy, coordsA="data", coordsB="data", 
                          axesA=ax2, axesB=ax, color="crimson", lw=3)
    ax2.add_artist(con)
    xy = (-1, 0)
    con = ConnectionPatch(xyA=(0,0), xyB=xy, coordsA="data", coordsB="data", 
                          axesA=ax1, axesB=ax, color="darkblue", lw=3)
    ax1.add_artist(con)
    xy = (1, -1)
    con = ConnectionPatch(xyA=(0,0), xyB=xy, coordsA="data", coordsB="data", 
                          axesA=ax3, axesB=ax, color="gold", lw=3)
    ax3.add_artist(con)
    
    plt.show()