Updating Matplotlib 3D with new data and ticker values - python

When I use axes.cla() on my 3D.plot to remove all the lines, modify the axis' and then call draw() on that figure again, the axis labels and lines become thicker. And it is only on the axis that are modified with ticker. My guess is i have to delete the old axis labels somehow, how can I do that?
Here is a minimal example code of what I am talking about. I linked the redraw to mouse click on the canvas. Code is ment for Python 3. (I already tried a workaround with clf() and redraw the whole figure, but in 3D mode it does not work.)
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.ticker as ticker
def onclick(event):
event.inaxes.cla()
event.inaxes.w_xaxis.set_major_locator(ticker.LinearLocator())
event.inaxes.w_xaxis.set_major_locator(ticker.LinearLocator())
event.canvas.draw()
fig = plt.figure()
axes = fig.add_subplot(111, projection='3d')
fig.canvas.mpl_connect('button_press_event', onclick)
plt.show()

I got a workaround by not using cla(). I only removed my plot data in axes.lines and axes.collections.

Related

Redrawing Seaborn Figures for Animations

Some seaborn methods like JointPlot create new figures on each call. This makes it impossible to create a simple animation like in matplotlib where iterative calls to plt.cla() or plt.clf() allow to update the contents of a figure without closing/opening the window each time.
The only solution I currently see is:
for t in range(iterations):
# .. update your data ..
if 'jp' in locals():
plt.close(jp.fig)
jp = sns.jointplot(x=data[0], y=data[1])
plt.pause(0.01)
This works because we close the previous window right before creating a new one. But of course, this is far from ideal.
Is there a better way? Can the plot somehow be done directly on a previously generated Figure object? Or is there a way to prevent these methods to generate new figures on each call?
sns.jointplot creates a figure by itself. In order to animate the jointplot, one might therefore reuse this created figure instead of recreating a new one in each iteration.
jointplot internally creates a JointGrid, so it makes sense to directly use this and plot the joint axes and the marginals individually. In each step of the animation one would then update the data, clear the axes and set them up just as during creation of the grid. Unfortunately, this last step involves a lot of code lines.
The final code may then look like:
import matplotlib.pyplot as plt
import matplotlib.animation
import seaborn as sns
import numpy as np
def get_data(i=0):
x,y = np.random.normal(loc=i,scale=3,size=(2, 260))
return x,y
x,y = get_data()
g = sns.JointGrid(x=x, y=y, size=4)
lim = (-10,10)
def prep_axes(g, xlim, ylim):
g.ax_joint.clear()
g.ax_joint.set_xlim(xlim)
g.ax_joint.set_ylim(ylim)
g.ax_marg_x.clear()
g.ax_marg_x.set_xlim(xlim)
g.ax_marg_y.clear()
g.ax_marg_y.set_ylim(ylim)
plt.setp(g.ax_marg_x.get_xticklabels(), visible=False)
plt.setp(g.ax_marg_y.get_yticklabels(), visible=False)
plt.setp(g.ax_marg_x.yaxis.get_majorticklines(), visible=False)
plt.setp(g.ax_marg_x.yaxis.get_minorticklines(), visible=False)
plt.setp(g.ax_marg_y.xaxis.get_majorticklines(), visible=False)
plt.setp(g.ax_marg_y.xaxis.get_minorticklines(), visible=False)
plt.setp(g.ax_marg_x.get_yticklabels(), visible=False)
plt.setp(g.ax_marg_y.get_xticklabels(), visible=False)
def animate(i):
g.x, g.y = get_data(i)
prep_axes(g, lim, lim)
g.plot_joint(sns.kdeplot, cmap="Purples_d")
g.plot_marginals(sns.kdeplot, color="m", shade=True)
frames=np.sin(np.linspace(0,2*np.pi,17))*5
ani = matplotlib.animation.FuncAnimation(g.fig, animate, frames=frames, repeat=True)
plt.show()
using the celluloid package (https://github.com/jwkvam/celluloid) I was able to animate seaborn plots without much hassle:
import numpy as np
from celluloid import Camera
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
fig = plt.figure()
camera = Camera(fig)
# animation draws one data point at a time
for i in range(0, data.shape[0]):
plot = sns.scatterplot(x=data.x[:i], y=data.y[:i])
camera.snap()
anim = camera.animate(blit=False)
anim.save('animation.mp4')
I'm sure similar code could be written for jointplots

Adjust whitespace margins on animated Axes3D scatter plot (matplotlib)

I am using matplotlib 1.2.1 and am limited to that version for now. The problem I run into is the crowding of the axis labels and ticks in the bottom of the frame with this rotated plot. I have tried plt.adjust_subplots, I've tried tight_layout, changing the figure size, ax.dist and more but nothing seems to change the appearance as shown in the example image below. This is just one frame but often as the plot rotates, it goes out of frame at the bottom (especially when ticks and labels are bigger). Perhaps my problem is where I place the ax.dist argument?
How can I add a little more whitespace at the bottom of this animated plot? As a bonus, how can I get rid of the confusing double grid lines at the edges? Thank you.
import matplotlib as mpl
import matplotlib.pyplot as plt
plt.rcParams['animation.ffmpeg_path']='C:/FFMPEG/bin/ffmpeg.exe'
import matplotlib.animation as animation
from mpl_toolkits.mplot3d import Axes3D
def format_3d_ax(ax):
ax.w_xaxis.set_pane_color((1.0,1.0,1.0,1.0))
ax.w_yaxis.set_pane_color((1.0,1.0,1.0,1.0))
ax.w_zaxis.set_pane_color((1.0,1.0,1.0,1.0))
ax.w_xaxis._axinfo['grid'].update({'linewidth':0.25,'color':'black'})
ax.w_yaxis._axinfo['grid'].update({'linewidth':0.25,'color':'black'})
ax.w_zaxis._axinfo['grid'].update({'linewidth':0.25,'color':'black'})
def rotate(i,ax, angles, elev):
ax.view_init(elev,angles[i])
plt.draw()
return ax
def rotate_3d(fig,ax,angles,elev):
frames=len(angles)
ani=animation.FuncAnimation(fig,rotate, fargs=(ax,angles, elev),blit=True,
frames=frames, interval=200)
return ani
fig=plt.figure(figsize=(12,7.6))
ax=Axes3D(fig)
ax.view_init(elev=25, azim=0)
format_3d_ax(ax)
ax.set_xlabel('Reasonably sized x label')
ax.set_ylabel('Reasonably sized y label')
ax.set_zlabel('z label')
ax.scatter([1],[1],[1], marker='.', s=80)
ani=rotate_3d(fig,ax,range(0,45),25)
Writer=animation.writers['ffmpeg']
writer=Writer(fps=30, bitrate=2000)
ani.save('C:/Temp/test.mp4', writer=writer, dpi=300)
print ('Done') #helps to know when the writer is done.

"Reset original view" does not show the whole plot

I'm plotting a line and updating it in a loop. When I pan the plot at some point during the execution and then click "Reset original view" in the interactive matplotlib window, I am taken back to the plot state from the moment when I started zooming/panning it. Is there a way to see the full extents of the plot instead? Even better, is there a way to tell matplotlib to keep updating the view after this operation?
python 3.4.3, matplotlib 1.4.3
import matplotlib
matplotlib.use('Qt4Agg')
import matplotlib.pyplot as plt
import numpy as np
fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
values_v = []
values_i = []
ln1, = ax1.plot(values_i, values_v, color='green')
plt.ion()
plt.show()
for i in range(40):
scopevals = [i, i+2+np.random.rand()]
values_v.append(scopevals[0])
values_i.append(scopevals[1])
ln1.set_data([values_i, values_v])
ax1.relim()
ax1.autoscale_view(True,True,True)
plt.pause(1)
I encountered the problem when I displayed different images in the same figure.
Clearing the old figure helped me:
plt.figure(1) #the figure you re working with
plt.clf() #clear figure
plt.imshow(self.sample_image) # show new picture
And the Original view should work again.
Cheers
Andy

Plotting second figure with matplotlib while first is still open

K here's a more precise example of what I am trying to do. I am using WXBuilder for Python as my user interface with multiple plotting functionality i.e. the user must be able to plot a graph based on their chosen parameters. After a graph is plotted I want the user to be able to plot a second without closing the first figure. This is for comparison purposes. Below is an oversimplified example of what I am looking to do.
import matplotlib as plt
def OnPlotClick1(self, event):
plt.plot(self.DateArray1, self.kVAArray2)
plt.show()
def OnPlotClick2(self, event):
plt.plot(self.DateArray1, self.kVAArray2)
plt.show()
Now I am assuming my problem is arising due plotting and showing() the graph, and therefore the program somehow is blocked from functionality until the first figure or plot window is closed.
I hope this explains my problem better.
You should not block show. Use:
import matplotlib.pylab as plt
plt.plot([1,2,3]) # first plot
plt.show(block=False) # do not block
plt.plot([11,21,31]) # second plot
Each window is in matplotlib parlance, a new figure. You can call plt.subplots twice to create two figures:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 10, 1000)
y1 = np.sin(x)*np.exp(-x/5.0)
y2 = np.sin(x**2)*x
fig1, ax1 = plt.subplots()
ax1.plot(x, y1)
fig2, ax2 = plt.subplots()
ax2.plot(x, y2)
plt.show()
Note that plt.show() starts a GUI event loop and so generally it should only be called once per script.
You can also draw 2 or more than 2 plotters in the same figure
import matplotlib.pyplot as plt
def my_plotter(ax, data1, data2, param_dict):
out = ax.plot(data1, data2, **param_dict)
return out
fig, (ax1, ax2) = plt.subplots(1, 2)
#here you put your data
data1=[0,1,2,3,8]
data2=[0,1,2,3,8]
data3=[0,1,2,3,8]
data4=[0,1,2,3,8]
my_plotter(ax1, data1, data2, {'marker':'x'})
my_plotter(ax2, data3, data4, {'marker':'o'})
plt.show()
You can either follow #(Corrupted MyStack) suggestion or with interactive graphic devide. Run
plt.ion()
once, anytime before you start the plots. To turn it off
plt.ioff()

