Plotting points from a function on a unit sphere using matplotlib - python

I am trying to plot the following function on a unit sphere, the points should be on the sphere and fill up the whole sphere however some of the points are falling off. Any suggestions why? I believe it is because the sphere is not spanning 1,1,1 3D grid but I am not sure how to edit my code to fix this.
from itertools import product, combinations
import matplotlib
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
def d(kx,ky):
M = 1
B = 1
vf = 1
kxx,kyy = np.meshgrid(kx,ky)
x = (vf*kxx)/(np.sqrt(((((vf**2)*(kxx**2)))+((vf**2)*(kyy**2))+(M-B*(kxx**2+(kyy**2)))**2)))
y = (vf*kxx)/(np.sqrt(((((vf**2)*(kxx**2)))+((vf**2)*(kyy**2))+(M-B*(kxx**2+(kyy**2)))**2)))
z = (M-B*(kxx**2+(kyy**2)))/(np.sqrt(((((vf**2)*(kxx**2)))+((vf**2)*(kyy**2))+(M-B*(kxx**2+(kyy**2)))**2)))
return x,y,z
kx = np.linspace(-2, 2, 10)
ky = np.linspace(-2, 2, 10)
xi, yi, zi = d(kx,ky)
phi = np.linspace(0, np.pi, 100)
theta = np.linspace(0, 2*np.pi, 100)
phi, theta = np.meshgrid(phi, theta)
x = np.sin(phi) * np.cos(theta)
y = np.sin(phi) * np.sin(theta)
z = np.cos(phi)
fig = plt.figure(figsize=plt.figaspect(1.))
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(x, y, z, color="w", rstride=1, cstride=1)
ax.scatter(xi,yi,zi,color="k",s=20)
plt.show()
Thank you kindly,

Related

Colormap a 3D curve in matplotlib

