fill regions outside contour - python

Is there any simple way to fill regions outside the contour boundary? Take the following example from matplotlib website:
import numpy as np
import matplotlib.cm as cm
import matplotlib.pyplot as plt
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (Z1 - Z2) * 2
fig, ax = plt.subplots()
CS = ax.contour(X, Y, Z, levels=(0.1, 1.5))
ax.clabel(CS, inline=True, fontsize=10)
This will plot two simple contour of values 0.1 and 1.5.
Now I would like to show regions outside these contours with colors. I tried
ax.contourf(X, Y, Z, levels=(0.1, 1.5))
but this fills regions between 0.1 and 1.5. What can I do to fill outside region but keep inside transparent/white?
(question edited to remove confusing elements from the previous version)

Related

Drawing 2D and 3D contour in the same plot in python

Is it possible to draw 2D and 3D contour plot like this in python.
Sorry I couldn't provide much detail on the plot in terms of mathematical equations and all.
Use plot_surface along with contour to project the contour. It is not limited to the Z plane; you can do this to the X and Y planes as well.
There is an example in the official documentation of Matplotlib: https://matplotlib.org/stable/gallery/mplot3d/contourf3d_2.html#sphx-glr-gallery-mplot3d-contourf3d-2-py
Note that an offset is needed to move the contour to the bottom of the 3D plot. You can set the offset equal to the lower bound of the y limit.
I created an example:
import matplotlib.pyplot as plt
import numpy as np
x = y = np.arange(-3.0, 3.0, 0.02)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X ** 2 - Y ** 2)
Z2 = np.exp(-(X - 1) ** 2 - (Y - 1) ** 2)
Z3 = np.exp(-(X + 1) ** 2 - (Y + 1) ** 2)
Z = (Z1 - Z2 - Z3) * 2
fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
# draw surface plot
surf = ax.plot_surface(X, Y, Z, lw=0.1, cmap='coolwarm', edgecolor='k')
# add color bar
fig.colorbar(surf, shrink=0.5, aspect=10)
# projecting the contour with an offset
ax.contour(X, Y, Z, 20, zdir='z', offset=-2, cmap='coolwarm')
# match the lower bound of zlim to the offset
ax.set(zlim=(-2, 1))
plt.tight_layout()
plt.show()

Strange edge behaviour of surface plot in matplotlib