Matplotlib crashes after saving many plots

I am plotting and saving thousands of files for later animation in a loop like so:
import matplotlib.pyplot as plt
for result in results:
plt.figure()
plt.plot(result) # this changes
plt.xlabel('xlabel') # this doesn't change
plt.ylabel('ylabel') # this doesn't change
plt.title('title') # this changes
plt.ylim([0,1]) # this doesn't change
plt.grid(True) # this doesn't change
plt.savefig(location, bbox_inches=0) # this changes
When I run this with a lot of results, it crashes after several thousand plots are saved. I think what I want to do is reuse my axes like in this answer: https://stackoverflow.com/a/11688881/354979 but I don't understand how. How can I optimize it?
I would create a single figure and clear the figure each time (use .clf).
import matplotlib.pyplot as plt
fig = plt.figure()
for result in results:
fig.clf() # Clears the current figure
...
You are running out of memory since each call to plt.figure creates a new figure object. Per #tcaswell's comment, I think this would be faster than .close. The differences are explained in:
When to use cla(), clf() or close() for clearing a plot in matplotlib?
Although this question is old, the answer would be:
import matplotlib.pyplot as plt
fig = plt.figure()
plot = plt.plot(results[0])
title = plt.title('title')
plt.xlabel('xlabel')
plt.ylabel('ylabel')
plt.ylim([0,1])
plt.grid(True)
for i in range(1,len(results)):
plot.set_data(results[i])
title.set_text('new title')
plt.savefig(location[i], bbox_inches=0)
plt.close('all')

Categories

Resources