1

I like to create a legend based on certain numbers using Numpy and Matplotlib but to no avail. So I started to play around with a test function to get it right before transferring this to my main script.

%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np

min_xyz = np.random.randint(5, size=(50,1,50))

fig = plt.figure(figsize=(7,7))
ax = fig.add_subplot(111)
ax.set_title('test')
plt.imshow(min_xyz[:,0,:])
ax.set_aspect('equal')

ax.set_xlabel('Distance')
ax.set_ylabel('Depth')
ax.legend()

So this creates something like this

Initial test image

I would like to create the legend that shows the following:

 Mineral 1 = colour_1
 Mineral 2 = colour_2
 Mineral 3 = colour_3
 Mineral 4 = colour_4
 Mineral 5 = colour_5

I tried working with ax.legend() but I can't seem to get it right. Any ideas?

Edit: Solution with vertical colour bars

My solution with the input from j08lue - Vertical colour bars

%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.patches as mpatches

min_xyz = np.random.randint(5, size=(50,1,50))

fig, ax = plt.subplots(figsize = (7,40))
ax.set_title('test')
cax = ax.imshow(min_xyz[:,0,:], cmap = plt.cm.Accent)

"""Handles for min_xyz"""
cbar = plt.colorbar(cax, ticks=[0, 1, 2, 3, 4], 
                    orientation='vertical',
                    fraction=0.045, pad=0.05)
cbar.ax.set_yticklabels(['Mineral 1', 'Mineral 2', 'Mineral 3', 'Mineral 4','Mineral 5'])

ax.set_xlabel('Distance')
ax.set_ylabel('Depth')

Test image with colour bar solution

Edit: Creating customised legend

I have placed the solution suggested by j08lue and managed to get the first legend correct. However, I believe it got to do with the normalising of the colour bar to get the legend reflect the right colour. I know I am missing something but I am not sure what I should be searching for. Any input is greatly appreciated.

%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.patches as mpatches

min_xyz = np.random.randint(5, size=(50,50))

fig2, ax2 = plt.subplots(figsize = (7,40))
ax = fig.add_subplot(111)
ax2.set_title('test')
cax2 = ax2.imshow(min_xyz, cmap = plt.cm.Accent, vmin=0, vmax=4)
ax2.set_aspect('equal')

"""Handles for min_xyz"""
 my_colors = {
    'Mineral 1' : 0.,
    'Mineral 2' : 1., # It is normalised to 0 to 1
    'Mineral 3' : 2.,
    'Mineral 4' : 3.,
    'Mineral 5' : 4.,
}
patches = [mpatches.Patch(color=cmap(v), label=k) for k,v in sorted(my_colors.items(), key=lambda t: t[0])]
plt.legend(handles=patches, loc=2, bbox_to_anchor=(1.01,1))

ax2.set_xlabel('Distance')
ax2.set_ylabel('Depth')

Unfinished legend example

2
  • Edited post with solution with color bars. Am working on a possible solution with proxy artists. Commented Feb 16, 2016 at 4:20
  • Great you are figuring things out. =) However, your values in my_colors are not between 0 and 1. Make them 0, 0.1, 0.2, 0.3, 0.4 instead and it will look much better. Or, even better, use 0, 0.25, 0.5, 0.75, 1 instead, to exploit the whole spectrum of the colormap. This is what I meant by normalizing your data to (0,1). Commented Feb 29, 2016 at 12:20

1 Answer 1

1

Proxy artists

This can be done via proxy artists. Example from the docs:

import matplotlib.patches as mpatches
import matplotlib.pyplot as plt

red_patch = mpatches.Patch(color='red', label='The red data')
plt.legend(handles=[red_patch])

plt.show()

Proxy artist demo

But you need to figure out which colours correspond to which values. E.g.

cmap = plt.cm.viridis
my_colors = {
    'Mineral 1' : 0.1,
    'Mineral 2' : 0.2,
    }

patches = [mpatches.Patch(color=cmap(v), label=k) for k,v in my_colors.items()]

plt.legend(handles=patches)

Minerals patches

The numbers in the dictionary correspond to the data normalized to [0,1] and you need to plot your data with the same cmap, of course.

Alternative: Colorbar

Alternatively, you can add a colorbar (the equivalent to the legend in imshow plots and the like) and place your labels on the ticks.

cbar = plt.colorbar(cax, ticks=list(my_colors.values()), orientation='horizontal')
cbar.ax.set_xticklabels(list(my_colors.keys()))
Sign up to request clarification or add additional context in comments.

3 Comments

NB: You might need to sort the dictionary when making the patches, if you want them to be in alphabetical order: sorted(my_colors.items(), key=lambda t: t[0]).
Thanks for your input j08lue, I only managed to have a solution by using the colourbar. However, I don't wish to normalise the data (since the data is in integers (0, 1, 2, 3, 4).
You do not need to normalize the data. Only the values for cmap(v) must be between 0 and 1.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.