I need to generate and save thousands of scatter plots which are essentially the same except the only thing that changes is the "y" variable. What is the fastest way to do this?
I thought about creating the Figure and Axes instance and simply clearing then between plots like this:
import matplotlib.pyplot as plt
import numpy as np
data = np.random.random((100, 1000))
x = list(range(100))
fig = plt.figure()
ax = fig.add_subplot(111)
for i in range(data.shape[1]):
ax.scatter(x, data[:,i])
fig.savefig("%d.png" % i, dpi=100)
ax.cla()
This still takes an decent amount of time, so is a better/faster way to do this? Each image in this example is about 15kb so I'm assuming writing to disk is not limiting the speed too much.
One option is to use multiple processes.
import matplotlib.pyplot as plt
import numpy as np
import multiprocessing
data = np.random.random((100, 1000))
x = list(range(100))
fig = plt.figure()
ax = fig.add_subplot(111)
def save_plot(i):
ax.scatter(x, data[:,i])
fig.savefig("%d.png" % i, dpi=100)
ax.cla()
p = multiprocessing.Pool(4)
p.map(save_plot, range(data.shape[1]))
Try creating your figures without the GUI. I find that much faster when creating and saving many figures.
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
fig = plt.Figure()
ax = fig.add_subplot(111)
ax.plot(range(5))
canvas = FigureCanvas(fig)
canvas.print_figure('sample.png')
Something similar can be found at http://www.dalkescientific.com/writings/diary/archive/2005/04/23/matplotlib_without_gui.html
Related
I am trying to create a crude animation, where each additional data point is plotted on the same graph. The problem is that the loop is generating a new graph for each data point.
Here's the code:
x = []
y = []
for i in range(3):
x.append(random.randint(0,10))
y.append(random.randint(0,10))
plt.scatter(x,y)
plt.pause(0.1)
This resulted in 3 separate plots stacked vertically. I would like all data points to update on the same graph, creating an animation. Thanks!
I've made an adaptation of your code (in particular, in each iteration I plot only another scatter point, because plotting each time ALL the points soon becomes unbearably slow).
If you will execute this file, as I invite you to do, $ python3 slow.py, it will print 0 50 100 150 200 and, initially fast, then slower and slower, it will produce a progressive display of the data points, all in the same Axes.
I have to confess that I don't understand your problem description because it's so different from what I've seen.
import matplotlib.pyplot as plt
from matplotlib.cm import ScalarMappable
import random
def point():
return (random.randint(0,10), random.randint(0,10))
plt.xlim((-1, 11))
plt.ylim((-1, 11))
random.seed(20221229)
N = 200
cmap = plt.get_cmap('plasma')
plt.colorbar(ScalarMappable(cmap=cmap)).set_ticks((0,1), labels=("1",str(N)))
for i in range(N):
if not(i%50) : print(i)
plt.scatter(*point(), color=cmap(i/N), ec='black', s=80)
plt.pause(0.001)
print(N)
plt.show()
To update matplotlib graph you should use the module animation like Matplotlib is not very compatible with threads.
Here is an example adding a new point every 2 seconds :
import matplotlib.pyplot as pl
from matplotlib.animation import FuncAnimation
import random
datas = [0, 50]
fig = pl.figure()
ax = fig.add_subplot(1,1,1)
ax.scatter(x=datas, y=datas, marker = '+', c='red')
def update(frame):
global datas
ax.clear()
ax.scatter(x=datas, y=datas, marker = '+', c='red')
datas.append(random.randint(0,50))
animation = FuncAnimation(fig, update, interval=2000, repeat = True)
pl.show()
Interval (in milliseconds) in FuncAnimation function plays the role of the pause function you are looking for.
James, I think plt.scatter can't make an animation. All the code will be executed and then a chart with 3 points will be the result.
To avoid the generation of multiple figures you can use plt.subplots.
fig, ax = plt.subplots()
for i in range(3):
ax.scatter(x=random.randint(0,10),y= random.randint(0,10))
If you want to create some animated figure use matplotlib.animation.FuncAnimation , as in the answer of the following topic. How can i make points of a python plot appear over time?
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from IPython.display import HTML
x = np.arange(10)
y = np.random.random(10)
size = np.random.randint(150, size=10)
colors = np.random.choice(["r", "g", "b"], size=10)
fig = plt.figure()
plt.xlim(0, 10)
plt.ylim(0, 1)
graph = plt.scatter([], [])
def animate(i):
graph.set_offsets(np.vstack((x[:i+1], y[:i+1])).T)
graph.set_sizes(size[:i+1])
graph.set_facecolors(colors[:i+1])
return graph
ani = FuncAnimation(fig, animate, repeat=False, interval=200)
HTML(ani.to_jshtml())
Mark the option loop, and click in the minus or plus sing to slow or speed up the animation
Alternative approach, using FuncAnimation
from matplotlib.pyplot import Normalize, get_cmap, subplots
from matplotlib.cm import ScalarMappable
from matplotlib.animation import FuncAnimation, PillowWriter
from numpy.random import rand, seed
def update(xyn):
x, y, n = xyn
ax.scatter(x, y, color=cmap(n/N), ec='grey')
def start():
ax.set_xlim((-0.1, 1.1)) ; ax.set_ylim((-0.1, 1.1))
cb = fig.colorbar(ScalarMappable(cmap=cmap, norm=Normalize(0, 1)))
cb.set_ticks((0, 1), labels=('First', 'Last'))
def points(n):
seed(20230102)
def _():
for n_ in range(n):
yield rand(), rand(), n_
return _
fig, ax = subplots()
cmap = get_cmap('Greys')
N = 80
FuncAnimation(fig, update, points(N), start, repeat=False).save(
'scatter.gif', writer=PillowWriter())
Click Here for the image
trying to plot an animated line chart in python. Why is this code returning to a blank white plot ? a guidance would be appreciated. And also if there is a better way to draw an animated line chart in Python, please suggest.Thank you.
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
x_data=[]
y_data =[]
fig,ax = plt.subplots()
ax.set_xlim(0,100)
ax.set_ylim(0,12)
line, = ax.plot(0,0)
def update(i):
x_data.append(i*10)
y_data.append(i)
line.set_xdata(x_data)
line.set_ydata(y_data)
return line,
animation = FuncAnimation(fig,func = update, frames = np.arange(0,10,0.01), interval =200)
plt.show()
The code works for me, but is very slow because you have added 1000 frames at 200ms intervals, so the full animation takes 200 seconds to complete.
You need the imports at the top (and the appropriate libraries installed)
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
x_data = []
y_data = []
fig, ax = plt.subplots()
ax.set_xlim(0, 100)
ax.set_ylim(0, 12)
line, = ax.plot(0, 0)
def update(i):
x_data.append(i*10)
y_data.append(i)
line.set_xdata(x_data)
line.set_ydata(y_data)
return line,
animation = FuncAnimation(fig,func = update, frames = np.arange(0, 10, 0.01), interval = 2)
plt.show()
I have set the interval to 2ms in the above code to show a faster animation.
how to do this in jupyter notebook:
%matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt
m = 100
n = 100
matrix = np.random.normal(0,1,m*n).reshape(m,n)
fig = plt.figure()
ax = fig.add_subplot(111)
plt.ion()
fig.show()
fig.canvas.draw()
for i in range(0,100):
#ax.clear()
plt.plot(matrix[i,:])
fig.canvas.draw()
but using "%matplotlib qt5" instead of "notebook"?
When I try it it show the figure only after the loop is ended. I would like to see it updating every plot.
In principle you can do the following in interactive mode:
%matplotlib qt4
import numpy as np
import matplotlib.pyplot as plt
m = 100
n = 100
matrix = np.random.normal(0,1,m*n).reshape(m,n)
fig = plt.figure()
ax = fig.add_subplot(111)
plt.ion()
plt.draw()
for i in range(0,30):
#ax.clear()
plt.plot(matrix[i,:])
plt.draw()
plt.pause(0.1)
plt.ioff()
plt.show()
However, it might crash in jupyter after finishing the loop, due to Qt being used.
It should however work, when using the tk backend, %matplotlib tk.
You might want to consider using a FuncAnimation instead of interactive mode. While this would suffer from the same limitations with Qt in Jupyter I find it more intuitive to use a function for updating the plot and there is no need to redraw the canvas manually.
%matplotlib tk
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation
m = 100
n = 100
matrix = np.random.normal(0,1,m*n).reshape(m,n)
fig = plt.figure()
ax = fig.add_subplot(111)
def update(i):
#ax.clear()
plt.plot(matrix[i,:])
ani = matplotlib.animation.FuncAnimation(fig, update, frames=30, repeat=False)
plt.show()
I asked a new question on why this would no work with the qt backend.
I have a large scatter, say 100k points.
I want to animate it by enlarging a single point at each time-stamp so a trail will be seen.
FuncAnimation is great for updating scatter plots, however it updates the entire scatter, so once again slow at this scale.
Is there a way to get fast performance since i only wish to update a single point at each time?
Thanks!
import matplotlib.pyplot as plt
import numpy as np
num_points = 1000
plt.ion()
fig = plt.figure()
data = np.random.uniform(low=0, high=1, size=(num_points,2))
scat_hndlrs = []
for d in data:
scat_hndlrs.append(plt.scatter(*d, s=10))
for i in range(1,num_points):
scat_hndlrs[i]._sizes=[80]
scat_hndlrs[i-1]._sizes=[10]
fig.canvas.draw()
I'd suggest to only create one scatter plot, rather than a plot instance per point and then use the animation module to modify the sizes of the PathCollection returned by plt.scatter
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
num_points = 1000
fig, ax = plt.subplots()
data = np.random.uniform(low=0, high=1, size=(num_points,2))
data_t = data.T
sizes = np.ones(num_points)*10
scat_handler = plt.scatter(data_t[0], data_t[1], s=sizes)
def animate_blit(i):
sizes = scat_handler.get_sizes()
sizes[i] = 50
sizes[(i - 1) % num_points] = 10
scat_handler.set_sizes(sizes)
return scat_handler,
ani = animation.FuncAnimation(fig, animate_blit, num_points, blit=True, repeat=True)
plt.show()
I have read widely on this topic and spent several hours looking for ways to implement an animation for my plot.
I have managed an "animation" in-loop using plt.ion(), however, I can't get anything in matplotlib.animation to produce me an animated clip.
Below is a basic setup of the data I have to animate.
import matplotlib.pyplot as plt
import numpy as np
class plot_output(object):
def __init__(self):
self.fig, self.ax = plt.subplots(2,10)
plt.ion()
plt.show()
def iterate_animation(self, i, weight_list):
X = weight_list[i]
for i in range(20):
self.ax[i/10, i%10].cla()
wi = X[:,i]
wi=wi.reshape((28,28))
self.ax[i/10, i%10].axis('off')
self.ax[i/10, i%10].imshow(wi,cmap=plt.cm.gist_yarg,
interpolation='gaussian', aspect='equal')
plt.draw()
w_list = [np.random.rand(784,20) for i in range(4)]
p = plot_output()
for i in range(4):
p.iterate_animation(i=i, weight_list=w_list)