I have boolean data on a 2D grid and want to use matplotlib
to plot a countour between the areas where the data is True
and False
.
However, the separation between these areas is not smooth within the actual data. How can I compute a smoothed countour given this data?
Here is a minimal example:
import numpy as np
import matplotlib.pyplot as plt
# generate some non-smooth example data
MESHSIZE = 10
REFINEMENT = 4*MESHSIZE
x = np.linspace(-MESHSIZE, MESHSIZE, REFINEMENT)
xv, yv = np.meshgrid(x, x)
xvf = xv.reshape(-1)
yvf = yv.reshape(-1)
def choppy_circle(x, y):
inner = x.astype(int)**2+y.astype(int)**2 < 10.0
return inner
# consider this the *actual* data given to me as-is
my_x = xvf
my_y = yvf
my_z = choppy_circle(xvf, yvf)
# need to visualize the contour that separates areas where
# my_z is True/False
plt.tricontour(my_x, my_y, my_z, levels=np.array([1.0-1e-3]))
plt.scatter(xv, yv, s=0.1)
plt.show()
This produces the following plot, which is faithful to the data, but not what I'm looking for:
How can I use the data given in my_x
, my_y
and my_z
to construct a smoothed contour around the domain where my_z
is True
?
Something like this:
Extracting the contour data like proposed in this answer and interpolating with splines as proposed by @user2640045 allows doing it for arbitrary contours:
# my_x, my_y, my_z as above...
# get contour data
cs = plt.tricontour(my_x, my_y, my_z, levels=np.array([1.0-1e-3]))
print(type(cs))
# process each contour
for contour in cs.collections[0].get_paths():
vert = contour.vertices
vert_x = vert[:, 0]
vert_y = vert[:, 1]
# plot contour
plt.plot(vert_x, vert_y)
# interpolate contour points
s = 20
tck, u = interpolate.splprep([vert_x, vert_y], per=1, s=s)
x_interp, y_interp = interpolate.splev(np.linspace(0, 1, 10**3), tck)
# plot interpolated contour
plt.plot(x_interp, y_interp)
# plot grid
plt.scatter(xv, yv, s=0.1)
# display plot
plt.show()
The important bit is the loop header
for contour in cs.collections[0].get_paths():
where the x-y data of each contour line is obtained.