Add image annotations to bar plots
Asked Answered
A

1

11

So, for example, let's say I have some data

countries = ["Norway", "Spain", "Germany", "Canada", "China"]
valuesA = [20, 15, 30, 5, 26]
valuesB = [1, 5, 3, 6, 2] 

and I do wanna plot them like

this.

How do I put those flag pictures in the graph (if it is even possbile)? And secondly, how can I automate this?

Attain answered 29/5, 2017 at 16:8 Comment(0)
S
25

This solution will work for axes level plots produced with matplotlib, seaborn, and pandas.DataFrame.plot.

The main idea would be to separate the problem into small pieces:

  1. Get the flag as an array into the script. E.g.

     def get_flag(name):
         path = "path/to/flag/{}.png".format(name)
         im = plt.imread(path)
         return im
    
  2. Position an image at a certain position in a plot. This can be accomplished using an OffsetImage. An example can be found on the matplotlib page. Best use a function which takes the name of the country and the position as arguments and generates an AnnotationBbox with the OffsetImage inside.

  3. Drawing the barplot using ax.bar. To set the country names as ticklabels, use ax.set_ticklabels(countries). Then for every country place the OffsetImage from above using a loop.

(coord, 0) and xybox=(0., -16.) can be adjusted to place the image annotations at any location.

The final result may then look something like this:

enter image description here

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.offsetbox import OffsetImage,AnnotationBbox

def get_flag(name):
    path = "data/flags/Flags/flags/flags/24/{}.png".format(name.title())
    im = plt.imread(path)
    return im

def offset_image(coord, name, ax):
    img = get_flag(name)
    im = OffsetImage(img, zoom=0.72)
    im.image.axes = ax

    ab = AnnotationBbox(im, (coord, 0),  xybox=(0., -16.), frameon=False,
                        xycoords='data',  boxcoords="offset points", pad=0)

    ax.add_artist(ab)
    

countries = ["Norway", "Spain", "Germany", "Canada", "China"]
valuesA = [20, 15, 30, 5, 26]
 

fig, ax = plt.subplots()

ax.bar(range(len(countries)), valuesA, width=0.5,align="center")
ax.set_xticks(range(len(countries)))
ax.set_xticklabels(countries)
ax.tick_params(axis='x', which='major', pad=26)

for i, c in enumerate(countries):
    offset_image(i, c, ax)

plt.show()
Smaragd answered 30/5, 2017 at 13:45 Comment(4)
Source of flags: github.com/google/region-flags or github.com/hjnilsson/country-flagsOtha
How would you change this example to work if you wanted to ONLY use the flags, and not the country names? Would you still have to use this approach or could you use the images as the x tick labels directly somehow?Stevestevedore
Could you specify all the arguments for the function AnnotationBbox more in detail ? Like the xy values are used to align the image above the label.Lsd
You changed the Y values so that Germany now scores highest (in whatever unit it is measured ;-)Lsd

© 2022 - 2024 — McMap. All rights reserved.