102

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)?

Adam Fraser
  • 6,255
  • 10
  • 42
  • 54

2 Answers2

94

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. :]

Adam Fraser
  • 6,255
  • 10
  • 42
  • 54
  • It definitely does the trick. Official docs show nothing more. – Agos May 11 '11 at 15:36
  • 7
    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. – keflavich Feb 23 '13 at 19:56
  • 3
    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. – tacaswell Nov 03 '14 at 13:41
  • 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. – Gilly Aug 02 '16 at 00:08
  • 3
    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? – MaciekS Apr 19 '17 at 11:48
  • 2
    @MaciekS , when using a diverging colormap, you do not want your `nan` values and median values being plotted the same color e.g. white. – AGS Sep 20 '17 at 20:00
73

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

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

current_cmap = matplotlib.cm.get_cmap()
current_cmap.set_bad(color='red')
plt.imshow(arr)

plot result

Arcturus B
  • 5,181
  • 4
  • 31
  • 51