I have 4 arrays x, y, z and T of length n and I want to plot a 3D curve using matplotlib. The (x, y, z) are the points positions and T is the value of each point (which is plotted as color), like the temperature of each point. How can I do it?
Example code:
import numpy as np
from matplotlib import pyplot as plt
fig = plt.figure()
ax = fig.gca(projection='3d')
n = 100
cmap = plt.get_cmap("bwr")
theta = np.linspace(-4 * np.pi, 4 * np.pi, n)
z = np.linspace(-2, 2, n)
r = z**2 + 1
x = r * np.sin(theta)
y = r * np.cos(theta)
T = (2*np.random.rand(n) - 1) # All the values are in [-1, 1]
What I found over the internet:
It's possible to use cmap with scatter like shown in the docs and in this stackoverflow question
ax = plt.gca()
ax.scatter(x, y, z, cmap=cmap, c=T)
The problem is that scatter is a set of points, not a curve.
In this stackoverflow question the solution was divide in n-1 intervals and each interval we use a different color like
t = (T - np.min(T))/(np.max(T)-np.min(T)) # Normalize
for i in range(n-1):
plt.plot(x[i:i+2], y[i:i+2], z[i:i+2], c=cmap(t[i])
The problem is that each segment has only one color, but it should be an gradient. The last value is not even used.
Useful links:
Matplotlib - Colormaps
Matplotlib - Tutorial 3D
This is a case where you probably need to use Line3DCollection. This is the recipe:
create segments from your array of coordinates.
create a Line3DCollection object.
add that collection to the axis.
set the axis limits.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Line3DCollection
from matplotlib.cm import ScalarMappable
from matplotlib.colors import Normalize
def get_segments(x, y, z):
"""Convert lists of coordinates to a list of segments to be used
with Matplotlib's Line3DCollection.
"""
points = np.ma.array((x, y, z)).T.reshape(-1, 1, 3)
return np.ma.concatenate([points[:-1], points[1:]], axis=1)
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
n = 100
cmap = plt.get_cmap("bwr")
theta = np.linspace(-4 * np.pi, 4 * np.pi, n)
z = np.linspace(-2, 2, n)
r = z**2 + 1
x = r * np.sin(theta)
y = r * np.cos(theta)
T = np.cos(theta)
segments = get_segments(x, y, z)
c = Line3DCollection(segments, cmap=cmap, array=T)
ax.add_collection(c)
fig.colorbar(c)
ax.set_xlim(x.min(), x.max())
ax.set_ylim(y.min(), y.max())
ax.set_zlim(z.min(), z.max())
plt.show()

How to plot a spherical segment in Python?

How can one plot a spherical segment, specifically a sphere "slice" in Python?
I know how to plot a sphere surface via
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
x = 10 * np.outer(np.cos(u), np.sin(v))
y = 10 * np.outer(np.sin(u), np.sin(v))
z = 10 * np.outer(np.ones(np.size(u)), np.cos(v))
ax.plot_surface(x, y, z, rstride=4, cstride=4, color='b')
plt.show()
or some variation of that code, but I'm struggeling to plot only a part of the sphere, leading to an image like the following:
https://en.wikipedia.org/wiki/Spherical_segment#/media/File:LaoHaiKugelschicht1.png
If I vary the code I presented above by manipulating the definitions of u and v, e.g.:
u = np.linspace(0, 2 * np.pi, 20)
v = np.linspace(0, np.pi, 20)
the sphere is still presented as a whole, but with a very poor resolution.
Changing the starting point of the linspace range doens't seem to change anything.
I have figured out something that works for me. Here you go:
q = 0.5 # defines upper starting point of the spherical segment
p = 0.8 # defines ending point of the spherical segment as ratio
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(q, p * np.pi, p * 100)
x = r * np.outer(np.cos(u), np.sin(v)) + x_0
y = r * np.outer(np.sin(u), np.sin(v)) + y_0
z = r * np.outer(np.ones(np.size(u)), np.cos(v)) + z_0
ax.plot_surface(x, y, z, color='b')

Spline interpolation over 3 variables for scattered data in Python?

With other words I got a set of data-points (x,y,z) associated to a value b and I would like to interpolate this data as accurate as possible.
Scipy.interpolate.griddata only can do a linear interpolation, what are the other options?
How about interpolating x, y, z separatly? I modified this tutorial example and added interpolation to it:
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import InterpolatedUnivariateSpline
mpl.rcParams['legend.fontsize'] = 10
# let's take only 20 points for original data:
n = 20
fig = plt.figure()
ax = fig.gca(projection='3d')
theta = np.linspace(-4 * np.pi, 4 * np.pi, n)
z = np.linspace(-2, 2, n)
r = z**2 + 1
x = r * np.sin(theta)
y = r * np.cos(theta)
ax.plot(x, y, z, label='rough curve')
# this variable represents distance along the curve:
t = np.arange(n)
# now let's refine it to 100 points:
t2 = np.linspace(t.min(), t.max(), 100)
# interpolate vector components separately:
x2 = InterpolatedUnivariateSpline(t, x)(t2)
y2 = InterpolatedUnivariateSpline(t, y)(t2)
z2 = InterpolatedUnivariateSpline(t, z)(t2)
ax.plot(x2, y2, z2, label='interpolated curve')
ax.legend()
plt.show()
The result looks like this:
UPDATE
Didn't understand the question at the first time, sorry.
You are probably looking for tricubic interpolation. Try this.

Spherical coordinates plot in matplotlib

R(teta, phi) = cos(phi^2), teta[0, 2*pi], phi[0,pi]
How to draw a graph of this function (R(teta, phi)) in spherical coordinates with the help of matplotlib?
The documentation I have not found Spherical coordinates.
The code below is very much like the 3D polar plot from the Matplotlib gallery. The only difference is that you use np.meshgrid to make 2D arrays for PHI and THETA instead of R and THETA (or what the 3D polar plot example calls P).
The moral of the story is that as long as X, Y, and Z can be expressed as (smooth) functions of two parameters, plot_surface can plot it.
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as axes3d
theta, phi = np.linspace(0, 2 * np.pi, 40), np.linspace(0, np.pi, 40)
THETA, PHI = np.meshgrid(theta, phi)
R = np.cos(PHI**2)
X = R * np.sin(PHI) * np.cos(THETA)
Y = R * np.sin(PHI) * np.sin(THETA)
Z = R * np.cos(PHI)
fig = plt.figure()
ax = fig.add_subplot(1,1,1, projection='3d')
plot = ax.plot_surface(
X, Y, Z, rstride=1, cstride=1, cmap=plt.get_cmap('jet'),
linewidth=0, antialiased=False, alpha=0.5)
plt.show()
yields
Typically R, the radius, should be positive, so you might want
R = np.abs(np.cos(PHI**2))
In that case,
import matplotlib.colors as mcolors
cmap = plt.get_cmap('jet')
norm = mcolors.Normalize(vmin=Z.min(), vmax=Z.max())
plot = ax.plot_surface(
X, Y, Z, rstride=1, cstride=1,
facecolors=cmap(norm(Z)),
linewidth=0, antialiased=False, alpha=0.5)
yields
Who knew R = np.abs(np.cos(PHI**2)) is a little girl in a dress? :)
If you want a lot of control you can use Poly3Dcollection directly and roll your own (allows you to have portions of the surface, that you don't plot.
Note that I changed the variables to the more common definition of phi in the azimuth and theta for the z-direction.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import numpy as np
from __future__ import division
fig = plt.figure()
ax = fig.gca(projection='3d')
nphi,nth=48,12
phi = np.linspace(0,360, nphi)/180.0*np.pi
th = np.linspace(-90,90, nth)/180.0*np.pi
verts2 = []
for i in range(len(phi)-1):
for j in range(len(th)-1):
r= np.cos(phi[i])**2 # <----- your function is here
r1= np.cos(phi[i+1])**2
cp0= r*np.cos(phi[i])
cp1= r1*np.cos(phi[i+1])
sp0= r*np.sin(phi[i])
sp1= r1*np.sin(phi[i+1])
ct0= np.cos(th[j])
ct1= np.cos(th[j+1])
st0= np.sin(th[j])
st1= np.sin(th[j+1])
verts=[]
verts.append((cp0*ct0, sp0*ct0, st0))
verts.append((cp1*ct0, sp1*ct0, st0))
verts.append((cp1*ct1, sp1*ct1, st1))
verts.append((cp0*ct1, sp0*ct1, st1))
verts2.append(verts )
poly3= Poly3DCollection(verts2, facecolor='g')
poly3.set_alpha(0.2)
ax.add_collection3d(poly3)
ax.set_xlabel('X')
ax.set_xlim3d(-1, 1)
ax.set_ylabel('Y')
ax.set_ylim3d(-1, 1)
ax.set_zlabel('Z')
ax.set_zlim3d(-1, 1)
plt.show()

Plotting Ellipsoid with Matplotlib

Does anyone have sample code for plotting ellipsoids? There is one for sphere on matplotlib site, but nothing for ellipsoids. I am trying to plot
x**2 + 2*y**2 + 2*z**2 = c
where c is a constant (like 10) that defines an ellipsoid. I tried the meshgrid(x,y) route, reworked the equation so z is on one side, but the sqrt is a problem. The matplotlib sphere example works with angles, u,v, but I am not sure how to work that for ellipsoid.
Here is how you can do it via spherical coordinates:
# from mpl_toolkits.mplot3d import Axes3D # Not needed with Matplotlib 3.6.3
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(figsize=plt.figaspect(1)) # Square figure
ax = fig.add_subplot(111, projection='3d')
coefs = (1, 2, 2) # Coefficients in a0/c x**2 + a1/c y**2 + a2/c z**2 = 1
# Radii corresponding to the coefficients:
rx, ry, rz = 1/np.sqrt(coefs)
# Set of all spherical angles:
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
# Cartesian coordinates that correspond to the spherical angles:
# (this is the equation of an ellipsoid):
x = rx * np.outer(np.cos(u), np.sin(v))
y = ry * np.outer(np.sin(u), np.sin(v))
z = rz * np.outer(np.ones_like(u), np.cos(v))
# Plot:
ax.plot_surface(x, y, z, rstride=4, cstride=4, color='b')
# Adjustment of the axes, so that they all have the same span:
max_radius = max(rx, ry, rz)
for axis in 'xyz':
getattr(ax, 'set_{}lim'.format(axis))((-max_radius, max_radius))
plt.show()
The resulting plot is similar to
The program above actually produces a nicer looking "square" graphics.
This solution is strongly inspired from the example in Matplotlib's gallery.
Building on EOL's answer. Sometimes you have an ellipsoid in matrix format:
A and c Where A is the ellipsoid matrix and c is a vector representing the centre of the ellipsoid.
import numpy as np
import numpy.linalg as linalg
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# your ellispsoid and center in matrix form
A = np.array([[1,0,0],[0,2,0],[0,0,2]])
center = [0,0,0]
# find the rotation matrix and radii of the axes
U, s, rotation = linalg.svd(A)
radii = 1.0/np.sqrt(s)
# now carry on with EOL's answer
u = np.linspace(0.0, 2.0 * np.pi, 100)
v = np.linspace(0.0, np.pi, 100)
x = radii[0] * np.outer(np.cos(u), np.sin(v))
y = radii[1] * np.outer(np.sin(u), np.sin(v))
z = radii[2] * np.outer(np.ones_like(u), np.cos(v))
for i in range(len(x)):
for j in range(len(x)):
[x[i,j],y[i,j],z[i,j]] = np.dot([x[i,j],y[i,j],z[i,j]], rotation) + center
# plot
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_wireframe(x, y, z, rstride=4, cstride=4, color='b', alpha=0.2)
plt.show()
plt.close(fig)
del fig
So, not too much new here, but helpful if you've got an ellipsoid in matrix form which is rotated and perhaps not centered at 0,0,0 and want to plot it.
If you have an ellipsoid specified by an arbitrary covariance matrix cov and offset bias, you do not need to figure out the intuitive parameters of the ellipsoid to get the shape. Specifically, you don't need the individual axes or rotations. The whole point of the matrix is that it transforms a unit sphere (represented by the identity matrix) into your ellipse.
Starting with
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
Make a unit sphere
x = np.outer(np.cos(u), np.sin(v))
y = np.outer(np.sin(u), np.sin(v))
z = np.outer(np.ones_like(u), np.cos(v))
Now transform the sphere:
ellipsoid = (cov # np.stack((x, y, z), 0).reshape(3, -1) + bias).reshape(3, *x.shape)
You can plot the result pretty much as before:
ax.plot_surface(*ellipsoid, rstride=4, cstride=4, color='b', alpha=0.75)

Categories