Related
How to plot normal vectors in each point of the curve with a given length?
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
plt.rcParams["figure.figsize"] = [8, 8]
x = np.linspace(-1, 1, 100)
y = x**2
ax.set_ylim(-0.3, 1.06)
ax.plot(x, y)
plt.show()
To plot the normals, you need to calculate the slope at each point; from there, you get the tangent vector that you can rotate by pi/2.
here is one approach using python i/o np, which makes it probably easier to understand at first.
Changing the length will adjust the size of the normals to properly scale with your plot.
import matplotlib.pyplot as plt
import numpy as np
import math
def get_normals(length=.1):
for idx in range(len(x)-1):
x0, y0, xa, ya = x[idx], y[idx], x[idx+1], y[idx+1]
dx, dy = xa-x0, ya-y0
norm = math.hypot(dx, dy) * 1/length
dx /= norm
dy /= norm
ax.plot((x0, x0-dy), (y0, y0+dx)) # plot the normals
fig, ax = plt.subplots()
plt.rcParams["figure.figsize"] = [8, 8]
x = np.linspace(-1, 1, 100)
y = x**2
ax.set_ylim(-0.3, 1.06)
ax.plot(x, y)
get_normals()
plt.show()
or longer normals, directed downwards: get_normals(length=-.3)
(use ax.set_aspect('equal') to maintain angles)
import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
plt.rcParams["figure.figsize"] = [8, 8]
x = np.linspace(-1, 1, 100)
y = x**2
# Calculating the gradient
L=.1 # gradient length
grad = np.ones(shape = (2, x.shape[0]))
grad[0, :] = -2*x
grad /= np.linalg.norm(grad, axis=0) # normalizing to unit vector
nx = np.vstack((x - L/2 * grad[0], x + L/2 * grad[0]))
ny = np.vstack((y - L/2 * grad[1], y + L/2 * grad[1]))
# ax.set_ylim(-0.3, 1.06)
ax.plot(x, y)
ax.plot(nx, ny, 'r')
ax.axis('equal')
plt.show()
I have 4 subplots with a different 3D plot with a colorbar.
I want to plot a XY view of my 3D plot, remove the x,y,z axis and resize my plot to use all the space available in the subplot such that the XY view has the same height as the colorbar. I can remove the axis but I do not know how to resize the image. I attached a working code to illustrate this.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.tri as mtri
import matplotlib
import numpy as np
# Create 3D function
n_radii = 8
n_angles = 36
radii = np.linspace(0.125, 1.0, n_radii)
angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)[..., np.newaxis]
x = np.append(0, (radii*np.cos(angles)).flatten())
y = np.append(0, (radii*np.sin(angles)).flatten())
z = np.sin(-x*y)
fig = plt.figure()
for ii in range(1, 4):
#Plot
# ============================================================================
ax = fig.add_subplot(2,2, ii, projection='3d')
cs =ax.plot_trisurf(x, y, z, linewidth=0.2, antialiased=True)
ax.view_init(90, 0)
plt.title(ii)
# ax.axis('off')
plt.grid(b=None)
# Create color bar
# ============================================================================
norm = matplotlib.colors.Normalize(vmin = 0, vmax = 1, clip = False)
m = plt.cm.ScalarMappable(norm=norm)
m.set_array([])
plt.colorbar(m)
plt.tight_layout()
plt.show()
#plt.savefig("test.pdf",bbox_inches='tight')
Any idea how can I do this?
I have added
plt.gca().set_axis_off()
plt.axis([0.6 * x for x in plt.axis()])
to your code which hides the axes and sets the view to 60% of its previous value. The result looks like this:
Full code:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.tri as mtri
import matplotlib
import numpy as np
# Create 3D function
n_radii = 8
n_angles = 36
radii = np.linspace(0.125, 1.0, n_radii)
angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)[..., np.newaxis]
x = np.append(0, (radii*np.cos(angles)).flatten())
y = np.append(0, (radii*np.sin(angles)).flatten())
z = np.sin(-x*y)
fig = plt.figure()
for ii in range(1, 4):
#Plot
# ============================================================================
ax = fig.add_subplot(2,2, ii, projection='3d')
cs =ax.plot_trisurf(x, y, z, linewidth=0.2, antialiased=True)
ax.view_init(90, 0)
plt.title(ii)
# ax.axis('off')
plt.grid(b=None)
# Create color bar
# ============================================================================
norm = matplotlib.colors.Normalize(vmin = 0, vmax = 1, clip = False)
m = plt.cm.ScalarMappable(norm=norm)
m.set_array([])
plt.colorbar(m)
plt.gca().set_axis_off()
plt.axis([0.6 * x for x in plt.axis()])
plt.tight_layout()
plt.show()
#plt.savefig("test.pdf",bbox_inches='tight')
Consider this y(x) function:
where we can generate these scattered points in a file: dataset_1D.dat:
# x y
0 0
1 1
2 0
3 -9
4 -32
The following is a 1D interpolation code for these points:
Load this scattered points
Create a x_mesh
Perform a 1D interpolation
Code:
import numpy as np
from scipy.interpolate import interp2d, interp1d, interpnd
import matplotlib.pyplot as plt
# Load the data:
x, y = np.loadtxt('./dataset_1D.dat', skiprows = 1).T
# Create the function Y_inter for interpolation:
Y_inter = interp1d(x,y)
# Create the x_mesh:
x_mesh = np.linspace(0, 4, num=10)
print x_mesh
# We calculate the y-interpolated of this x_mesh :
Y_interpolated = Y_inter(x_mesh)
print Y_interpolated
# plot:
plt.plot(x_mesh, Y_interpolated, "k+")
plt.plot(x, y, 'ro')
plt.legend(['Linear 1D interpolation', 'data'], loc='lower left', prop={'size':12})
plt.xlim(-0.1, 4.2)
plt.grid()
plt.ylabel('y')
plt.xlabel('x')
plt.show()
This plots the following:
Now, consider this z(x,y) function:
where we can generate these scattered points in a file: dataset_2D.dat :
# x y z
0 0 0
1 1 0
2 2 -4
3 3 -18
4 4 -48
In this case we would have to perform a 2D interpolation:
import numpy as np
from scipy.interpolate import interp1d, interp2d, interpnd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# Load the data:
x, y, z = np.loadtxt('./dataset_2D.dat', skiprows = 1).T
# Create the function Z_inter for interpolation:
Z_inter = interp2d(x, y, z)
# Create the x_mesh and y_mesh :
x_mesh = np.linspace(1.0, 4, num=10)
y_mesh = np.linspace(1.0, 4, num=10)
print x_mesh
print y_mesh
# We calculate the z-interpolated of this x_mesh and y_mesh :
Z_interpolated = Z_inter(x_mesh, y_mesh)
print Z_interpolated
print type(Z_interpolated)
print Z_interpolated.shape
# plot:
fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(x, y, z, c='r', marker='o')
plt.legend(['data'], loc='lower left', prop={'size':12})
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.show()
This plots the following:
where the scattered data is shown again in red dots, to be consistent with the 2D plot.
I do not know how to interpret the Z_interpolated result:
According to the printing lines for the above code,
Z_interpolated is a n-dimensional numpy array, of shape (10,10). In other words, a 2D matrix with 10 rows and 10 columns.
I would have expected an interpolated z[i] value for each value of x_mesh[i] and y_mesh[i] Why I do not receive this ?
How could I plot also in the 3D plot the interpolated data (just like the black crosses in the 2D plot)?
Interpretation of Z_interpolated: your 1-D x_mesh and y_mesh defines a mesh on which to interpolate. Your 2-D interpolation return z is therefore a 2D array with shape (len(y), len(x)) which matches np.meshgrid(x_mesh, y_mesh). As you can see, your z[i, i], instead of z[i], is the expected value for x_mesh[i] and y_mesh[i]. And it just has a lot more, all values on the mesh.
A potential plot to show all interpolated data:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import interp2d
# Your original function
x = y = np.arange(0, 5, 0.1)
xx, yy = np.meshgrid(x, y)
zz = 2 * (xx ** 2) - (xx ** 3) - (yy ** 2)
# Your scattered points
x = y = np.arange(0, 5)
z = [0, 0, -4, -18, -48]
# Your interpolation
Z_inter = interp2d(x, y, z)
x_mesh = y_mesh = np.linspace(1.0, 4, num=10)
Z_interpolated = Z_inter(x_mesh, y_mesh)
fig = plt.figure()
ax = fig.gca(projection='3d')
# Plot your original function
ax.plot_surface(xx, yy, zz, color='b', alpha=0.5)
# Plot your initial scattered points
ax.scatter(x, y, z, color='r', marker='o')
# Plot your interpolation data
X_real_mesh, Y_real_mesh = np.meshgrid(x_mesh, y_mesh)
ax.scatter(X_real_mesh, Y_real_mesh, Z_interpolated, color='g', marker='^')
plt.show()
You would need two steps of interpolation. The first interpolates between y data. And the second interpolates between z data. You then plot the x_mesh with the two interpolated arrays.
x_mesh = np.linspace(0, 4, num=16)
yinterp = np.interp(x_mesh, x, y)
zinterp = np.interp(x_mesh, x, z)
ax.scatter(x_mesh, yinterp, zinterp, c='k', marker='s')
In the complete example below I added some variation in y direction as well to make the solution more general.
u = u"""# x y z
0 0 0
1 3 0
2 9 -4
3 16 -18
4 32 -48"""
import io
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# Load the data:
x, y, z = np.loadtxt(io.StringIO(u), skiprows = 1, unpack=True)
x_mesh = np.linspace(0, 4, num=16)
yinterp = np.interp(x_mesh, x, y)
zinterp = np.interp(x_mesh, x, z)
fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(x_mesh, yinterp, zinterp, c='k', marker='s')
ax.scatter(x, y, z, c='r', marker='o')
plt.legend(['data'], loc='lower left', prop={'size':12})
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.show()
For using scipy.interpolate.interp1d the solution is essentially the same:
u = u"""# x y z
0 0 0
1 3 0
2 9 -4
3 16 -18
4 32 -48"""
import io
import numpy as np
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# Load the data:
x, y, z = np.loadtxt(io.StringIO(u), skiprows = 1, unpack=True)
x_mesh = np.linspace(0, 4, num=16)
fy = interp1d(x, y, kind='cubic')
fz = interp1d(x, z, kind='cubic')
fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(x_mesh, fy(x_mesh), fz(x_mesh), c='k', marker='s')
ax.scatter(x, y, z, c='r', marker='o')
plt.legend(['data'], loc='lower left', prop={'size':12})
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.show()
I am trying to plot the comun distribution of two normal distributed variables.
The code below plots one normal distributed variable. What would the code be for plotting two normal distributed variables?
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.mlab as mlab
import math
mu = 0
variance = 1
sigma = math.sqrt(variance)
x = np.linspace(-3, 3, 100)
plt.plot(x,mlab.normpdf(x, mu, sigma))
plt.show()
It sounds like what you're looking for is a Multivariate Normal Distribution. This is implemented in scipy as scipy.stats.multivariate_normal. It's important to remember that you are passing a covariance matrix to the function. So to keep things simple keep the off diagonal elements as zero:
[X variance , 0 ]
[ 0 ,Y Variance]
Here is an example using this function and generating a 3D plot of the resulting distribution. I add the colormap to make seeing the curves easier but feel free to remove it.
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import multivariate_normal
from mpl_toolkits.mplot3d import Axes3D
#Parameters to set
mu_x = 0
variance_x = 3
mu_y = 0
variance_y = 15
#Create grid and multivariate normal
x = np.linspace(-10,10,500)
y = np.linspace(-10,10,500)
X, Y = np.meshgrid(x,y)
pos = np.empty(X.shape + (2,))
pos[:, :, 0] = X; pos[:, :, 1] = Y
rv = multivariate_normal([mu_x, mu_y], [[variance_x, 0], [0, variance_y]])
#Make a 3D plot
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(X, Y, rv.pdf(pos),cmap='viridis',linewidth=0)
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')
plt.show()
Giving you this plot:
Edit the method used below was deprecated in Matplotlib v2.2 and removed in v3.1
A simpler version is available through matplotlib.mlab.bivariate_normal
It takes the following arguments so you don't need to worry about matrices
matplotlib.mlab.bivariate_normal(X, Y, sigmax=1.0, sigmay=1.0, mux=0.0, muy=0.0, sigmaxy=0.0)
Here X, and Y are again the result of a meshgrid so using this to recreate the above plot:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.mlab import bivariate_normal
from mpl_toolkits.mplot3d import Axes3D
#Parameters to set
mu_x = 0
sigma_x = np.sqrt(3)
mu_y = 0
sigma_y = np.sqrt(15)
#Create grid and multivariate normal
x = np.linspace(-10,10,500)
y = np.linspace(-10,10,500)
X, Y = np.meshgrid(x,y)
Z = bivariate_normal(X,Y,sigma_x,sigma_y,mu_x,mu_y)
#Make a 3D plot
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(X, Y, Z,cmap='viridis',linewidth=0)
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')
plt.show()
Giving:
The following adaption to #Ianhi's code above returns a contour plot version of the 3D plot above.
import matplotlib.pyplot as plt
from matplotlib import style
style.use('fivethirtyeight')
import numpy as np
from scipy.stats import multivariate_normal
#Parameters to set
mu_x = 0
variance_x = 3
mu_y = 0
variance_y = 15
x = np.linspace(-10,10,500)
y = np.linspace(-10,10,500)
X,Y = np.meshgrid(x,y)
pos = np.array([X.flatten(),Y.flatten()]).T
rv = multivariate_normal([mu_x, mu_y], [[variance_x, 0], [0, variance_y]])
fig = plt.figure(figsize=(10,10))
ax0 = fig.add_subplot(111)
ax0.contour(X, Y, rv.pdf(pos).reshape(500,500))
plt.show()
While the other answers are great, I wanted to achieve similar results while also illustrating the distribution with a scatter plot of the sample.
More details can be found here: Python 3d plot of multivariate gaussian distribution
The results looks like:
And is generated using the following code:
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from scipy.stats import multivariate_normal
# Sample parameters
mu = np.array([0, 0])
sigma = np.array([[0.7, 0.2], [0.2, 0.3]])
rv = multivariate_normal(mu, sigma)
sample = rv.rvs(500)
# Bounds parameters
x_abs = 2.5
y_abs = 2.5
x_grid, y_grid = np.mgrid[-x_abs:x_abs:.02, -y_abs:y_abs:.02]
pos = np.empty(x_grid.shape + (2,))
pos[:, :, 0] = x_grid
pos[:, :, 1] = y_grid
levels = np.linspace(0, 1, 40)
fig = plt.figure()
ax = fig.gca(projection='3d')
# Removes the grey panes in 3d plots
ax.xaxis.set_pane_color((1.0, 1.0, 1.0, 0.0))
ax.yaxis.set_pane_color((1.0, 1.0, 1.0, 0.0))
ax.zaxis.set_pane_color((1.0, 1.0, 1.0, 0.0))
# The heatmap
ax.contourf(x_grid, y_grid, 0.1 * rv.pdf(pos),
zdir='z', levels=0.1 * levels, alpha=0.9)
# The wireframe
ax.plot_wireframe(x_grid, y_grid, rv.pdf(
pos), rstride=10, cstride=10, color='k')
# The scatter. Note that the altitude is defined based on the pdf of the
# random variable
ax.scatter(sample[:, 0], sample[:, 1], 1.05 * rv.pdf(sample), c='k')
ax.legend()
ax.set_title("Gaussian sample and pdf")
ax.set_xlim3d(-x_abs, x_abs)
ax.set_ylim3d(-y_abs, y_abs)
ax.set_zlim3d(0, 1)
plt.show()
I am trying to color each individual face of a cylinder, however I am not sure how to go about it, I have tried the following:
for i in range(10):
col.append([])
for i in range(10):
for j in range(20):
col[i].append(plt.cm.Blues(0.4))
ax.plot_surface(X, Y, Z,facecolors = col,edgecolor = "red")
I want each face to be assigned its own color, so I would think I would supply an array of colors for each of the faces in a 2d array.
But this gives an error:
in plot_surface
colset.append(fcolors[rs][cs])
IndexError: list index out of range
Here is the full code:
import numpy as np
from matplotlib import cm
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.linalg import norm
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
origin = np.array([0, 0, 0])
#axis and radius
p0 = np.array([1, 3, 2])
p1 = np.array([8, 5, 9])
R = 5
#vector in direction of axis
v = p1 - p0
#find magnitude of vector
mag = norm(v)
#unit vector in direction of axis
v = v / mag
#make some vector not in the same direction as v
not_v = np.array([1, 0, 0])
if (v == not_v).all():
not_v = np.array([0, 1, 0])
#make vector perpendicular to v
n1 = np.cross(v, not_v)
#normalize n1
n1 /= norm(n1)
#make unit vector perpendicular to v and n1
n2 = np.cross(v, n1)
#surface ranges over t from 0 to length of axis and 0 to 2*pi
t = np.linspace(0, mag, 200)
theta = np.linspace(0, 2 * np.pi, 100)
#use meshgrid to make 2d arrays
t, theta = np.meshgrid(t, theta)
#generate coordinates for surface
X, Y, Z = [p0[i] + v[i] * t + R * np.sin(theta) * n1[i] + R * np.cos(theta) * n2[i] for i in [0, 1, 2]]
col = []
for i in range(10):
col.append([])
for i in range(10):
for j in range(20):
col[i].append(plt.cm.Blues(0.4))
ax.plot_surface(X, Y, Z,facecolors = col,edgecolor = "red")
#plot axis
ax.plot(*zip(p0, p1), color = 'red')
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
ax.set_zlim(0, 10)
plt.axis('off')
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
plt.show()
Your Z array is of size 100x200, yet you are only specifying 10x20 colors. A quicker way to make col (with the right dimensions) might be something like:
col1 = plt.cm.Blues(np.linspace(0,1,200)) # linear gradient along the t-axis
col1 = np.repeat(col1[np.newaxis,:, :], 100, axis=0) # expand over the theta-axis
col2 = plt.cm.Blues(np.linspace(0,1,100)) # linear gradient along the theta-axis
col2 = np.repeat(col2[:, np.newaxis, :], 200, axis=1) # expand over the t-axis
ax=plt.subplot(121, projection='3d')
ax.plot_surface(X, Y, Z, facecolors=col1)
ax=plt.subplot(122, projection='3d')
ax.plot_surface(X, Y, Z, facecolors=col2)
Which produces: