I generate a matplotlib 3d surface plot. I only need to see the upper-triangular half of the matrix on the plot, as the other half is redundant.
np.triu() makes the redundant half of the matrix zeros, but I'd prefer if I can make them Nans, then those cells don't show up at all on the surface plot.
What would be a pythonic way to fill with NaN instead of zeros? I cannot do a search-and-replace 0 with NaN, as zeros will appear in the legitimate data I want to display.
NumPy: triu() function Upper triangle of an array. The triu() function is used to get a copy of a matrix with the elements below the k-th diagonal zeroed. Number of rows in the array. Diagonal above which to zero elements.
a zero square matrix is upper and lower triangular as well as diagonal matrix.
You can use numpy.tril_indices()
to assign the NaN
value to lower triangle, e.g.:
>>> import numpy as np
>>> m = np.triu(np.arange(0, 12, dtype=np.float).reshape(4,3))
>>> m
array([[ 0., 1., 2.],
[ 0., 4., 5.],
[ 0., 0., 8.],
[ 0., 0., 0.]])
>>> m[np.tril_indices(m.shape[0], -1)] = np.nan
>>> m
array([[ 0., 1., 2.],
[ nan, 4., 5.],
[ nan, nan, 8.],
[ nan, nan, nan]])
tril_indices()
might be the obvious approach here that generates the lower triangular indices and then you can use those to set those in input array to NaNs
.
Now, if you care about performance, you can use boolean indexing
after creating a mask of such lower triangular shape and then set those to NaNs
. The implementation would look like this -
m[np.arange(m.shape[0])[:,None] > np.arange(m.shape[1])] = np.nan
So, np.arange(m.shape[0])[:,None] > np.arange(m.shape[1])
is the mask here that was created using broadcasting
.
Sample run -
In [51]: m
Out[51]:
array([[ 11., 49., 23., 30.],
[ 40., 41., 19., 26.],
[ 32., 36., 30., 25.],
[ 15., 27., 25., 40.],
[ 33., 18., 45., 43.]])
In [52]: np.arange(m.shape[0])[:,None] > np.arange(m.shape[1]) # mask
Out[52]:
array([[False, False, False, False],
[ True, False, False, False],
[ True, True, False, False],
[ True, True, True, False],
[ True, True, True, True]], dtype=bool)
In [53]: m[np.arange(m.shape[0])[:,None] > np.arange(m.shape[1])] = np.nan
In [54]: m
Out[54]:
array([[ 11., 49., 23., 30.],
[ nan, 41., 19., 26.],
[ nan, nan, 30., 25.],
[ nan, nan, nan, 40.],
[ nan, nan, nan, nan]])
Runtime tests -
This section compares the boolean indexing based approach listed in this solution to np.tril_indices
based one in the other solution
for performance.
In [38]: m = np.random.randint(10,50,(1000,1100)).astype(float)
In [39]: %timeit m[np.tril_indices(m.shape[0], -1)] = np.nan
10 loops, best of 3: 62.8 ms per loop
In [40]: m = np.random.randint(10,50,(1000,1100)).astype(float)
In [41]: %timeit m[np.arange(m.shape[0])[:,None] > np.arange(m.shape[1])] = np.nan
100 loops, best of 3: 8.03 ms per loop
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With