numpymatplotlibnanmasked-array

Why is matplotlib plotting nan differently than zeros?


I have 2D numpy array that I want to mask and plot. I have tried this.

import numpy as np
import matplotlib.pyplot as plt

a = np.random.random((101,99))

data1 = a.copy()
bound = np.percentile(data1, 80)
data1[data1<bound] = np.nan
plt.figure()
plt.imshow(data1)

Output:

enter image description here

data2 = a.copy()
data2[data2 < bound] = 0
plt.figure()
plt.imshow(data2)

Output:

enter image description here

I am expecting the first image to look like the second image, where there are the same number of white pixels as dark-blue pixels, and the white pixels are in the same position as the dark-blue pixels. Clearly, there are more white pixels than dark-blue pixels. I feel like I'm missing something simple. Is something wrong with my matplotlib configuration?

EDIT:

To show the first image actually has more white pixels than the second image -- and there are no anti-aliasing effects -- I have rerun the code block using plt.gca().set_facecolor('black'):

a = np.random.random((101,99))

data1 = a.copy()

bound = np.percentile(data1, 80)
data1[data1<bound] = np.nan
plt.figure()
plt.imshow(data1)
plt.gca().set_facecolor('black')

Output:

enter image description here

data2 = a.copy()
data2[data2 < bound] = 0
plt.figure()
plt.imshow(data2)

Output:

enter image description here


Solution

  • The effect is due to antialiasing. For each pixel on the screen, matplotlib averages out the corresponding pixels of the data. If one of the data pixels is NaN, the complete screen pixel is considered transparent. With zeros instead of NaNs, the standard averaging is used.

    The following code example illustrates what's happening.

    import numpy as np
    import matplotlib.pyplot as plt
    
    a = np.random.random((101, 99))
    
    data1 = a.copy()
    bound = np.percentile(data1, 80)
    data1[data1 < bound] = np.nan
    fig, (ax1, ax2, ax3) = plt.subplots(ncols=3, figsize=(15, 6))
    ax1.imshow(data1)
    ax2.imshow(data1)
    ax2.set_facecolor('black')
    
    data2 = a.copy()
    data2[data2 < bound] = 0
    ax3.imshow(data2)
    plt.tight_layout()
    plt.show()
    

    figsize=(15,6)

    Now, the same, but with figsize=(10,4)

    figsize=(10,4)