axes3d.plot_wireframe(X,Y,Z) Error - python

I'm trying to learn Python through a tutorial on youtube and I'm having some difficulies working with 3D graphs. Long stories short, I continuously get (if
Z.ndim != 2:
AttributeError: 'list' object has no attribute 'ndim')
error while trying to launch this simple program:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
fig = plt.figure()
chart = fig.add_subplot(1,1,1,projection = '3d')
X,Y,Z = [1,2,3,4,5,6,7,8],[2,5,3,8,9,5,6,1],[3,6,2,7,5,4,5,6]
chart.plot_wireframe(X,Y,Z)
plt.show()
I know that it is related to the Axes3.plot_wireframe() method but Could anyone explain to me what's happening.

I walked around this problem by doing two things.
import numpy as np
making the z-axis a multidimensional array
#My 3d graph
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
import numpy as np
figure = plt.figure()
axis = figure.add_subplot(111, projection = '3d')
x = [1,2,3,4,5,6,7,8,9,10]
y = [5,6,7,8,2,5,6,3,7,2]
z = np.array([[1,2,6,3,2,7,3,3,7,2],[1,2,6,3,2,7,3,3,7,2]])
axis.plot_wireframe(x, y, z)
axis.set_xlabel('x-axis')
axis.set_ylabel('y-axis')
axis.set_zlabel('z-axis')
plt.show()
Take special note of the z variable. If z is not multidimensional, it will throw an error.
Hope it solves your problem

Running your code with either Python 2.7.10 or Python 3.6.0, with matplotlib version 2.0.2, yields the same image with no error:
This is not a wireframe though, and a simple ax.plot(X, Y, Z) would have generated it. As DavidG and ImportanceOfBeingErnest cleverly mentioned, it makes no sense to pass 1D lists to the wireframe function, as X, Y and Z should be two-dimensional.
The following code (an example taken from the matplotlib official documentation) shows exactly how the parameters of the plot_wireframe function should be (using numpy arrays):
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
'''
def get_test_data(delta=0.05):
from matplotlib.mlab import bivariate_normal
x = y = np.arange(-3.0, 3.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = Z2 - Z1
X = X * 10
Y = Y * 10
Z = Z * 500
return X, Y, Z
'''
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x, y, z = axes3d.get_test_data(0.05)
ax.plot_wireframe(x,y,z, rstride=2, cstride=2)
plt.show()
The output image is a true wireframe:
Printing x.shape, for instance, yields you (120, 120), showing that the array is two-dimensional and have 120 positions in the first dimension and 120 positions in the second one.

I had the exact problem (example from video not working though exactly copied). Without looking into the source code I'm assuming a reality check was added to matplotlib 2.1.0 that NOW stops 1D arrays from being used in plot_wireframe. Changing that method call to simply "plot" did indeed fix the problem.

The command
ax.plot_wireframe(x,y,z, rstride=2, cstride=2)
is creating the problems with the latest versions.
Try using:
ax.plot(x,y,z)
This will definitely solve your issues.
Python has been known for being inconsistent with the older libraries.
I am getting this image as the output:
This is the 3d Image I am getting

Related

Plotly plot_trisurf isn't working with arange arrays

I've basically just copied the example code found on the Matplotlib website, but I replaced their radii and angles with simple arange arrays.
I've tried different array functions and I can't seem to figure out anything.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
from Equation import Expression
x = np.arange(0,100,0.01)
y = np.arange(0,100,0.01)
x2 = np.append(0,x.flatten())
y2 = np.append(0,y.flatten())
z = x2 + y2
print(z)
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_trisurf(x, y, z, linewidth=0.2, antialiased=True)
plt.show()
I'm just trying to make a graph of z = x + y but I'm getting a confusing error.
"RuntimeError: Error in qhull Delaunay triangulation calculation: singular input data (exitcode=2); use python verbose option (-v) to see original qhull error."
Edit: I've also tried it without calling flatten() but I get the same result though.
The error you are getting is because your z is not a surface but a line. You need to use at least 3 points that would define a plane. One option could be to use np.meshgrid to create your surface for plotting and then flatten everything to insert into the function. Try going back to some example code here. Note you may also want to change your resolution depending on the detail of your surface.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(0,100,1)
y = np.arange(0,100,1)
x2 = np.append(0,x.flatten())
y2 = np.append(0,y.flatten())
x2,y2 = np.meshgrid(x2,y2) #This is what you were missing
z = x2 + y2
fig = plt.figure(figsize=(12,12))
ax = fig.gca(projection='3d')
ax.plot_trisurf(x2.flatten(), y2.flatten(), z.flatten(), linewidth=0.2, antialiased=True) #flatten all the arrays here
plt.show()

