Search code examples
pythonmatplotlibplotpnggeometry

Matplotlib Make Center Circle Transparent


I am plotting a pie chart making background in the png image looks transparent. How can I make the center circle also looks transparent instead of the white color? enter image description here

import matplotlib.pyplot as plt

# Pie chart, where the slices will be ordered and plotted counter-clockwise:
labels = 'Correct', 'Wrong'
sizes = [20, 80]

fig1, ax1 = plt.subplots()
ax1.pie(sizes,colors=['green','red'], labels=labels,autopct='%1.1f%%', 
shadow=True, startangle=90)
centre_circle = plt.Circle((0,0),0.75,edgecolor='black', 
facecolor='white',fill=True,linewidth=0.25)
fig1 = plt.gcf()
fig1.gca().add_artist(centre_circle)
ax1.axis('equal')  # Equal aspect ratio ensures that pie is drawn as a circle.
fig1.savefig('foo.png', transparent=True)

Solution

  • The way you create the white middle part in the above code is by obfuscating the center of the pie by a circle. This can of course not procude a transparent interior.

    A solution to this would also be found in the more sophisticated question Double donut chart in matplotlib. Let me go into detail:

    In order to produce a true donut chart with a hole in the middle, one would need to cut the wedges such that they become partial rings. Fortunately, matplotlib provides the tools to do so. A pie chart consists of several wedges. From the matplotlib.patches.Wedge documentation we learn

    class matplotlib.patches.Wedge(center, r, theta1, theta2, width=None, **kwargs)
    Wedge shaped patch. [...] If width is given, then a partial wedge is drawn from inner radius r - width to outer radius r.

    In order to give set the width to all wedges, an easy method is to use plt.setp

    wedges, _ = ax.pie([20,80], ...)
    plt.setp( wedges, width=0.25)
    

    Complete example:

    import matplotlib.pyplot as plt
    
    fig, ax = plt.subplots()
    fig.set_facecolor("#fff9c9") # set yellow background color to see effect
    
    wedges, text, autotext = ax.pie([25, 40], colors=['limegreen','crimson'],
                                    labels=['Correct', 'Wrong'], autopct='%1.1f%%')
    plt.setp( wedges, width=0.25)
    
    ax.set_aspect("equal")
    # the produced png will have a transparent background
    plt.savefig(__file__+".png", transparent=True)
    plt.show()
    

    enter image description here


    The following would be a way to tackle the problem if the Wedge did not have a width argument. Since the pie chart is centered at (0,0), copying the outer path coordinates, reverting them and multiplying by some number smaller 1 (called r for radius in below code), gives the coordinates of the inner ring. Joining those two list of coordinates and taking care of the proper path codes allows to create a ring shape as desired.

    import matplotlib.pyplot as plt
    import matplotlib.path as mpath
    import matplotlib.patches as mpatches
    import numpy as np
    
    def cutwedge(wedge, r=0.8):
        path = wedge.get_path()
        verts = path.vertices[:-3]
        codes = path.codes[:-3]
        new_verts = np.vstack((verts , verts[::-1]*r, verts[0,:]))
        new_codes =  np.concatenate((codes , codes[::-1], np.array([79])) )
        new_codes[len(codes)] = 2
        new_path = mpath.Path(new_verts, new_codes)
        new_patch = mpatches.PathPatch(new_path)
        new_patch.update_from(wedge)
        wedge.set_visible(False)
        wedge.axes.add_patch(new_patch)
        return new_patch
    
    fig, ax = plt.subplots()
    fig.set_facecolor("#fff9c9") # set yellow background color to see effect
    
    
    wedges, text, autotext = ax.pie([25, 75], colors=['limegreen','indigo'], 
                                    labels=['Correct', 'Wrong'], autopct='%1.1f%%')
    
    for w in wedges:
        cutwedge(w)
        # or try cutwedge(w, r=0.4)
    
    ax.set_aspect("equal")
    
    # the produced png will have a transparent background
    plt.savefig(__file__+".png", transparent=True)
    plt.show()
    

    enter image description here