I would like to make surface plot of a function which is discontinuous at certain values in parameter space. It is near these discontinuities that the plot's coloring becomes incorrect, as shown in the picture below. How can I fix this?
My code is given below:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
def phase(mu_a, mu_b, t, gamma):
theta = 0.5*np.arctan2(2*gamma, mu_b-mu_a)
epsilon = 2*gamma**2/np.sqrt((mu_a-mu_b)**2+4*gamma**2)
y1 = np.arccos(0.5/t*(-mu_a*np.sin(theta)**2 -mu_b*np.cos(theta)**2 - epsilon))
y2 = np.arccos(0.5/t*(-mu_a*np.cos(theta)**2 -mu_b*np.sin(theta)**2 + epsilon))
return y1+y2
fig = plt.figure()
ax = fig.gca(projection='3d')
# Make data.
X = np.arange(-2.5, 2.5, 0.01)
Y = np.arange(-2.5, 2.5, 0.01)
X, Y = np.meshgrid(X, Y)
Z = phase(X, Y, 1, 0.6)
# Plot the surface.
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm, linewidth=0, antialiased=False)
surf.set_clim(1, 5)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
An idea is to make all the arrays 1D, filter out the NaN values and then call ax.plot_trisurf:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
def phase(mu_a, mu_b, t, gamma):
theta = 0.5 * np.arctan2(2 * gamma, mu_b - mu_a)
epsilon = 2 * gamma ** 2 / np.sqrt((mu_a - mu_b) ** 2 + 4 * gamma ** 2)
with np.errstate(divide='ignore', invalid='ignore'):
y1 = np.arccos(0.5 / t * (-mu_a * np.sin(theta) ** 2 - mu_b * np.cos(theta) ** 2 - epsilon))
y2 = np.arccos(0.5 / t * (-mu_a * np.cos(theta) ** 2 - mu_b * np.sin(theta) ** 2 + epsilon))
return y1 + y2
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
# Make data.
X = np.linspace(-2.5, 2.5, 200)
Y = np.linspace(-2.5, 2.5, 200)
X, Y = np.meshgrid(X, Y)
X = X.ravel() # make the array 1D
Y = Y.ravel()
Z = phase(X, Y, 1, 0.6)
mask = ~np.isnan(Z) # select the indices of the valid values
# Plot the surface.
surf = ax.plot_trisurf(X[mask], Y[mask], Z[mask], cmap=cm.coolwarm, linewidth=0, antialiased=False)
surf.set_clim(1, 5)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
Some remarks:
plot_trisurf will join the XY-values via triangles; this only works well if the domain is convex
to make things draw quicker, less points could be used (the original used 500x500 points, the code here reduces that to 200x200
calling fig.gca(projection='3d') has been deprecated; instead, you could call fig.add_subplot(projection='3d')
the warnings for dividing by zero or using arccos out of range can be temporarily suppressed; that way the warning will still be visible for situations when such isn't expected behavior

Ticks in colorbar of matplotlib (Python) are not positioned correctly when I add an arbitrary value

I have plotted a contourf (and contour to plot lines on top) graph with colorbar and equispaced data levels and it is ok. However, when I add an arbitrary level value(1.1 in the example), it is not representd correctly in the colorbar.
In the reproducible example below, besides the levels with steps of 0.5, a new value with a step of 0.1 is added. In the colorbar, the distances are the same.
How can I correct this?
Thanks in advance
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(-2.0, 3.0, 0.025)
y = np.arange(-2.0, 3.0, 0.025)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (Z1 - Z2) * 2
levels = np.arange(-2,3,1)
levels=np.append(levels,1.1)
levels=np.sort(levels)
fig, ax = plt.subplots()
c = plt.contourf(X, Y, Z, levels=levels,alpha=0.15)
cs = plt.contour(X, Y, Z, levels=levels)
ax.clabel(cs, inline=1, fontsize=10)
cbar=plt.colorbar(c)
plt.show()
You should pass spacing parameter to plt.colorbar:
spacing: uniform spacing gives each discrete color the same space; proportional makes the space proportional to the data interval.
cbar=plt.colorbar(c, spacing = 'proportional')

Slices across Contourf plots at different angles to get 2D line plots

I am trying to generate 2D line plots at different angles or slices of a matplotlib contourf plot.
As an example from the matplotlib contourf demo example below
import numpy as np
import matplotlib.pyplot as plt
origin = 'lower'
delta = 0.025
x = y = np.arange(-3.0, 3.01, delta)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (Z1 - Z2) * 2
nr, nc = Z.shape
fig1, ax2 = plt.subplots(constrained_layout=True)
CS = ax2.contourf(X, Y, Z, 10, cmap=plt.cm.viridis, origin=origin,extend='both')
ax2.set_title('Random Plot')
ax2.set_xlabel('X Axis')
ax2.set_ylabel('Y Axis')
cbar = fig1.colorbar(CS)
Ideally, I want to generate lines at different angles (30,45,60 degrees) across the map (starting at any arbitrary point till the end of existing array) and then plot the Z variations across that line.
I think a simpler problem in principle would be, lines from (X2,Y2) to (X1,Y1) and plot the Z variation for the given contour (which is already interpolated data).
As an example, original problem would be line from (-3,-3) at angle 45 deg across. Analogous problem would be lets say a line from (-3,-3) to (3,3) and plot the Z variation at different locations on that line.
The source contour plot generated is :
Here is a rather inefficient approach, but it does the job. It recalculates the function on a new grid of which it only needs the diagonal.
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import RectBivariateSpline
delta = 0.025
x = y = np.arange(-3.0, 3.01, delta)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X ** 2 - Y ** 2)
Z2 = np.exp(-(X - 1) ** 2 - (Y - 1) ** 2)
Z = (Z1 - Z2) * 2
nr, nc = Z.shape
x1, y1 = -3, -2
x2, y2 = 3, 2
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(15, 5))
CS = ax1.contourf(X, Y, Z, 10, cmap=plt.cm.viridis, origin='lower', extend='both')
ax1.plot([x1, x2], [y1, y2], color='k', ls='--', lw=3, alpha=0.6)
ax1.set_xlabel('X Axis')
ax1.set_ylabel('Y Axis')
cbar = fig.colorbar(CS, ax=ax1)
spline_func = RectBivariateSpline(x, y, Z)
xline = np.linspace(x1, x2, 200)
yline = np.linspace(y1, y2, 200)
zline = spline_func(xline, yline)
ax2.plot(xline, zline.diagonal())
ax2.set_xlabel('X Axis')
ax2.set_ylabel('Z Axis')
plt.show()

Adding extra contour lines using matplotlib 2D contour plotting

I am creating a two-dimensional contour plot with matplotlib. Using the documentation provided http://matplotlib.org/examples/pylab_examples/contour_demo.html, such a contour plot can be created by
import matplotlib
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
# difference of Gaussians
Z = 10.0 * (Z2 - Z1)
plt.figure()
CS = plt.contour(X, Y, Z)
plt.clabel(CS, inline=1, fontsize=10)
plt.title('Simplest default with labels')
which outputs the following plot.
The documentation details how to manually label certain contours (or "lines") on the existing plot. My question is how to create more contour lines than those shown.
For example, the plot shown has two bivariate gaussians. The upper right has three contour lines, at 0.5, 1.0, and 1.5.
How could I add contour lines at say 0.75 and 1.25?
Also, I should be able to zoom in and (in principle) add dozens of dozens of contour lines from (for example) 1.0 and 1.5. How does one do this?
To draw isolines at specified level values, set the levels parameter in .contour:
levels = np.arange(-1.0,1.5,0.25)
CS = plt.contour(X, Y, Z, levels=levels)
import numpy as np
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
# difference of Gaussians
Z = 10.0 * (Z2 - Z1)
plt.figure()
levels = np.arange(-1.0,1.5,0.25)
CS = plt.contour(X, Y, Z, levels=levels)
plt.clabel(CS, inline=1, fontsize=10)
plt.title('levels = {}'.format(levels.tolist()))
plt.show()
The sixth figure here uses this method to draw isolines at levels = np.arange(-1.2, 1.6, 0.2).
To zoom in, set the x limits and y limits of the desired region:
plt.xlim(0, 3)
plt.ylim(0, 2)
and to draw, say, 24 automatically-chosen levels, use
CS = plt.contour(X, Y, Z, 24)
For example,
import numpy as np
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
# difference of Gaussians
Z = 10.0 * (Z2 - Z1)
plt.figure()
N = 24
CS = plt.contour(X, Y, Z, N)
plt.clabel(CS, inline=1, fontsize=10)
plt.title('{} levels'.format(N))
plt.xlim(0, 3)
plt.ylim(0, 2)
plt.show()
The third figure here uses this method to draw 6 isolines.

Categories

Resources