Matplotlib grayscale heatmap with visually distinct "NA" squares fields

10,553

Solution 1

A simple solution is to just hatch the background axes patch. E.g.:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
np.random.seed(1977)

data = np.random.random((10,25))
data = np.ma.masked_greater(data, 0.8)

fig, ax = plt.subplots()
im = ax.pcolormesh(data, cmap=cm.gray, edgecolors='white', linewidths=1,
                   antialiased=True)
fig.colorbar(im)

ax.patch.set(hatch='xx', edgecolor='black')

plt.show()

enter image description here

Note that if you'd prefer not to have the borders drawn in between empty cells, you can use pcolor instead of pcolormesh. For example, if we change the line:

im = ax.pcolormesh(data, cmap=cm.gray, edgecolors='white', linewidths=1,
                   antialiased=True)

to:

im = ax.pcolor(data, cmap=cm.gray, edgecolors='white', linewidths=1)

We'll get:

enter image description here

The difference is subtle -- Lines are not drawn between adjacent empty cells with pcolor. Which aesthetics you prefer is purely personal, but it highlights a key difference between pcolor and pcolormesh.

Solution 2

I was not able to reproduce Joe's answer by adding a patch by ax.patch.set_hatch('x'). Instead I had to create the patch as a rectangle according to this question as

import numpy as np
import matplotlib.pyplot as plt

import matplotlib.cm as cm
import matplotlib.patches as patches

data = np.random.random((10,25))
data = np.ma.masked_greater(data, 0.8)

fig, ax = plt.subplots()
im = ax.pcolormesh(data, cmap=cm.gray, edgecolors='white', linewidths=0)
fig.colorbar(im)

# ax.patch.set_hatch('x')  replaced by:
p = patches.Rectangle((0,0), 25, 10, hatch='xx', fill=None,zorder=-10)
ax.add_patch(p)

plt.show()

Furthermore, pcolormesh seems to be sorted out by now, so one can use it here.

Share:
10,553
Bryce Thomas
Author by

Bryce Thomas

https://www.linkedin.com/in/brycethomas/

Updated on July 12, 2022

Comments

  • Bryce Thomas
    Bryce Thomas almost 2 years

    I am creating a heatmap to be used in a publication. The publication is restricted to black and white printing, so I'm creating the heatmap in grayscale. The problem I have is that there are some squares in the heatmap which are "Not Applicable" which I want to visually differentiate from the other cells. My understanding is that this may(?) be possible using numpy's masked arrays if the heatmap is colored at both ends of the scale, and that masked fields may simply display as white. The problem is, I would like to use the full spectrum from white to black to illustrate the range of the non-NA data. Is there anyway to distinguish NA cells with some other visual mechanism, such as a strikethrough?

    Below is a minimum example of grayscale with a masked array (adapted from here). The NA values are probably masked here, you just can't tell because it is using white which is already being used as the color on the high end of the valid spectrum.

    import numpy as np
    from pylab import *
    
    z = rand(10, 25)
    z = np.ma.masked_array(z,mask=z>0.8)
    
    c = pcolor(z)
    set_cmap('gray')
    colorbar()
    c = pcolor(z, edgecolors='w', linewidths=1)
    axis([0,25,0,10])
    savefig('plt.png')
    show()
    

    enter image description here