Closed asbjos closed 1 year ago
Thank you @asbjos for using mpltern and reporting the issue you faced. So far I guess this is likely the expected behavior inherited from matplotlib; (tricontourf
is NOT the original of mpltern but inherited from Matplotlib, and mpltern simply offers a wrapper for that.) You can take a look on:
https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.tricontourf.html
They explicitly write "All values in z must be finite. Hence, nan and inf values must either be removed or set_mask be used." So, probably you can try to use the mask
option.
Thank you for the quick response, @yuzie007 ! Could you please guide me to how to implement such a mask? I tried setting a mask like this:
ax2.tricontourf(x, y, z, T, mask=np.isnan(T))
But get the ValueError that "mask array must have same length as triangles array", although x
, y
, z
, T
, and np.isnan(T)
all have the same length (=66).
Thank you @asbjos for your report. I found that the mask
option works for triangles, not directly for given points. For this we need to make a Triangulation
object on our side. Since this works so far only for Matplotlib xy space (not for mpltern ternary space), we need to convert the coordinates from ternary to the original xy coordinates. Then, we need to identify the triangle indices we want to color in white (for this you probably need to refer to https://matplotlib.org/stable/api/tri_api.html#matplotlib-tri).
The running example is as follows.
import numpy as np
import matplotlib.pyplot as plt
from mpltern.datasets import get_triangular_grid
from matplotlib.tri import Triangulation
#%% matplotlib
plt.figure()
ax1 = plt.subplot(121, projection="3d")
x = np.linspace(0, 1, 11)
y = np.linspace(0, 1, 11)
x, y = np.meshgrid(x, y)
z = 1 - x - y
T = x*y
T[np.where(z < 0)] = np.nan
T[2, 1] = np.nan
ax1.contourf(x, y, T)
ax1.text(x[2, 1], y[2, 1], 0, "nan", color="r")
#%% mpltern
ax2 = plt.subplot(122, projection="ternary")
x, y, z = get_triangular_grid(11)
T = x * y
# T[3] = np.nan # results in error
tlr = np.column_stack((x, y, z))
xx, yy = ax2.transProjection.transform(tlr).T # coordinates in xy
tri = Triangulation(xx, yy)
mask = np.zeros(len(tri.triangles), dtype=bool)
mask[20] = 1 # you need to identify the triangle index you want to color in white
tri.set_mask(mask)
ax2.tricontourf(tri, T, transform=ax2.transData) # plot in xy coordinates
plt.show()
If you want to give np.nan at triangle corner points as you originally wished, I would suggest to make an enhancement request to Matplotlib tricontourf
(or any related methods working on triangular grids), as this is the limitation rather inherited from Matplotlib.
While matplotlib supports having nan-values in an array passed to contour(f), the mpltern analogue tricontour(f) raises a ValueError: z array must not contain non-finite values within the triangulation.
Could this be implemented please? (I'm doing least_squares over the ternary compositions, and if no solution is found by it, I want to mask out those concentrations by passing NaNs)