Search code examples
python-3.xmatplotlibpie-chart

matplotlib image as labels on pie chart


I don't find the way to move the labels' text to images. How to use PIL or imageio to retrieve flag images from internet and use them instead of the strings. Here is the MWE:

import matplotlib.pyplot as plt

nationalities = {
    'Fr': 5,
    'De': 10,
    'It': 3,
    'Se': 5,
    'Pl':2,
    'Cz': 3,
}

flags = {
    'Fr':'https://www.touteleurope.eu/wp-content/uploads/2022/08/drapeau-France.png',
    'De':'https://www.touteleurope.eu/wp-content/uploads/2022/07/drapeau-allemagne.png',
    'It':'https://www.touteleurope.eu/wp-content/uploads/2022/08/drapeau-Italie.png',
    'Se':'https://www.touteleurope.eu/wp-content/uploads/2022/07/1280px-Flag_of_Sweden.svg.png',
    'Pl':'https://www.touteleurope.eu/wp-content/uploads/2022/08/drapeau-Pologne.png',
    'Cz':'https://www.touteleurope.eu/wp-content/uploads/2022/08/drapeau-Republique-tcheque.png',
}

fig, ax = plt.subplots(figsize=(18,6))
plt.xticks(fontsize=20)
plt.yticks([], fontsize=20)
Countries = []
Composers = []
Flags = []
for Cnty, Cps in nationalities.items():
    Countries.append(Cnty)
    Composers.append(Cps)
    Flags.append( flags[Cnty] ) 
    nb_of_composers = 100*max(Composers)/sum(Composers)
print (str(nb_of_composers)[0:2])
#plt.pie( Composers, labels=Flags, radius=1, textprops={'size': 20}, wedgeprops={'linewidth': 1.0, 'edgecolor': 'white'}, )
plt.pie( Composers, labels=Countries, radius=1, textprops={'size': 20}, wedgeprops={'linewidth': 1.0, 'edgecolor': 'white'}, )
plt.text(s=str(nb_of_composers)[0:2]+'%', x=-0.7, y=0.3, fontsize=30, color='white')
plt.tight_layout()
plt.show()

Thanks a lot for your help !


Solution

  • You can download the images with request+PIL, then add the images with imshow:

    from PIL import Image
    import requests
    
    # download the images
    headers ={
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36'
    }
    
    Flags = {k: Image.open(requests.get(url, headers=headers, stream=True).raw)
             for k, url in flags.items()}
    
    # plot the original graph
    fig, ax = plt.subplots(figsize=(9,3))
    plt.xticks(fontsize=20)
    plt.yticks([], fontsize=20)
    
    Countries, Composers = zip(*nationalities.items())
    nb_of_composers = max(Composers)/sum(Composers)
    
    patches, texts = plt.pie(Composers, labels=Countries, radius=1, textprops={'size': 20},
                             wedgeprops={'linewidth': 1.0, 'edgecolor': 'white'}, )
    plt.text(s=f'{nb_of_composers:.0%}', x=-0.7, y=0.3, fontsize=30, color='white')
    
    # replace the string labels with images
    for t in texts:
        x, y = t.get_position()
        ax.imshow(Flags[t._text], extent=[x-0.15, x+0.15, y-0.1, y+0.1], zorder=1)
        t.set_visible(False)
    
    plt.tight_layout()
    

    Output:

    matplotlib pie chart image label flags