2

I am using python 3.5.2 I would like to make a pie chart with an png image imbedded. I have pictures of certain bulk products that I would like to insert into the slices. For example strawberries in one slice and raspberries in another. Much like the picture http://www.python-course.eu/images/pie_chart_with_raspberries.png shows.

I can produce images and even plot images instead of points as demonstrated here Matplotlib: How to plot images instead of points?

However, I could not find any approach towards what I am proposing. I suppose it could be manually done in paint, but I was trying to avoid that.

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
CJD
  • 171
  • 1
  • 1
  • 13

1 Answers1

8

enter image description here

That is sure possible. We can start with a normal pie chart. Then we would need to get the images into the plot. This is done using plt.imread and by using a matplotlib.offsetbox.OffsetImage. We would need to find good coordinates and zoom levels to place the image, such that it overlapps completely with respective pie wedge. Then the Path of the pie's wedge is used as a clip path of the image, such that only the part inside the wedge is left over. Setting the zorder of the unfilled wedge to a high number ensures the borders to be placed on top of the image. This way it looks like the wedges are filled with the image.

import matplotlib.pyplot as plt
from matplotlib.patches import PathPatch
from matplotlib.offsetbox import OffsetImage, AnnotationBbox

total = [5,7,4]
labels = ["Raspberries", "Blueberries", "Blackberries"]
plt.title('Berries')
plt.gca().axis("equal")
wedges, texts = plt.pie(total, startangle=90, labels=labels,
                        wedgeprops = { 'linewidth': 2, "edgecolor" :"k","fill":False,  })


def img_to_pie( fn, wedge, xy, zoom=1, ax = None):
    if ax==None: ax=plt.gca()
    im = plt.imread(fn, format='png')
    path = wedge.get_path()
    patch = PathPatch(path, facecolor='none')
    ax.add_patch(patch)
    imagebox = OffsetImage(im, zoom=zoom, clip_path=patch, zorder=-10)
    ab = AnnotationBbox(imagebox, xy, xycoords='data', pad=0, frameon=False)
    ax.add_artist(ab)

positions = [(-1,0.3),(0,-0.5),(0.5,0.5)]
zooms = [0.4,0.4,0.4]

for i in range(3):
    fn = "data/{}.png".format(labels[i].lower())
    img_to_pie(fn, wedges[i], xy=positions[i], zoom=zooms[i] )
    wedges[i].set_zorder(10)

plt.show()
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • 1
    Thanks. It works. I need to spend some time to understand it, but I got it working. Thanks again, I spent a long time on this, then I decided to ask. I'll try and get this to work completely now. I don't have enough reputation to up vote, but it is worth an up vote. – CJD Jun 13 '17 at 19:32