I have a data set which maps a tuple of phi and theta to
a value which represents the strength of the signal.
I want to plot these on a sphere. I simply followed
a demo from matplotlib and adjusted the code to my
use case.
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
u = phi
v = theta
vals =vals/vals.max()
Map = cm.coolwarm
facecolors = Map(vals[:])
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=1, cstride=1, cmap=cm.coolwarm,
linewidth=0, antialiased=False, facecolors=facecolors)
plt.show()
This generates an error message IndexError: index 4 is out of bounds for axis 0 with size 4. I also looked into the source code, which seems
to indicate to me that facecolors isn't formatted correctly, but I'm
struggling to figure out, what formatting is needed exactly.
Any help or other ways to achieve this goal would be greatly
appreciated.
Greetings
If your question is: "How to get rid of this IndexError?", I modified your code and now it works. plot_surface takes X,Y,Z and facecolors as 2D arrays of corresponding values on a 2D grid. Facecolors in your case weren't and this was the source of your error.
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm, colors
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
u, v = np.mgrid[0:np.pi:50j, 0:2*np.pi:50j]
strength = u
norm=colors.Normalize(vmin = np.min(strength),
vmax = np.max(strength), clip = False)
x = 10 * np.sin(u) * np.cos(v)
y = 10 * np.sin(u) * np.sin(v)
z = 10 * np.cos(u)
ax.plot_surface(x, y, z, rstride=1, cstride=1, cmap=cm.coolwarm,
linewidth=0, antialiased=False,
facecolors=cm.coolwarm(norm(strength)))
plt.show()
Result is this image of a sphere.
However, if your data is not on a 2D grid you are in trouble. Additionally if your grid is not regular the sphere you plot will look irregular as well. So if your question is: "How to plot a heatmap on a sphere?", there is already such a question and solution here using Basemap package produces this result:
Related
I have computed a lot (~5000) of 3d points (x,y,z) in a quite complicated way so I have no function such that z = f(x,y). I can plot the 3d surface using
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
X = surface_points[:,0]
Y = surface_points[:,1]
Z = surface_points[:,2]
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
surf = ax.plot_trisurf(X, Y, Z, cmap=cm.coolwarm, vmin=np.nanmin(Z), vmax=np.nanmax(Z))
I would like to plot this also in 2d, with a colorbar indicating the z-value. I know there is a simple solution using ax.contour if my z is a matrix, but here I only have a vector.
Attaching the plot_trisurf result when rotated to xy-plane. This is what I what like to achieve without having to rotate a 3d plot. In this, my variable surface_points is an np.array with size 5024 x 3.
I had the same problems in one of my codes, I solved it this way:
import numpy as np
from scipy.interpolate import griddata
import matplotlib.pylab as plt
from matplotlib import cm
N = 10000
surface_points = np.random.rand(N,3)
X = surface_points[:,0]
Y = surface_points[:,1]
Z = surface_points[:,2]
nx = 10*int(np.sqrt(N))
xg = np.linspace(X.min(), X.max(), nx)
yg = np.linspace(Y.min(), Y.max(), nx)
xgrid, ygrid = np.meshgrid(xg, yg)
ctr_f = griddata((X, Y), Z, (xgrid, ygrid), method='linear')
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.contourf(xgrid, ygrid, ctr_f, cmap=cm.coolwarm)
plt.show()
You could use a scatter plot to display a projection of your z color onto the x-y axis.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
N = 10000
surface_points = np.random.rand(N,3)
X = surface_points[:,0]
Y = surface_points[:,1]
Z = surface_points[:,2]
# fig = plt.figure()
# ax = fig.add_subplot(projection='3d')
# surf = ax.plot_trisurf(X, Y, Z, cmap=cm.coolwarm, vmin=np.nanmin(Z), vmax=np.nanmax(Z))
fig = plt.figure()
cmap = cm.get_cmap('coolwarm')
color = cmap(Z)[..., :3]
plt.scatter(X,Y,c=color)
plt.show()
Since you seem to have a 3D shape that is hollow, you could split the projection into two like if you cur the shape in two pieces.
fig = plt.figure()
plt.subplot(121)
plt.scatter(X[Z<0.5],Y[Z<0.5],c=color[Z<0.5])
plt.title('down part')
plt.subplot(122)
plt.scatter(X[Z>=0.5],Y[Z>=0.5],c=color[Z>+0.5])
plt.title('top part')
plt.show()
I created 3d bar graph with matplotlib with the following code:
fig = plt.figure()
cmap = get_cmap(len(os.listdir(conv1d_kernel_path)))
ax = fig.add_subplot(111, projection='3d')
for f in os.listdir(conv1d_kernel_path):
step = int(re.findall(r'\d+', f)[0])
conv1d_kernel_histo[f]['bins'] = convert_bins(30, min_weight, max_weight, conv1d_kernel_histo[f])
bin_counts = conv1d_kernel_histo[f]['bins'][:, 2]
width = (max_weight-min_weight)/30 #ToDo change 30 to numbins
xs = conv1d_kernel_histo[f]['bins'][:, 0] + width / 2
ax.bar(xs, list(bin_counts), width=width, zs=step, zdir='y', color=cmap(step), ec=cmap(step+20), alpha=0.8)
ax.set_xlabel('weights')
ax.set_ylabel('step')
ax.set_zlabel('count')
plt.show()
The directory and convert bins function isn't so important, just that it gives me the info I can use to define the input data that I iteratively pass to the ax.bar function. Anyways, I receive the following output:
I want to invert the axis titled 'steps', and this question seems to be nearly what I need; however, when I used ax.invert_yaxis() the axis titled 'weights' is inverted. When I alternatively use ax.invert_xaxis() the same axis is inverted. Out of curiosity, I tried ax.invert_zaxis() but that worked as one would think, and flipped the whole graph upside down. Does anybody have a solution to this problem? A different method for inverting the axis? Thanks, all help is appreciated
Well inverting the axis limits like ax.set_ylim(150,0) should work fine. Sample plot:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
# Make data.
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
# Plot the surface.
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
# Invert Y-Axis
ax.set_ylim(4,-4)
# Customize the z axis.
ax.set_zlim(-1.01, 1.01)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
# Add a color bar which maps values to colors.
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
Original:
Inverted Y-Axis:
I use matplotlib to simulate Y^2 + Z^2 = (SinX)^2
That is,the sine graph rotate 360 degrees based on x axis.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
# Make data.
t = np.arange(-5, 5, 0.25)
X,Y = np.meshgrid(t,t)
Z = np.sin(t)**2
# Plot the surface.
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
# Customize the z axis.
ax.set_zlim(-1.01, 1.01)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
# Add a color bar which maps values to colors.
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
Following is the image
However,that seems not fit my expectation.
Is my way reasonable?
Or is there any way can implement in vpython?
Here is a VPython program that plots a function in 3D, which may be related to what you want to do.
http://www.glowscript.org/#/user/GlowScriptDemos/folder/Examples/program/Plot3D
Currently when I animate a surface in matplotlib, I generate snapshots manually and stitch together with ImageMagick. This is similar to the standard matplotlib animation in that it does not transition between the two frames.
Can I ease (in D3js terminology, and I'm sure of industry terminology more broadly - linear/cubic easing) during the transition? Or, is there a function in numpy to interpolate between two frames (the two surfaces) and end with a transition?
A simple example would be transitioning from the matplotlib example to any modification of the surface.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
# Make data.
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
# Plot the surface.
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
# Customize the z axis.
ax.set_zlim(-1.01, 1.01)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
# Add a color bar which maps values to colors.
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
to
Z = np.sin(2*R)
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
You could evaluate np.sin(a * R) for a range of a values using broadcasting:
n = 10 # or however many intermediate arrays you want
a = np.linspace(1, 2, n)
interp_z = np.sin(a[:, None, None] * R[None]) # an (n, 40, 40) array
Now you can plot each of the intermediate arrays, save it as an image, then stitch the images together however you like:
for i, Z in enumerate(interp_z):
ax.plot_surface(X, Y, Z, ...)
fig.savefig('image_{}.png'.format(i))
I've had a look at matplotlib's examples of 3d plots, but none of these give me what I want to plot, something like:
The plot shows a series of measurements on the y-axis (N) and each measurement has an intensity spectrum (p/2hk_L), i.e. N is fixed for each line you see in the graph. What is the easiest function to use to plot data like this?
Here is a try:
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = np.linspace(-50,50,100)
y = np.arange(25)
X,Y = np.meshgrid(x,y)
Z = np.zeros((len(y),len(x)))
for i in range(len(y)):
damp = (i/float(len(y)))**2
Z[i] = 5*damp*(1 - np.sqrt(np.abs(x/50)))
Z[i] += np.random.uniform(0,.1,len(Z[i]))
ax.plot_surface(X, Y, Z, rstride=1, cstride=1000, color='w', shade=False, lw=.5)
ax.set_zlim(0, 5)
ax.set_xlim(-51, 51)
ax.set_zlabel("Intensity")
ax.view_init(20,-120)
plt.show()