Using matplotlib to make 3D plot

Just trying to make a 3D plot of a constant (0). So I have
width = 1
dx = 0.1
X = np.arange(-width, width, dx)
Y = np.arange(-width, width, dx)
X, Y = np.meshgrid(X, Y)
Z = []
for i in range(len(X)):
Z.append(np.zeros(len(X[i])))
But when I try to run Axes3D.plot_wireframe(X,Y,Z) I get plot_wireframe() missing 1 required positional argument: 'Z'. I need help understanding why this is, because Z is a 2D array like it should be, and I can't find many helpful examples with 3D plotting with matplotlib.
The main point is that you cannot run Axes3D.plot_wireframe(X,Y,Z) by itself. Instead you need to create an instance of Axes3D and call its method [*]. Just like in the 2D case where you wouldn't call matplotlib.axes.Axes.plot(x,y) but ax.plot(x,y) where ax is the instance created e.g. via fig.add_subplot.
An example for the wireframe plot can be found here.
The following code (using the code from the question)
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')
width = 1
dx = 0.1
X = np.arange(-width, width, dx)
Y = np.arange(-width, width, dx)
X, Y = np.meshgrid(X, Y)
Z = []
for i in range(len(X)):
Z.append(np.zeros(len(X[i])))
ax.plot_wireframe(X, Y, Z)
plt.show()
produses the following plot
[*] To be precise here; you can call the class method plot_wireframe, but you would then need to supply it with the instance like
Axes3D.plot_wireframe(ax, X, Y, Z)
I found this example online.
I've pasted it in your code example and I got the following code + plot:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
width = 1
dx = 0.1
X = np.arange(-width, width, dx)
Y = np.arange(-width, width, dx)
X, Y = np.meshgrid(X, Y)
Z = []
for i in range(len(X)):
Z.append(np.zeros(len(X[i])))
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_wireframe(X, Y, Z)
plt.show()
Hope this helps! I ran it with python 3.5, using the spyder IDE.
Cheers,
Dave

creating surface data for axes3d

Okay, apologies for this question but I'm pulling my hair out here.
I have a data structure loaded in python in the form:
[(1,0,#),(1,1,#),(1,2,#),(1,3,#),(2,0,#),(2,1,#) ... (26,3,#)]
with # being a different number each time that I wish to represent on the z-axis. You can see that x and y are always integers.
Plotting a scatter graph is simple:
x,y,z = zip(*data)
fig = plt.figure()
ax = fig.gca(projection = '3d')
surface = ax.scatter(x, y, z)
plt.show()
But when it comes to surfaces, I can see two methods:
1) Call ax.plot_trisurf(), which should work with 1D arrays similar to ax.scatter() and apparently works here, but for me gives me an error:
"AttributeError: Axes3D subplot object has not attribute 'plot_trisurf'"
This error also appears if I use the example source code at:
http://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html#tri-surface-plots, suggesting it's something wrong with my installation - my Matplotlib version is 1.1.1rc,. This error does not appear if, for example, ax.plot_surface() is called, nor ax.scatter().
2) Use meshgrid() or griddata() in combination with ax.plot_surface() - in either case, after two days' of pouring over the documentation and examples, I still don't understand how to correctly use these in my case, particularly when it comes to generating the values for Z.
Any help would be much appreciated.
To address your first question (1) I believe you need to import Axes3D from the mplot3d library, even if you're not directly calling it. Maybe try adding
from mpl_toolkits.mplot3d import Axes3D
before your main code (this line triggered a memory while reading the tutorial).
As for (2), X, Y and Z need to be matrix (2d array) type objects. This can get confusing, but you may consider an example:
# two arrays - one for each axis
x = np.arange(-5, 5, 0.25)
y = np.arange(-5, 5, 0.25)
# create a mesh / matrix like object from the arrays
X, Y = np.meshgrid(x, y)
# create Z values - also in a mesh like shape
Z = np.sin(np.sqrt(X**2 + Y**2))
# plot!
surface = ax.plot_surface(X, Y, Z)
Here is an example of how could you extract your z-values from data
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
data = [(j,i,i**2 + j) for j in range(1,27) for i in range(4)]
print data
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(0, 4, 1)
Y = np.arange(1, 27, 1)
X, Y = np.meshgrid(X, Y)
print X.shape
print Y.shape
Z = np.array([z for _,_,z in data]).reshape(26,4)
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,
linewidth=0, antialiased=True)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.xlabel('X')
plt.ylabel('Y')
plt.show()

