2

In my quest to somehow get 3D polygons to actually plot, I came across the following script (EDIT: modified slightly): Plotting 3D Polygons in python-matplotlib

from mpl_toolkits.mplot3d import Axes3D
from matplotlib.collections import Poly3DCollection
import matplotlib.pyplot as plt
fig = plt.figure()
ax = Axes3D(fig)
x = [0,1,1,0]
y = [0,0,1,1]
z = [0,1,0,1]
verts = [zip(x, y,z)]
ax.add_collection3d(Poly3DCollection(verts),zs=z)
plt.show()

But when I run that, I get the following error message:

TypeError: object of type 'zip' has no len()

It seems that this may be a Python 2 vs. 3 thing, as I am running in Python 3, and that post is five years old. So I changed the third-to-last line to:

verts = list(zip(x, y, z))

Now verts shows up in the variable list, but I still get an error:

TypeError: zip argument #1 must support iteration

What? How do I fix this?

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Forklift17
  • 2,245
  • 3
  • 20
  • 32

3 Answers3

8

I've had a similar problem with the zipping. I support the thesis it is a python 2.x vs 3.x thing.

However, I've found somewhere that apparently works:

from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import matplotlib.pyplot as plt

fig = plt.figure()
ax = Axes3D(fig)
x = [0, 1, 1, 0]
y = [0, 0, 1, 1]
z = [0, 1, 0, 1]
verts = [list(zip(x, y, z))]
print(verts)
ax.add_collection3d(Poly3DCollection(verts), zdir='z')
plt.show()

I've thus made two changes:

  1. replaced the line: from matplotlib.collections import Poly3DCollection by: from matplotlib.mplot3.art3d import Poly3DCollection.

    I don't know where your import statement originates from, but it didn't seem to work for me

  2. changed the line: verts = list(zip(x,y,z)) to verts = [list(zip(x, y, z))]

Somehow, the latter seems to work. Having just started myself with python, I cannot offer an iron-clad explanation. However, here goes nothing: the class Poly3DCollection requires as the first input parameter a "collection", hence a list of lists. In this case, only list is given, which is assumed thus missed a level. By adding another level to it (via the [...]) it worked.

I've got no idea if this explanation makes sense, however it fits intuitively to me ;)

These modifications seem to work, as this code creates the desired 3D polygon (I've noticed that since this is my first post, I'm not allowed to post a proof-of-the-pudding figure.... )

hope this was useful,

kind regards

JohanC
  • 71,591
  • 8
  • 33
  • 66
Joris
  • 168
  • 9
  • You're right, `zip()` is the problem. In Python 2, `zip()` used to directly return a plain old list, which **matplotlib** can digest. In Python 3, `zip()` was changed to return an iterator, for use in contexts where not all of the items are needed at once (which could occupy a lot of memory if there are many) or where one stops consuming items before reaching the end. That **matplotlib** function cannot digest an iterator, so `list()` flattens the iterator into a list and makes it work again. – blubberdiblub Feb 13 '17 at 20:09
2

You have to use Poly3DCollection instead of PolyCollection:

from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import matplotlib.pyplot as plt
fig = plt.figure()
ax = Axes3D(fig)
x = [0,1,1,0]
y = [0,0,1,1]
z = [0,1,0,1]
verts = [zip(x,y,z)]
ax.add_collection3d(Poly3DCollection(verts), zs=z)
plt.show()

enter image description here

Serenity
  • 35,289
  • 20
  • 120
  • 115
  • 1
    Oops--I failed to mention that I was already trying Poly3DCollection. Again, the zipping is what is causing the error. I do not know how to fix this. – Forklift17 Jun 02 '16 at 18:01
  • Works for me when I remove the zs parameter. That is: ax.add_collection3d(Poly3DCollection(verts)) works. ax.add_collection3d(Poly3DCollection(verts), zs=z) returns error – mousomer Nov 21 '19 at 09:12
  • 1
    You don't need `zs=z` if you use `Poly3DCollection` as the z-axis value is already include. – nn0p Mar 12 '20 at 07:58
1

Here is a current solution, in case you get the Error ValueError: not enough values to unpack (expected 3, got 1), due to how verts is constructed in the other solutions:

from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import matplotlib.pyplot as plt
fig = plt.figure()
ax = Axes3D(fig)
x = [0,1,1,0]
y = [0,0,1,1]
z = [0,1,0,1]
verts = [[xx,yy,zz] for xx,yy,zz in zip(x,y,z)]     # NEW
ax.add_collection3d(Poly3DCollection(verts))
plt.show()

drops
  • 1,524
  • 1
  • 11
  • 20