How can I plot NaN values as a special color with imshow?
Asked Answered
P

2

108

I am trying to use imshow in matplotlib to plot data as a heatmap, but some of the values are NaNs. I'd like the NaNs to be rendered as a special color not found in the colormap.

example:

import numpy as np
import matplotlib.pyplot as plt
f = plt.figure()
ax = f.add_subplot(111)
a = np.arange(25).reshape((5,5)).astype(float)
a[3,:] = np.nan
ax.imshow(a, interpolation='nearest')
f.canvas.draw()

The resultant image is unexpectedly all blue (the lowest color in the jet colormap). However, if I do the plotting like this:

ax.imshow(a, interpolation='nearest', vmin=0, vmax=24)

--then I get something better, but the NaN values are drawn the same color as vmin... Is there a graceful way that I can set NaNs to be drawn with a special color (eg: gray or transparent)?

Postdiluvian answered 5/4, 2010 at 14:3 Comment(1)
A few years later (matplotlib.__version__=='1.2.1'), this works without a problem.Fogdog
D
80

With newer versions of Matplotlib, it is not necessary to use a masked array anymore.

For example, let’s generate an array where every 7th value is a NaN:

arr = np.arange(100, dtype=float).reshape(10, 10)
arr[~(arr % 7).astype(bool)] = np.nan

.cm.get_cmap() is replaced by .colormaps.get_cmap('viridis') in matplotlib v3.7.0

Set the color with .set_bad.

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

arr = np.arange(100, dtype=float).reshape(10, 10)
arr[~(arr % 7).astype(bool)] = np.nan

cmap = mpl.colormaps.get_cmap('viridis')  # viridis is the default colormap for imshow
cmap.set_bad(color='red')

plt.imshow(arr, cmap=cmap)

plot result


.cm.get_cmap() is deprecated

We can modify the current colormap and plot the array with the following lines:

current_cmap = mpl.cm.get_cmap()
current_cmap.set_bad(color='red')
plt.imshow(arr)
Deliquesce answered 9/10, 2017 at 14:40 Comment(2)
is it possible to use 0 instead of np.nan?Extravaganza
@yukashimahuksay: yes, but you would need to mask these values. Eg. arr = np.ma.array(arr, mask=(arr == 0)).Deliquesce
P
98

Hrm, it appears I can use a masked array to do this:

masked_array = np.ma.array (a, mask=np.isnan(a))
cmap = matplotlib.cm.jet
cmap.set_bad('white',1.)
ax.imshow(masked_array, interpolation='nearest', cmap=cmap)

This should suffice, though I'm still open to suggestions. :]

Postdiluvian answered 5/4, 2010 at 14:30 Comment(6)
It definitely does the trick. Official docs show nothing more.Mehalek
A side point - I think doing this will override the default matplotlib.cm.jet, so I usually make a copy: import copy; cmap=copy.copy(matplotlib.cm.jet). Also, if you want to set 0-values to a different color, something like cmap._init(); cm._lut[:,0] = (1,1,1,1) should work.Kowatch
There is also set_over and set_under to control the coloring of out of range values. The default behaviour is to match the top/bottom of the color range.Piperine
It is not entirely obvious that 'w' means white. Maybe you could replace 'w' with 'white', so that it is more obvious that you could replace it, for example, with 'red'? And explain the second parameter to set_bad? Apologies to make suggestions to such an old answer, but this would be helpful.Army
Is the masked_array required at all? If a contains NaN values (so it seems as mask=np.isnan(a)), then just imshow-ing the array a with the customized map cmap will display NaN-cells with the required colour (white). So it works for me. Are there any exceptions?Immaterialize
@Immaterialize , when using a diverging colormap, you do not want your nan values and median values being plotted the same color e.g. white.Drucilladrucy
D
80

With newer versions of Matplotlib, it is not necessary to use a masked array anymore.

For example, let’s generate an array where every 7th value is a NaN:

arr = np.arange(100, dtype=float).reshape(10, 10)
arr[~(arr % 7).astype(bool)] = np.nan

.cm.get_cmap() is replaced by .colormaps.get_cmap('viridis') in matplotlib v3.7.0

Set the color with .set_bad.

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

arr = np.arange(100, dtype=float).reshape(10, 10)
arr[~(arr % 7).astype(bool)] = np.nan

cmap = mpl.colormaps.get_cmap('viridis')  # viridis is the default colormap for imshow
cmap.set_bad(color='red')

plt.imshow(arr, cmap=cmap)

plot result


.cm.get_cmap() is deprecated

We can modify the current colormap and plot the array with the following lines:

current_cmap = mpl.cm.get_cmap()
current_cmap.set_bad(color='red')
plt.imshow(arr)
Deliquesce answered 9/10, 2017 at 14:40 Comment(2)
is it possible to use 0 instead of np.nan?Extravaganza
@yukashimahuksay: yes, but you would need to mask these values. Eg. arr = np.ma.array(arr, mask=(arr == 0)).Deliquesce

© 2022 - 2024 — McMap. All rights reserved.