Plotting surface of implicitly defined volume

Having a volume implicitly defined by
x*y*z <= 1
for
-5 <= x <= 5
-5 <= y <= 5
-5 <= z <= 5
how would I go about plotting its outer surface using available Python modules, preferably mayavi?
I am aware of the function mlab.mesh, but I don't understand its input. It requires three 2D arrays, that I don't understand how to create having the above information.
EDIT:
Maybe my problem lies with an unsufficient understanding of the meshgrid()-function or the mgrid-class of numpy. I see that I have to use them in some way, but I do not completely grasp their purpose or what such a grid represents.
EDIT:
I arrived at this:
import numpy as np
from mayavi import mlab
x, y, z = np.ogrid[-5:5:200j, -5:5:200j, -5:5:200j]
s = x*y*z
src = mlab.pipeline.scalar_field(s)
mlab.pipeline.iso_surface(src, contours=[1., ],)
mlab.show()
This results in an isosurface (for x*y*z=1) of a volume though, which is not quite what I was looking for. What I am looking for is basically a method to draw an arbitrary surface, like a "polygon in 3d" if there is such a thing.
I created the following code, which plots a surface (works with mayavi, too). I would need to modify this code to my particular problem, but to do that I need to understand why and how a 3d surface is defined by three 2d-arrays? What do these arrays (x, y and z) represent?
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import axes3d, Axes3D
phi, theta = np.mgrid[0:np.pi:11j, 0:2*np.pi:11j]
x = np.sin(phi) * np.cos(theta)
y = np.sin(phi) * np.sin(theta)
z = np.cos(phi)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_wireframe(x,y,z)
fig.show()
The outer surface, implicitly defined by
x*y*z = 1,
cannot be defined explicitly globally. To see this, consider x and y given, then:
z = 1/(x*y),
which is not defined for x = 0 or y = 0. Therefore, you can only define your surface locally for domains that do not include the singularity, e.g. for the domain
0 < x <= 5
0 < y <= 5
z is indeed defined (a hyperbolic surface). Similarly, you need to plot the surfaces for the other domains, until you have patched together
-5 <= x <= 5
-5 <= y <= 5
Note that your surface is not defined for x = 0 and y = 0, i.e. the axis of your coordinate system, so you cannot patch your surfaces together to get a globally defined surface.
Using numpy and matplotlib, you can plot one of these surfaces as follows (adopted from http://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html#surface-plots):
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(0.25, 5, 0.25)
Y = np.arange(0.25, 5, 0.25)
X, Y = np.meshgrid(X, Y)
Z = 1/(X*Y)
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
ax.set_zlim(0, 10)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
I'm not familiar with mayavi, but I would assume that creating the meshes with numpy would work the same.
The test case in the Mayavi docs where the function test_mesh() is defined is capable of producing a sphere. This is done by replacing
r = sin(m0*phi)**m1 + cos(m2*phi)**m3 + sin(m4*theta)**m5 + cos(m6*theta)**m7
with r = 1.0 say.
However, your problem is you need to understand that the equations you are writing define a volume when you want to draw a sphere. You need to reformulate them to give a parametric equation of a sphere. This is essentially what is done in the above example, but it may be worth your while to try it yourself. As a hint consider the equation of a circle and extend it.

Plotting A Hyperboloid

Teacher in class gave this formula
w = x**2 + y**2 - z**2
and showed its 3d graphic in class seen below. How do I plot this using Matplotlib (minus the intersecting plane)? I guess first a specific value for w needs to be selected, for example 10, otherwise 3d plotting would not be possible. Then should I convert to polar coordinates because of the z**2 in the formula? I tried this and failed. Any help would be appreciated. Also, does this shape have a name?
Got it. Found some good stuff here, and following the formulas presented, I have the Python code below.
http://msenux.redwoods.edu/Math4Textbook/Plotting/ParametricSurfaces.pdf
from __future__ import division
from mpl_toolkits.mplot3d import Axes3D
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')
r=1;
u=np.linspace(-2,2,200);
v=np.linspace(0,2*np.pi,60);
[u,v]=np.meshgrid(u,v);
a = 1
b = 1
c = 1
x = a*np.cosh(u)*np.cos(v)
y = b*np.cosh(u)*np.sin(v)
z = c*np.sinh(u)
ax.plot_surface(x, y, z, rstride=4, cstride=4, color='b')
plt.show()

Categories