I have a loss function of two variables W1, W2 and an output z = F(W1,W2).
Now I plot the contour map of this loss function. Now say, I have calculated gradient vector at two points, therefore I have two gradient vectors now. I want to plot these gradient vector on my contour plot but I have no idea how to procces. Any help is appreciated
enter code here
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
feature_x = np.arange(-50, 50, 2)
feature_y = np.arange(-50, 50, 3)
# Creating 2-D grid of features
[X, Y] = np.meshgrid(feature_x, feature_y)
fig, ax = plt.subplots(1, 1)
z = 0.5*np.array((Y-X)*(Y-X) + 0.5*(1-X)*(1-X))
# plots contour lines
ax.contour(X, Y, z, 10, cmap = 'jet')
ax.grid(True)
ax.axis('scaled')
#ax.clabel(cp, inline=1, fontsize=10)
ax.set_title('Contour Plot')
ax.set_xlabel('feature_x')
ax.set_ylabel('feature_y')
plt.show()
You could use FancyArrowPatch to draw the gradients at a few selected positions.
from matplotlib.patches import FancyArrowPatch
x1 = -20 # position of the gradient
y1 = 10
dz1_dx = 10 # value of the gradient at that position
dz1_dy = -5
arrow = FancyArrowPatch((x1, y1), (x1+dz1_dx, y1+dz1_dy),
arrowstyle='simple', color='k', mutation_scale=10)
ax.add_patch(arrow)
Otherwise if you want to plot the whole vector field quiver might be an option:
feature_x = np.arange(-50, 50, 2)
feature_y = np.arange(-50, 50, 2)
x, y = np.meshgrid(feature_x, feature_y)
z = 0.5*(y-x)**2 + 0.5*(1-x)**2
u = 2*x - y - 1
v = y - x
# Normalize all gradients to focus on the direction not the magnitude
norm = np.linalg.norm(np.array((u, v)), axis=0)
u = u / norm
v = v / norm
fig, ax = plt.subplots(1, 1)
ax.set_aspect(1)
ax.plot(feature_x, feature_y, c='k')
ax.quiver(x, y, u, v, units='xy', scale=0.5, color='gray')
ax.contour(x, y, z, 10, cmap='jet', lw=2)
arrow = FancyArrowPatch((35, 35), (35+34*0.2, 35+0), arrowstyle='simple',
color='r', mutation_scale=10)
ax.add_patch(arrow) # NOTE: this gradient is scaled to make it better visible
I added the line y = x in this plot and marked the point where this lines intersects with a contour line. Here you can see clearly
that:
Gradients are orthogonal to level surfaces
So for your point (80, 80) the gradient (79, 0) is correct even so the general shape of isolines maybe suggest that there should be be a part in y-direction.
But if you look along the line y=x you see that the gradients there are always only in x-direction.
Related
Basically I have a surface plot consisting of a set of time series, and I would like to add a section plan at a specific height, to better understand the period of the year when values are higher than the selected threshold.
From this:
where the plan is shown but not as a section
To This:
Any suggestion?
Plying with alpha and camera elevation did not solve the issue
the plan still seems to be in front of the figure, not as a section
Drawing in 3 steps
As others pointed out, matplotlib's 3D capabilities are somewhat limited. To hide objects behind other objects, it uses the painter's algorithm. So, the objects are simply drawn back to front, and no objects are put partly before and partly behind some plane. Matplotlib calculates some average depth of each object to define the order. You can overwrite this order via ax.computed_zorder = False, as the automatic calculation is not always what is wished.
You could draw the "layers" yourself:
the 3D surface
then the plane
then the part of the 3D surface that should be visible on top
An example:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
from scipy.ndimage.filters import gaussian_filter
x = np.linspace(-10, 10, 51)
y = np.linspace(-10, 10, 51)
X, Y = np.meshgrid(x, y)
np.random.seed(20220201)
Z = np.random.rand(*X.shape) ** 5
Z[X ** 2 + Y ** 2 > 30] = 0
Z = gaussian_filter(Z, sigma=2) * 100
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
ax.computed_zorder = False
ax.plot_surface(X, Y, Z, cmap='turbo')
special_z = 16
ax.plot_surface(X, Y, np.full_like(Z, special_z), color='blue', alpha=0.4)
ax.plot_surface(X, Y, np.where(Z >= special_z, Z, np.nan), cmap='turbo', vmin=0)
plt.show()
Drawing layer by layer
An alternative way could be to draw the surface one layer at a time.
The example at the left shows the surface divided into 30 layers, the example at the right stops at a given height, visualizing the intersection.
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
from scipy.ndimage.filters import gaussian_filter
x = np.linspace(-10, 10, 51)
y = np.linspace(-10, 10, 51)
X, Y = np.meshgrid(x, y)
np.random.seed(20220201)
Z = np.random.rand(*X.shape) ** 5
Z[X ** 2 + Y ** 2 > 30] = 0
Z = gaussian_filter(Z, sigma=2) * 100
fig = plt.figure()
for which in ['left', 'right']:
ax = fig.add_subplot(121 + (which == 'right'), projection="3d")
ax.computed_zorder = False
layers = np.linspace(Z.min(), Z.max(), 32)[1:-1]
colors = plt.get_cmap('turbo', len(layers)).colors
special_z = 16
plane_drawn = False
for layer, color in zip(layers, colors):
if layer >= special_z and not plane_drawn:
ax.plot_surface(X, Y, np.full_like(Z, special_z), color='blue', alpha=0.5, zorder=2)
plane_drawn = True
ax.contour(X, Y, Z, levels=[layer], offset=layer, colors=[color])
if plane_drawn and which == 'right':
break
plt.show()
I wrote a matplotlib program that plots a 2d surface embedded in three dimensional space using plot_trisurf() and then plots a vector field defined on the surface using quiver(). I'd like the surface to be opaque to the vector field but instead the program plots both the vectors that are in front of the surface and those that are behind the surface with respect to the camera, despite the surface's alpha value being 1.0.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.tri as mtri
fig = plt.figure(figsize=plt.figaspect(1.0) * 1.7)
# Make a mesh in the space of parameterisation variables u and v
u = np.linspace(0, 2.0 * np.pi, endpoint=True, num=30) # u: theta
v = np.linspace(0, 2.0 * np.pi, endpoint=True, num=60) # v: phi
u, v = np.meshgrid(u, v)
u, v = u.flatten(), v.flatten()
x, y, z = F(u, v)
# Triangulate parameter space to determine the triangles
tri = mtri.Triangulation(u, v)
# Plot the surface. The triangles in parameter space determine which x, y, z
# points are connected by an edge.
ax = fig.add_subplot(projection='3d')
ax.plot_trisurf(x, y, z, triangles=tri.triangles, cmap=plt.cm.magma, alpha = 1.0)
xl = ax.get_xlim()
yl = ax.get_ylim()
ax.set_zlim(xl[0], xl[1])
plt.show()
Here's where the vector field gets plotted:
alpha = 1.0
lenght = 0.25
ax.quiver(xf, yf, zf, ox, oy, oz, color='red', alpha=alpha, length=lenght, normalize=True)
Here's an example that shows how the full vector field gets plotted.
I've also tried adding zorder parameters in the plotting functions but with no success: ax.plot_trisurf(x, y, z, triangles=tri.triangles, cmap=plt.cm.magma, alpha = 1.0, zorder = 2)
Set antialiased=False in plot_trisurf
I would like to make a 3d plot of a surface parametrised by a function, and I would like the surface to be of one color (say white) where it is above some value a, and of another color (say black) where it is below a.
Here is the code to generate and plot the surface (the way the surface is generated is not important, it could be a much simpler function):
from __future__ import division
import numpy as np
import time,random
random.seed(-2)
def build_spden(N,M, alpha):
#computes the spectral density in momentum space
sp_den = np.zeros((N,M))
for k1 in prange(-N//2, N//2):
for k2 in prange(-M//2, M//2):
sp_den[k1,k2] = np.abs(2*(np.cos(2*np.pi*k1/N)+np.cos(2*np.pi*k2/M)-2))
sp_den[0,0]=1
return 1/sp_den**(alpha/2)
def gaussian_field(N,M,alpha):
'''Builds a correlated gaussian field on a surface NxM'''
spectral_density = build_spden(N,M, alpha)
# FFT of gaussian noise:
noise_real = np.random.normal(0, 1, size = (N,M))
noise_fourier = np.fft.fft2(noise_real)
# Add correlations by Fourier Filtering Method:
convolution = noise_fourier*np.sqrt(spectral_density)
# Take IFFT and exclude residual complex part
correlated_noise = np.fft.ifft2(convolution).real
# Return normalized field
return correlated_noise * (np.sqrt(N*M)/np.sqrt(np.sum(spectral_density)) )
#PLOT
N = 2**5
alpha = .75
a = -.1985
surf = gaussian_field(N,N,alpha)
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
x = np.outer(np.arange(0, N), np.ones(N))
y = x.copy().T # transpose
z = surf
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.plot_surface(x, y, z,alpha=.4) #plot the surface
z2 = a*np.ones((N,N))
ax.plot_surface(x, y, z2, alpha=0.9) #plot a plane z = a.
plt.show()
The output is:
I would therefore like the surface to be white above the plane and black below.
Many thanks !
You can define a custom color map and pass to plot_surface:
from matplotlib.colors import ListedColormap, BoundaryNorm
cmap = ListedColormap(['r', 'b'])
norm = BoundaryNorm([z.min(), a, z.max()], cmap.N)
ax.plot_surface(x, y, z, cmap=cmap, norm=norm, alpha=.4) #plot the surface
z2 = a*np.ones((N,N))
ax.plot_surface(x, y, z2, colalpha=0.9) #plot a plane z = a.
plt.show()
Output:
I'd like to make a triangle plot in matplotlib with a mostly-transparent surface. I'm running the example code at https://matplotlib.org/mpl_examples/mplot3d/trisurf3d_demo.py:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
n_radii = 8
n_angles = 36
# Make radii and angles spaces (radius r=0 omitted to eliminate duplication).
radii = np.linspace(0.125, 1.0, n_radii)
angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)
# Repeat all angles for each radius.
angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)
# Convert polar (radii, angles) coords to cartesian (x, y) coords.
# (0, 0) is manually added at this stage, so there will be no duplicate
# points in the (x, y) plane.
x = np.append(0, (radii*np.cos(angles)).flatten())
y = np.append(0, (radii*np.sin(angles)).flatten())
# Compute z to make the pringle surface.
z = np.sin(-x*y)
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_trisurf(x, y, z, linewidth=0.2, antialiased=True)
plt.show()
I can set
ax.plot_trisurf(x, y, z, linewidth=0.2, alpha = 0.2, antialiased=True)
to set the opacity to 0.2, but then the lines disappear. Furthermore, when I change the linewidth, even without the alpha, I see no change in the thickness of the lines between the points. How can I have a triangle plot where the faces are mostly transparent and the lines are clearly visible?
Hello I have come across a problem where I need to generate dataset from a distribution given on a scatter plot where datapoints are mostly centred around the centre of the circle and also surrounded within particular radius of the circle.Any ideas of generating such datasets in python ?
One way of producing a distribution over a circular shape is to sample a one dimensional distribution and then stretch it over the 2 Pi circonference of a circle.
One could then decide to use a uniform or a normal distribution.
import matplotlib.pyplot as plt
import numpy as np
def dist(R=4., width=1., num=1000, uniform=True):
if uniform:
r = np.random.rand(num)*width+R
else:
r = np.random.normal(R, width, num)
phi = np.linspace(0,2.*np.pi, len(r))
x= r * np.sin(phi)
y = r* np.cos(phi)
return x,y
fig, ax = plt.subplots(ncols=2, figsize=(9,4))
ax[0].set_title("uniform")
x,y = dist()
ax[0].plot(x,y, linestyle="", marker="o", markersize="2")
x,y = dist(0,1.2, 400)
ax[0].plot(x,y, linestyle="", marker="o", markersize="2")
ax[1].set_title("normal")
x,y = dist(4,0.4, uniform=False)
ax[1].plot(x,y, linestyle="", marker="o", markersize="2")
x,y = dist(0,0.6, uniform=False)
ax[1].plot(x,y, linestyle="", marker="o", markersize="2")
for a in ax:
a.set_aspect("equal")
plt.show()
You can easily generalize random numbers with some distribution centered on a point, for example normal centered on the 0, 0.
x = np.random.normal(size=1000)
y = np.random.normal(size=1000)
plt.plot(x, y, 'o', alpha=0.6)
EDIT:
What we do is generate random points in polar coordinates. First we do a random for the angle (between 0 and 2 pi) and then we give the noise multiplying it by some random number.
n = 300
theta_out = np.random.uniform(low=0, high=2*np.pi, size=n)
noise_out = np.random.uniform(low=0.9, high=1.1, size=n)
x_out = np.cos(theta_out) * noise_out
y_out = np.sin(theta_out) * noise_out
theta_in = np.random.uniform(low=0, high=2*np.pi, size=n)
noise_in = np.random.uniform(low=0, high=0.5, size=n)
x_in = np.cos(theta_in) * noise_in
y_in = np.sin(theta_in) * noise_in
ax = plt.gca()
ax.set_aspect('equal')
plt.plot(x_out, y_out, 'o')
plt.plot(x_in, y_in, 'o')
Note that there is more density of points while the lower the radius.