I want a 3D scatter plot in MatPlotLib to be rotated interactively in a Jupyter Python notebook. For that reason I integrated a slider from ipywidgets
to update the view angle. The test code below shows what I am trying to achieve. The problem is that a new figure is added below the previous one, instead of the current figure cleared. I tried plt.close(fig)
, plt.cla()
and plt.clf()
without success. (further I realize that there is overhead in recreating the figure and axes but that is the lesser part of my current concerns...)
Here is the (test) code:
# init
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import ipywidgets as widgets
from IPython.display import display
# generate test data
x = np.random.rand(100)
y = np.random.rand(100)
z = np.random.rand(100)
# prepare plot
def draw_plot(angle1 = 20, angle2 = 40):
# create figure
fig = plt.figure(figsize=(15,10))
ax = fig.add_subplot(111, projection='3d')
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')
ax.scatter(x, y, z)
# set view angle
ax.view_init(angle1, angle2)
# show plot
plt.show()
# prepare widgets
angle1_slider = widgets.IntSlider(20, min = 0, max = 60)
angle1_label = widgets.Label(value = 'Angle 1 value is: ' + str(angle1_slider.value))
display(angle1_slider, angle1_label)
# handle angle 1 update
def update_angle1(value):
draw_plot(angle1 = value['new'])
angle1_label.value = 'Angle 1 value is: ' + str(value.new)
angle1_slider.observe(update_angle1, names = 'value')
# draw initial plot
draw_plot()
Any suggestions would be appreciated!
What you call overhead is the source of the problem. Or in other words: If in each call to the function, a new figure is created, is it surprising that you will end up with a lot of figures?
The idea is of course to draw a single figure. In order to be able to later update the figure the %matplotlib notebook
backend is needed.
The function that gets called when changing the slider will then only need to update the viewing angle and redraw the canvas.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import ipywidgets as widgets
from IPython.display import display
%matplotlib notebook
# generate test data
x = np.random.rand(100)
y = np.random.rand(100)
z = np.random.rand(100)
fig = plt.figure(figsize=(6,6))
ax = fig.add_subplot(111, projection='3d')
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')
ax.scatter(x, y, z)
ax.view_init(20, 40)
# show plot
plt.show()
def update_plot(angle1 = 20, angle2 = 40):
# set view angle
ax.view_init(angle1, angle2)
fig.canvas.draw_idle()
# prepare widgets
angle1_slider = widgets.IntSlider(20, min = 0, max = 60)
angle1_label = widgets.Label(value = 'Angle 1 value is: ' + str(angle1_slider.value))
display(angle1_slider, angle1_label)
# handle angle 1 update
def update_angle1(value):
update_plot(angle1 = value['new'])
angle1_label.value = 'Angle 1 value is: ' + str(value.new)
angle1_slider.observe(update_angle1, names = 'value')
This is how it would look like:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With