How do I animate a circle when I have its radius as a function of time (I have them both as arrays) in Python? That is, I want the frames to follow the times given in the time array.
I did the following which works but does not make use of FuncAnimation() (which is a problem as I need to save it as a GIF or mp4).
import matplotlib.pyplot as plt
import numpy as np
time = np.array([0.0,0.1,0.122,0.124,0.4,0.45,0.5,1.2,1.4,1.5])
r = np.array([100.0,99.0,90.0,80.0,78.0,50.0,40.0,30.0,10.0,5.0])
fig, ax = plt.subplots()
ax.set_xlim(-max(r),max(r))
ax.set_ylim(-max(r),max(r))
for j in range(len(time)):
ax.add_artist(plt.Circle((0, 0), r[j], color='r'))
ax.set_title(str(time[j]))
fig.canvas.draw()
if j+1==len(time):
break
plt.pause((time[j+1]-time[j]))
plt.gca().cla()
plt.show()
Is there a way to do it using FuncAnimation() and save it?
Related
I am giving data to a matrix (e.g. with shape 100x100) by the following code:
from random import randint
import matplotlib.pyplot as plt
import numpy as np
import random as rand
tab = np.eye(100, 100)
x = np.arange(0, 100, 1)
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
for i in range(100):
for j in range(100):
tab[i, j] = rand.randint(0, 254)
line1, = ax.plot(x, tab[i, :], 'r-')
line1.set_ydata(tab[i, j])
fig.canvas.draw()
fig.canvas.flush_events()
ax.lines.remove(line1)
I need to update matrix using loops and upgrade plot in the same time.
When loop with j ends, i-loop want to clear plot and start plotting again. Is it possible?
My result:
What I need:
After reading your comment i think i understood what you where trying to do
the reason you got those horizontal lines was that you're setting ydata again after plotting(to a constant so its like plotting a horizontal line)
consider the code below:
from random import randint
import matplotlib.pyplot as plt
import numpy as np
import random as rand
tab = np.eye(100, 100)
x = np.arange(0, 100, 1)
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
for i in range(100):
for j in range(100):
tab[i, j] = ((50-i/2)*(50-i/2)-(50-j)*(50-j))/100
for i in range(100):
line1, = ax.plot(x, tab[i, :], 'r-')
fig.canvas.draw()
fig.canvas.flush_events()
ax.lines.remove(line1)
I used another for to instantiate the tab map (since you're using sensor data I guess that is exactly what you're doing in your code because you need to read all of the data (at least the ones for the current cross section) to be able to plot the type of graph you want. this is equivalent to reading all of the data at the beginning and then starting to plot it)
(I also used simulated values instead of random values for the sake of testing)
if you want to draw the data AS THEY COME FROM THE SENSOR then you must define a function to get the data of the current cross section from the sensor and return an array. Idk the library you're using for the sensor but I'm assuming the scan functions are synchronous so the function will return exactly after the input is over making the whole thing pseudo-real time
from random import randint
import matplotlib.pyplot as plt
import numpy as np
import random as rand
x = np.arange(0, 100, 1)
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
for i in range(100):
data = READ_CURRENT_CROSS_SECTION()
line1, = ax.plot(x, data, 'r-')
fig.canvas.draw()
fig.canvas.flush_events()
ax.lines.remove(line1)
again, if plotting the data as the come from the sensor is your goal here it is going to depend a lot on the library you're using but except for all of that the problem with your code was that it was trying to plot while it was getting the data point by point which gives you insufficient data for plotting a cross section(hence the straight lines) (PS: there actually are some ways to pull it off like this but will be extremely slow!)
So either
write a function to scan the whole 2d area and return the whole map before you start plotting(which will be like my first code and the function i just said will replace lines 11-13). this takes away the real time feature but it will give you a beautiful animated plot in a short time
write a function to scan each cross section and return it as a 100 element array. which makes it kind of real time but i guess is harder to implement. This is like my second code but you have to define READ_CURRENT_CROSS_SECTION yourself
I know we can create simple 3-Dimensional spheres using matplotlib, an example of such a sphere is included in the documentation.
Now, we also have a warp method as part of the matplotlib module, an example of it's usage is here .
To warp a cylindrical image to the sphere. Is it possible to combine these methods to create a 3D rotatable earth? Unless my way of thinking about this problem is way off it seems that to be able to do this you would have to take the pixel data of the image and then plot every pixel using the sin and cosine expressions along the surface of the 3D sphere being created in the first example. Some examples of these cylindrical maps can be found here
I know alternative ways to do this are through maya and blender, but I am attempting to stay within matplotlib to do this, as I want to create this plot and then be able to plot geospatial data to the surface using an array of data.
Interesting question. I tried to basically follow the thinking outlined by #Skeletor, and map the image so that it can be shown with plot_surface:
import PIL
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
# load bluemarble with PIL
bm = PIL.Image.open('bluemarble.jpg')
# it's big, so I'll rescale it, convert to array, and divide by 256 to get RGB values that matplotlib accept
bm = np.array(bm.resize([d/5 for d in bm.size]))/256.
# coordinates of the image - don't know if this is entirely accurate, but probably close
lons = np.linspace(-180, 180, bm.shape[1]) * np.pi/180
lats = np.linspace(-90, 90, bm.shape[0])[::-1] * np.pi/180
# repeat code from one of the examples linked to in the question, except for specifying facecolors:
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = np.outer(np.cos(lons), np.cos(lats)).T
y = np.outer(np.sin(lons), np.cos(lats)).T
z = np.outer(np.ones(np.size(lons)), np.sin(lats)).T
ax.plot_surface(x, y, z, rstride=4, cstride=4, facecolors = bm)
plt.show()
Result:
Here what I made some hours ago:
First we import the needed libraries:
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import imageio
Secondly, we make the figures and stored them as png in our directory:
Note that I wrote range(0,330,20)
for i in range(0,330,20):
my_map = Basemap(projection='ortho', lat_0=0, lon_0=i, resolution='l', area_thresh=1000.0)
my_map.bluemarble()
my_map.etopo()
name=str(i)
path='/path/to/your/directory/'+name
plt.savefig(path+'.png')
plt.show()
plt.clf()
plt.cla()
plt.close()
And finally we can join all the images in an animated GIF:
images = []
for f in range(0,330,20):
images.append(imageio.imread("/path/to/your/directory/"+str(f)+".png"))
imageio.mimsave('movie.gif', images, duration=0.5)
and then enjoy the result:
I could imagine the following solution:
Using numpy.roll you could shift your array by one column (ore more) with each call. So you could load your image of the earth surface into a numpy array as a template and export the rotated image into a jpg. This you plot as shown in the warp example.
I have a while function that generates two lists of numbers and at the end I plot them using matplotlib.pyplot.
I'm doing
while True:
#....
plt.plot(list1)
plt.plot(list2)
plt.show()
But in order to see the progression I have to close the plot window.
Is there a way to refresh it with the new data every x seconds?
The most robust way to do what you want is to use matplotlib.animation. Here's an example of animating two lines, one representing sine and one representing cosine.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots()
sin_l, = ax.plot(np.sin(0))
cos_l, = ax.plot(np.cos(0))
ax.set_ylim(-1, 1)
ax.set_xlim(0, 5)
dx = 0.1
def update(i):
# i is a counter for each frame.
# We'll increment x by dx each frame.
x = np.arange(0, i) * dx
sin_l.set_data(x, np.sin(x))
cos_l.set_data(x, np.cos(x))
return sin_l, cos_l
ani = animation.FuncAnimation(fig, update, frames=51, interval=50)
plt.show()
For your particular example, you would get rid of the while True and put the logic inside that while loop in the update function. Then, you just have to make sure to do set_data instead of making a whole new plt.plot call.
More details can be found in this nice blog post, the animation API, or the animation examples.
I think what you're looking for is the "animation" feature.
Here is an example
This example is a second one.
I need to animate data as they come with a 2D histogram2d ( maybe later 3D but as I hear mayavi is better for that ).
Here's the code:
import numpy as np
import numpy.random
import matplotlib.pyplot as plt
import time, matplotlib
plt.ion()
# Generate some test data
x = np.random.randn(50)
y = np.random.randn(50)
heatmap, xedges, yedges = np.histogram2d(x, y, bins=5)
extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]
# start counting for FPS
tstart = time.time()
for i in range(10):
x = np.random.randn(50)
y = np.random.randn(50)
heatmap, xedges, yedges = np.histogram2d(x, y, bins=5)
plt.clf()
plt.imshow(heatmap, extent=extent)
plt.draw()
# calculate and print FPS
print 'FPS:' , 20/(time.time()-tstart)
It returns 3 fps, too slow apparently. Is it the use of the numpy.random in each iteration? Should I use blit? If so how?
The docs have some nice examples but for me I need to understand what everything does.
Thanks to #Chris I took a look at the examples again and also found this incredibly helpful post in here.
As #bmu states in he's answer (see post) using animation.FuncAnimation was the way for me.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
def generate_data():
# do calculations and stuff here
return # an array reshaped(cols,rows) you want the color map to be
def update(data):
mat.set_data(data)
return mat
def data_gen():
while True:
yield generate_data()
fig, ax = plt.subplots()
mat = ax.matshow(generate_data())
plt.colorbar(mat)
ani = animation.FuncAnimation(fig, update, data_gen, interval=500,
save_count=50)
plt.show()
I suspect it is the use of np.histogram2d in each loop iteration. or that in each loop iteration of the for loop you are clearing and drawing a new figure. To speed things up you should create a figure once and just update the properties and data of the figure in a loop. Have a look through the matplotlib animation examples for some pointers on how to do this. Typically it involves calling matplotlib.pyploy.plot then, in a loop, calling axes.set_xdata and axes.set_ydata.
In your case however, take a look at the matplotlib animation example dynamic image 2. In this example the generation of data is separated from the animation of the data (may not be a great approach if you have lots of data). By splitting these two parts up you can see which is causing a bottleneck, numpy.histrogram2d or imshow (use time.time() around each part).
P.s. np.random.randn is a psuedo-random number generator. These tend to be simple linear generators which can generate many millions of (psuedo-)random numbers per second, so this is almost certainly not your bottleneck - drawing to screen is almost always a slower process than any number crunching.
I need to generate a stack of 2D polar plots (a 3D cylindrical plot) so that I can view a distorted cylinder. I want to use matplotlib since I already have it installed and want to distribute my code to others who only have matplotlib. For example, say I have a bunch of 2-D arrays. Is there any way I can do this without having to download an external package? Here's my code.
#!usr/bin/env python
import matplotlib.pyplot as plt
import numpy as np
x = np.arange(-180.0,190.0,10)
theta = (np.pi/180.0 )*x # in radians
A0 = 55.0
offset = 60.0
R = [116.225,115.105,114.697,115.008,115.908,117.184,118.61,119.998,121.224,122.216,\
122.93,123.323,123.343,122.948,122.134,120.963,119.575,118.165,116.941,116.074,115.66\
,115.706,116.154,116.913,117.894,119.029,120.261,121.518,122.684,123.594,124.059,\
123.917,123.096,121.661,119.821,117.894,116.225]
fig = plt.figure()
ax = fig.add_axes([0.1,0.1,0.8,0.8],polar=True) # Polar plot
ax.plot(theta,R,lw=2.5)
ax.set_rmax(1.5*(A0)+offset)
plt.show()
I have 10 more similar 2D polar plots and I want to stack them up nicely. If there's any better way to visualize a distorted cylinder in 3D, I'm totally open to suggestions. Any help would be appreciated. Thanks!
If you want to stack polar charts using matplotlib, one approach is to use the Axes3D module. You'll notice that I used polar coordinates first and then converted them back to Cartesian when I was ready to plot them.
from numpy import *
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
n = 1000
fig = plt.figure()
ax = fig.gca(projection='3d')
for k in linspace(0, 5, 5):
THETA = linspace(0, 2*pi, n)
R = ones(THETA.shape)*cos(THETA*k)
# Convert to Cartesian coordinates
X = R*cos(THETA)
Y = R*sin(THETA)
ax.plot(X, Y, k-2)
plt.show()
If you play with the last argument of ax.plot, it controls the height of each slice. For example, if you want to project all of your data down to a single axis you would use ax.plot(X, Y, 0). For a more exotic example, you can map the height of the data onto a function, say a saddle ax.plot(X, Y, -X**2+Y**2 ). By playing with the colors as well, you could in theory represent multiple 4 dimensional datasets (though I'm not sure how clear this would be). Examples below: