0

I have a data which looks like (example)

x y  d
0 0 -2
1 0  0
0 1  1 
1 1  3

And I want to turn this into a coloumap plot which looks like one of these:

enter image description here

where x and y are in the table and the color is given by 'd'. However, I want a predetermined color for each number, for example:

-2 - orange
 0 - blue
 1 - red
 3 - yellow

Not necessarily these colours but I need to address a number to a colour and the numbers are not in order or sequence, the are just a set of five or six random numbers which repeat themselves across the entire array.

Any ideas, I haven't got a code for that as I don't know where to start. I have however looked at the examples in here such as:

Matplotlib python change single color in colormap

However they only show how to define colours and not how to link those colours to an specific value.

Community
  • 1
  • 1
Mac
  • 991
  • 3
  • 11
  • 22
  • I believe my solution solves this problem https://stackoverflow.com/a/66821752/7871710 without the need to define a custom color map and is independent of the ordering of the numbers in the matrix. – alluppercase Mar 26 '21 at 17:40

1 Answers1

1

It turns out this is harder than I thought, so maybe someone has an easier way of doing this.

Since we need to create an image of the data, we will store them in a 2D array. We can then map the data to the integers 0 .. number of different data values and assign a color to each of them. The reason is that we want the final colormap to be equally spaced. So

value -2 --> integer 0 --> color orange
value 0 --> integer 1 --> color blue and so on.

Having nicely spaced integers, we can use a ListedColormap on the image of newly created integer values.

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.colors

# define the image as a 2D array
d = np.array([[-2,0],[1,3]])

# create a sorted list of all unique values from d
ticks = np.unique(d.flatten()).tolist()
# create a new array of same shape as d
# we will later use this to store values from 0 to number of unique values
dc = np.zeros(d.shape)
#fill the array dc
for i in range(d.shape[0]):
    for j in range(d.shape[1]):
        dc[i,j] = ticks.index(d[i,j])

# now we need n (= number of unique values) different colors        
colors= ["orange", "blue", "red", "yellow"]
# and put them to a listed colormap
colormap =  matplotlib.colors.ListedColormap(colors)


plt.figure(figsize=(5,3))
#plot the newly created array, shift the colorlimits, 
# such that later the ticks are in the middle
im = plt.imshow(dc, cmap=colormap, interpolation="none", vmin=-0.5, vmax=len(colors)-0.5)
# create a colorbar with n different ticks
cbar = plt.colorbar(im, ticks=range(len(colors)) )
#set the ticklabels to the unique values from d
cbar.ax.set_yticklabels(ticks)
#set nice tickmarks on image
plt.gca().set_xticks(range(d.shape[1]))
plt.gca().set_yticks(range(d.shape[0]))

plt.show()

enter image description here


As it may not be intuitively clear how to get the array d in the shape needed for plotting with imshow, i.e. as 2D array, here are two ways of converting the input data columns:

import numpy as np

x = np.array([0,1,0,1])
y  = np.array([ 0,0,1,1])
d_original = np.array([-2,0,1,3])

#### Method 1 ####
# Intuitive method. 
# Assumption: 
#    * Indexing in x and y start at 0 
#    * every index pair occurs exactly once.
# Create an empty array of shape (n+1,m+1) 
#   where n is the maximum index in y and
#         m is the maximum index in x
d = np.zeros((y.max()+1 , x.max()+1), dtype=np.int) 
for k in range(len(d_original)) :
    d[y[k],x[k]] = d_original[k]  
print d

#### Method 2 ####
# Fast method
# Additional assumption: 
#  indizes in x and y are ordered exactly such
#  that y is sorted ascendingly first, 
#  and for each index in y, x is sorted. 
# In this case the original d array can bes simply reshaped
d2 = d_original.reshape((y.max()+1 , x.max()+1))
print d2
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • Hey mate, that sounds exactly like what I wanted to do. Just one question though. My data is a long list of numbers (like a hundred of them) I've got the x and y positions which you didn't seem to use. But how do I use a long list of values into the 2D array, which would be your d? – Mac Dec 02 '16 at 10:17
  • I'm assuming I'd need to create an array, say if my data gives a 10x10 graph of colours I'd need a 10x10 array, is that accurate? – Mac Dec 02 '16 at 10:42
  • 1
    I edited the answer to include how to make the required array from the column structure you have. It is indeed true, that for plotting a 10x10 graph, you need a 10x10 array. The indexing is done implicitely, first column, first row of that array is the index 0,0. Last column, last row would have the index 9,9. – ImportanceOfBeingErnest Dec 02 '16 at 13:16
  • That totally did it mate! Silly question, is there an easy way to change the size of each square? Now its only unity, but if I want each square to be 3x3? – Mac Dec 03 '16 at 20:10
  • The squares have no size. Do you mean to change the labeling? Try adding `plt.gca().set_xticklabels(np.array(range(d.shape[1]))*3 )`. – ImportanceOfBeingErnest Dec 03 '16 at 20:36
  • Hey mate, the way it is now gives me an scale in x from 0 to 9 and in y from 9 to zero. How to I make it go from 0 to 9 in both directions so that the fist square (bottom left corner) is at (0,0) and the top right corner square is at (9,9)? – Mac Dec 05 '16 at 17:29
  • Why 9 ? There are two pixels and 9 is no multiple of 2. – ImportanceOfBeingErnest Dec 05 '16 at 17:34
  • Sorry, if you use the edited version for a 10x10 array. – Mac Dec 05 '16 at 17:39
  • I do not know "the edited version for a 10x10 array". What does it do? Where does it have its labels? – ImportanceOfBeingErnest Dec 05 '16 at 17:44
  • Well, even for the one displaied. It's a 2x2 array but it's going from 1 to 0 in the y direction. How do I make it Go from 0 to 1 instead? – Mac Dec 05 '16 at 18:57
  • Use `origin="lower"` as keyword argument to `plt.imshow`. – ImportanceOfBeingErnest Dec 05 '16 at 19:03
  • I found an easier way of doing it here https://stackoverflow.com/a/66821752/7871710 . The solution involves creating a 3d matrix that contains the desired RBG value at each location and plotting that 3d matrix rather than the original array. – alluppercase Mar 26 '21 at 17:39