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?

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Mnovdef
  • 157
  • 2
  • 5

1 Answers1

24

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()
Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • 1
    Source of flags: https://github.com/google/region-flags or https://github.com/hjnilsson/country-flags – juanbretti Aug 05 '20 at 18:42
  • 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? – Raleigh L. Jan 26 '23 at 05:42
  • 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. – pas-calc Mar 15 '23 at 08:44
  • 1
    You changed the Y values so that Germany now scores highest (in whatever unit it is measured ;-) – pas-calc Mar 15 '23 at 08:47