not all scatter points are shown on plot_surface - python

I am trying to visualize points on 3d surface.
For some reason only some points are shown in the plot.
When I start to move the plot around, some points suddenly appear. Is there a way to visualize points on a 3d surface?
This is the result I am getting right now:
And below the code used to generate it:
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.1)
Y = np.arange(-5, 5, 0.1)
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)
X = np.arange(-5, 5, 1)
Y = np.arange(-5, 5, 1)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X ** 2 + Y ** 2)
Z = np.sin(R)
ax.scatter(X, Y, Z, c='r', marker='o')
# 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()

Related

3D plot in matplotlib using equation with x and y with Python

I'd like to create a 3D plot from an equation with x and y, similar to Google's 3D graph.
An example:
input: sin(sqrt(x**2 + y**2))
output (3D plot):
The Z will obviously be equal to the given input, but how will x and y be calculated? Thanks for any help given!
You can start by creating a meshgrid for your X and Y. Then compute your Z by doing Z=np.sin(np.sqrt(X**2 + Y**2)). Finally, you can plot the surface by using the matplotlib function ax.plot_surface(X, Y, Z).
You can find the code below:
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
N_points=100
x = np.linspace(-10, 10, N_points)
y = np.linspace(-10, 10, N_points)
X, Y = np.meshgrid(x, y)
Z=np.sin(np.sqrt(X**2 + Y**2))
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
ax.plot_surface(X, Y, Z)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()
And the output of this code gives:

Python 3D surface plot by only having X, Y, Z coordinates

Is there anyway to plot surface in Python by only having X, Y, Z coordinates?
I have column vectors of X, Y, Z values where Z=F(X,Y) is already calculated ( I only have the data not the function )
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(figsize = (12,10))
ax = plt.axes(projection='3d')
x = np.arange(-5, 5.1, 0.2)
y = np.arange(-5, 5.1, 0.2)
X, Y = np.meshgrid(x, y)
Z = np.sin(X)*np.cos(Y)
surf = ax.plot_surface(X, Y, Z, cmap = plt.cm.cividis)
# Set axes label
ax.set_xlabel('x', labelpad=20)
ax.set_ylabel('y', labelpad=20)
ax.set_zlabel('z', labelpad=20)
fig.colorbar(surf, shrink=0.5, aspect=8)
plt.show()
Examples like above in the manual consider that you can calculate Z = np.sin(X)*np.cos(Y)
But I cannot do that since I don't have the function.
Is there anyway to plot surface with only having X, Y, Z values in Python?

"Easing" Transitions in Matplotlib

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))

Python legend in 3dplot

I am plotting a 3d plot in python 2.7
When I try to plot a 3d plot with color and marker as in 2D plot() function. I come across an error.
So I tried to plot line separately and measured points with markers separately using scatter() function.
When I create legend entries my legend looks like this
But I don't want to have duplicate legend entries instead
I want my legend entries to group with colour, or
Is it possible have both marker and line as a single entry so that there are only 5 entries in my legend
I found a similar question to this (How to make custom legend in matplotlib) but it does not solve my problem
I am appending a code similar to my problem
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
mpl.rcParams['legend.fontsize'] = 10
fig = plt.figure()
ax = fig.gca(projection='3d')
theta = np.linspace(-4 * np.pi, 4 * np.pi, 100)
z = np.linspace(-2, 2, 100)
r = z**2 + 1
x = r * np.sin(theta)
y = r * np.cos(theta)
ax.plot(x, y, z, label='parametric curve 1')
ax.scatter(x, y, z, label='parametric curve 1',marker = 'o')
x = r * np.sin(theta + 1)
y = r * np.cos(theta + 1)
ax.plot(x, y, z, label='parametric curve 2')
ax.scatter(x, y, z, label='parametric curve 2',marker = 'o')
ax.legend()
plt.show()
The above code gives me a plot shown below
Plot
But I want my legend to have only two entries
Are you using the standard Matplotlib library to generate these 3D plots? If so, starting from the example in the documentation (http://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html#line-plots) it seems to work fine:
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
mpl.rcParams['legend.fontsize'] = 10
fig = plt.figure()
ax = fig.gca(projection='3d')
theta = np.linspace(-4 * np.pi, 4 * np.pi, 100)
z = np.linspace(-2, 2, 100)
r = z**2 + 1
x = r * np.sin(theta)
y = r * np.cos(theta)
ax.plot(x, y, z, label='parametric curve 1', marker='o')
x = r * np.sin(theta + 1)
y = r * np.cos(theta + 1)
ax.plot(x, y, z, label='parametric curve 2', marker='o')
ax.legend()
plt.show()

Adding legend to a surface plot

I am trying to add legend to a surface plot but unable to do so. Here is the code.
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import random
def fun(x, y):
return 0.063*x**2 + 0.0628*x*y - 0.15015876*x + 96.1659*y**2 - 74.05284306*y + 14.319143466051
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = y = np.arange(-1.0, 1.0, 0.05)
X, Y = np.meshgrid(x, y)
zs = np.array([fun(x,y) for x,y in zip(np.ravel(X), np.ravel(Y))])
Z = zs.reshape(X.shape)
ax.plot_surface(X, Y, Z)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.plot(color='red',label='Lyapunov function on XY plane',linewidth=4) # Adding legend
plt.show()
Kindly help. Thanks in advance.
It is not trivial to make a legend in a 3D axis. You can use the following hack:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib as mpl
import random
def fun(x, y):
return 0.063*x**2 + 0.0628*x*y - 0.15015876*x + 96.1659*y**2 - 74.05284306*y + 14.319143466051
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = y = np.arange(-1.0, 1.0, 0.05)
X, Y = np.meshgrid(x, y)
zs = np.array([fun(x,y) for x,y in zip(np.ravel(X), np.ravel(Y))])
Z = zs.reshape(X.shape)
ax.plot_surface(X, Y, Z)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
fake2Dline = mpl.lines.Line2D([0],[0], linestyle="none", c='b', marker = 'o')
ax.legend([fake2Dline], ['Lyapunov function on XY plane'], numpoints = 1)
plt.show()
I would say a title is more appropriate than a legend in this case.
According to this question, the issue is ongoing, and there is a relatively simple workaround. You can manually set the two missing attributes that would allow legend to automatically create the patch for you:
surf = ax.plot_surface(X, Y, Z, label='Lyapunov function on XY plane')
surf._edgecolors2d = surf._edgecolor3d
surf._facecolors2d = surf._facecolor3d
ax.legend()
The attribute names on the right hand side of the assignment are surf._edgecolors3d and surf.facecolors3d for matplotlib < v3.3.3.

Categories