4

I have a matplotlib figure that I want to be able to switch between 2D and 3D projections. I can go from 2D to 3D but I can't seem to work out how to go the other way. Example...

import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt

def randrange(n, vmin, vmax):
    return (vmax-vmin)*np.random.rand(n) + vmin

fig = plt.figure()

# Create a 3D scatter plot...
ax = fig.add_subplot(111, projection='3d')
n = 100
for c, m, zl, zh in [('r', 'o', -50, -25), ('b', '^', -30, -5)]:
    xs = randrange(n, 23, 32)
    ys = randrange(n, 0, 100)
    zs = randrange(n, zl, zh)
    ax.scatter(xs, ys, zs, c=c, marker=m)

ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')

# Now I want a 2D plot...
ax.cla()
ax = fig.add_subplot(111)
ax.plot(xs, ys)

plt.show()

The plot stays in the 3D projection and projection="2D" isn't a valid kwarg...

I thought perhaps ax.clf() would do what I wanted and let me define a new figure. But it just gives me the following error: ValueError: Unknown element o

Can anyone give me a hint as to the solution to this? Is the ValueError related to the problem or a hint to something else wrong with my setup? Is there a kwarg to switch the projection from 3D to 2D?

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Dan
  • 1,209
  • 3
  • 13
  • 29

3 Answers3

2

Let me start out by saying that if you boosted your acceptance rate (which is currently 0%) by going and demarcating the answers from your previous questions that you actually used, then perhaps more people would be willing to help you.

Now, to answer your question, there is no '2d' projection kwarg. However, if you want to go through and quickly decide which type of projection you would like, depending on a keyword 'q', the following should help you. I also altered the basic setup to avoid confusion between your different kinds of plots, since you had some plotting calls outside of the loop, and generally clean up your organization.

Hope this helps.

import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt

def randrange(n, vmin, vmax):
   return (vmax-vmin)*np.random.rand(n) + vmin

fig = plt.figure()
plt.clf()

q='2d'

n = 100
for c, m, zl, zh in [('r', 'o', -50, -25), ('b', '^', -30, -5)]:
  xs = randrange(n, 23, 32)
  ys = randrange(n, 0, 100)  
  zs = randrange(n, zl, zh)

if q=='3d':

  # Create a 3D scatter plot...
  ax = fig.add_subplot(111, projection='3d')
  ax.scatter(xs, ys, zs, c=c, marker=m)
  ax.set_xlabel('X Label')
  ax.set_ylabel('Y Label')
  ax.set_zlabel('Z Label')
  plt.show()

else:
  plt.clf()
  ax = fig.add_subplot(111)
  ax.plot(xs, ys)
  ax.set_xlabel('X Label')
  ax.set_ylabel('Y Label')
  plt.show()
cosmosis
  • 6,047
  • 3
  • 32
  • 28
  • Thanks for your input cosmosis. I'd missed that accepting and voting useful weren't the same thing! Corrected my mistake now. I guess my question must be somewhat ambiguous because of my simplified example. I want to be able to switch a single figure between 2D and 3D projections, not create one or the other. The figure is on a canvas in a wxPython GUI. Any ideas how this can be achieved? – Dan Oct 31 '11 at 11:54
2

I believe I have found one possible solution, although it seems to result in a bit of a memory issue. I suspect that it isn't actually deleting the initial plot data, just removing it from the figure so memory usage does climb every time the projection is changed.

# Delete the 3D subplot
self.fig.delaxes(self.axes)
# Create a new subplot that is 2D
self.axes = self.fig.add_subplot(111)
# 2D scatter
self.axes.plot(10*np.random.randn(100), 10*np.random.randn(100), 'o')
# Update the figure
self.canvas.draw()
Dan
  • 1,209
  • 3
  • 13
  • 29
1

I was having the same problem and tried the accepted solution (posted by Dan). It worked, but gave me the following warning:

"UserWarning: This figure includes Axes that are not compatible with tight_layout, so its results might be incorrect."

However if I use:

self.figure.clf()
self.axes = self.figure.add_subplot(111)

then it works without any warnings.

Vinoth Krishnan
  • 2,925
  • 6
  • 29
  • 34
Jay Eff
  • 11
  • 1
  • 5