0

I have a CSV data file that has data of the (simplified) form:

1.2, 2.2,   o,   s
1.7,   s, 2.4, 2.9
  o, 0.9, 0.1, NaN
1.4,   s, 0.5, 0.9

NB: This contains the character 'o', not the number zero (0). This file is mixed with characters that indicate different types of "failed" measurements. These matrices are typically much larger than a 4x4 grid.

I would like to plot these matrices on a colour map using matplotlib but where each failed measurement value is assigned its own colour on the plot, whilst preserving the colour range of legitimate data.

Something with logic like e.g. `

if data == 'o':
   pixel_colour = 'red'

if data == 's':
   pixel_colour = 'black'

if data == 'NaN':
   pixel_colour = 'white'

` I have seen this helpful post (Changing colours of pixels of plt.imshow() image.) already but I can't think of how to get this to handle heterogeneous data types.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
GBean
  • 41
  • 9
  • Do you have just o and s? Or are there more categories? – Jody Klymak May 25 '21 at 14:33
  • For now, just 'o' and 's'. NaN represents missing data in this case, which I should have specified. – GBean May 25 '21 at 14:41
  • Not an answer to your question, but you may just want to use "over" and "under" colors in your colormap: https://stackoverflow.com/questions/11386054/python-matplotlib-change-default-color-for-values-exceeding-colorbar-range – Jody Klymak May 25 '21 at 15:17
  • To answer your question you can always pass an rgbA array to imshow and it will just use those values. – Jody Klymak May 25 '21 at 15:26
  • Thank you for sharing! That makes sense but I think the issue would be assigning large numbers to, say, 's' will throw off the scale of the colour map. Also, this is only useful for 2 different types of 'failed' measurement. We have 3 and potentially more in the future. Thanks again though! – GBean May 25 '21 at 15:28

1 Answers1

2

To properly answer your question, you can mask the bad values, norm, map to RGBA, and then fill in the other colors as you like

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

data = np.array([[1.2, 2.2,   'o',   's', 2.5],
                 [1.7,   's', 2.4, 2.9, 1.7],
                 ['o', 0.9, 0.1, 'NaN', 0.4]])
data[data == 'NaN'] = -4000           
data[data == 'o'] = -5000           
data[data == 's'] = -6000
data = data.astype(np.float64)

norm = mpl.colors.Normalize(vmin=0, vmax=3)
cmap = plt.get_cmap('viridis')

# convert from 0-1:
datan = norm(data)
# convert to rgba:
rgba = cmap(datan)
# Fill in colours for the out of range data:
rgba[data==-4000, :] = [1, 1, 1, 1]
rgba[data==-5000, :] = [1, 0, 0, 1]
rgba[data==-6000, :] = [0, 1, 1, 1]

# plot:
fig, ax = plt.subplots()
ax.imshow(rgba)
plt.show()

enter image description here

Jody Klymak
  • 4,979
  • 2
  • 15
  • 31
  • This is great, thank you! However, I can't seem to get a colour bar to work on the side of the plot? Getting an error: RuntimeError: No mappable was found to use for colorbar creation. Fi rst define a mappable such as an image (with imshow) or a contour se t (with contourf). – GBean May 26 '21 at 12:20
  • For future reference: I eventually fixed the colour bar issue by following the matplotlib colorbar docs: https://matplotlib.org/stable/gallery/color/colorbar_basics.html – GBean May 26 '21 at 13:28