I'm making Terror Attacks analysis using Python. And I wanted make an animation. I made it but I have a problem the text above the animation overlaps in every frame. How can I fix it?
fig = plt.figure(figsize = (7,4))
def animate(Year):
ax = plt.axes()
ax.clear()
ax.set_title('Terrorism In Turkey\n'+ str(Year))
m5 = Basemap(projection='lcc',resolution='l' ,width=1800000, height=900000 ,lat_0=38.9637, lon_0=35.2433)
lat_gif=list(terror_turkey[terror_turkey['Year']==Year].Latitude)
long_gif=list(terror_turkey[terror_turkey['Year']==Year].Longitude)
x_gif,y_gif=m5(long_gif,lat_gif)
m5.scatter(x_gif, y_gif,s=[Death+Injured for Death,Injured in zip(terror_turkey[terror_turkey['Year']==Year].Death,terror_turkey[terror_turkey['Year']==Year].Injured)],color = 'r')
m5.drawcoastlines()
m5.drawcountries()
m5.fillcontinents(color='coral',lake_color='aqua', zorder = 1,alpha=0.4)
m5.drawmapboundary(fill_color='aqua')
ani = animation.FuncAnimation(fig,animate, list(terror_turkey.Year.unique()), interval = 1500)
ani.save('animation_tr.gif', writer='imagemagick', fps=1)
plt.close(1)
filename = 'animation_tr.gif'
video = io.open(filename, 'r+b').read()
encoded = base64.b64encode(video)
HTML(data='''<img src="data:image/gif;base64,{0}" type="gif" />'''.format(encoded.decode('ascii')))
Output:
#JohanC's recommendation in comments resolved my problem:
Did you consider creating the axes the usual way, as in fig, ax = plt.subplots(figsize = (7,4)) (in the main code, not inside the animate function)? And leaving out the call to plt.axes()?
Related
My script works, but when running I receive the error that is displayed in the heading. I do not understand why bc I use plt.clf() after every plot that I open and save. Below is an example of one of the instances I open (and close) a figure
...
roi1 = img[700:830, 730:835]
roiStats1(roi1)
plt.imshow(roi1)
plt.colorbar()
plt.savefig('roi1_'+file[:20]+'.tif')
plt.clf()
###### Make ROI sequential to be able to "bin" the axes #############
roi1y =roi1.reshape(13650, 1)
roi1y_df = pd.DataFrame(roi1y).reset_index().rename(columns= {0: 'Intensity'})
###### Plot Y axis variation of ROI for edge or lane gradient ###########
inlet_bins = np.linspace(-1, 13650, num=10, endpoint=True)
y_binlabels = [1,2,3,4,5,6,7,8,9]
roi1y_df['bins'] = pd.cut(roi1y_df['index'], inlet_bins, labels = y_binlabels)
roi1y_df['Intensity'] = roi1y_df['Intensity'].astype(float)
fig, ax = plt.subplots()
sns.boxplot(x='bins', y='Intensity', data=roi1y_df, ax = ax).set_title('Y-Axis Variation'+file[:20])
plt.savefig('YvarPlot_Inlet_'+file[:20]+'.tif')
plt.clf()
...
So I plt.clf() each time I open and save a figure, but I still get the memory warning
try plt.close() instead of plt.clf()
I want to draw a 3D volume using Matplotlib, slice by slice.
Mouse scroll to change the index. My program is given below:
#Mouse scroll event.
def mouse_scroll(event):
fig = event.canvas.figure
ax = fig.axes[0]
if event.button == 'down':
next_slice(ax)
fig.canvas.draw()
#Next slice func.
def previous_slice(ax):
volume = ax.volume
ax.index = (ax.index - 1) % volume.shape[0]
#ax.imshow(volume[ax.index])
ax.images[0].set_array(volume[ax.index])
Figure is initialized in the main function. like:
fig, ax = plt.subplots()
ax.volume = volume # volume is a 3D data, a 3d np array.
ax.index = 1
ax.imshow(volume[ax.index])
fig.canvas.mpl_connect('scroll_event', mouse_scroll)
Everything worked pretty well even I don't understand what is the ax.images. However, problem occurred when I replace the ax.volume with a new volume data. It suddenly stop to render! Debug into the code, the ax.image[0] is correctly set at each event callback.
But, if change the image set_array method to ax.show(). Figure begins to render again. But axes imshow function is really slow comparing to the ax.images[0].set_array() method.
How can I fix this problem? really want to use set_array() method. Thank you very much.
A simple executable script is attached.
plot.py#googledrive
You need to work on the same image all the time. Best give this a name
img = ax.imshow(volume[ax.index])
You can then set the data for it using set_data.
import numpy as np
import matplotlib.pyplot as plt
#Mouse scroll event.
def mouse_scroll(event):
fig = event.canvas.figure
ax = fig.axes[0]
if event.button == 'down':
next_slice(ax)
fig.canvas.draw()
#Next slice func.
def next_slice(ax):
volume = ax.volume
ax.index = (ax.index - 1) % volume.shape[0]
img.set_array(volume[ax.index])
def mouse_click(event):
fig = event.canvas.figure
ax = fig.axes[0]
volume = np.random.rand(10, 10, 10)
ax.volume = volume
ax.index = (ax.index - 1) % volume.shape[0]
img.set_array(volume[ax.index])
fig.canvas.draw_idle()
if __name__ == '__main__':
fig, ax = plt.subplots()
volume = np.random.rand(40, 40, 40)
ax.volume = volume # volume is a 3D data, a 3d np array.
ax.index = 1
img = ax.imshow(volume[ax.index])
fig.canvas.mpl_connect('scroll_event', mouse_scroll)
fig.canvas.mpl_connect('button_press_event', mouse_click)
plt.show()
Edit 3/15/2017 12:00 PM CDT: I have managed to fix the error in the program and complete the program as it was designed. I would like to thank berna1111 and TigerhawkT3 for their answer submissions, as they allowed me to complete this program. Thanks again, Stack Overflow!
I am attempting to save a series of array-built histograms (arrays made with numpy and histograms using matplotlib) to .png type files. I am receiving the following error message:
Traceback (most recent call last):
File "C:/Users/Ryan/PycharmProjects/NWS/weather_data.py", line 475, in <module>
figure1.savefig("{}_temperature.png".format(filename))
AttributeError: 'tuple' object has no attribute 'savefig'
The section the error refers to is below:
figure1 = plt.hist(temperature_graph_array, color="blue")
figure2 = plt.hist(feelslike_graph_array, color="blue")
figure3 = plt.hist(windspeed_graph_array, color="blue")
figure4 = plt.hist(windgustspeed_graph_array, color="blue")
figure5 = plt.hist(pressure_graph_array, color="blue")
figure6 = plt.hist(humidity_graph_array, color="blue")
figure1.savefig("{}_temperature.png".format(filename), format='png')
figure2.savefig("{}_feelslike.png".format(filename), format='png')
figure3.savefig("{}_windspeed.png".format(filename), format='png')
figure4.savefig("{}_windgustspeed.png".format(filename), format='png')
figure5.savefig("{}_pressure.png".format(filename), format='png')
figure6.savefig("{}_humidity.png".format(filename), format='png')
Why am I receiving this error, and how can I fix it? If someone could let me know I would greatly appreciate it.
Notes:
I have done some google searching and found a few similar errors, but none where the figure was interpreted as a tuple. I do not understand where the tuple part is coming from.
The "_graph_array" items in the histogram creation steps are arrays of dimensions 10 long, 1 tall. 10 total items inside, designated as type Float.
The "filename" variable in the saving step represents a string containing the date and time.
From the documentation for matplotlib.pyplot.hist:
The return value is a tuple (n, bins, patches) or ([n0, n1, ...], bins, [patches0, patches1,...]) if the input contains multiple data.
From the documentation for matplotlib.pyplot.savefig:
Save the current figure.
It looks like you should call savefig in the same way you call hist, not on the result of the hist call.
plt.savefig("{}_temperature.png".format(filename), format='png')
...
I've adapted your code and took the liberty to change the several lines creating a figure by list in comprehension of for loops:
import matplotlib.pyplot as plt
# should be equal when using .pylab
import numpy.random as rnd
# generate_data
n_points = 1000
temperature_graph_array = rnd.random(n_points)
feelslike_graph_array = rnd.random(n_points)
windspeed_graph_array = rnd.random(n_points)
windgustspeed_graph_array = rnd.random(n_points)
pressure_graph_array = rnd.random(n_points)
humidity_graph_array = rnd.random(n_points)
list_of_data = [temperature_graph_array,
feelslike_graph_array,
windspeed_graph_array,
windgustspeed_graph_array,
pressure_graph_array,
humidity_graph_array]
list_of_names = ['temperature',
'feelslike',
'windspeed',
'windgustspeed',
'pressure',
'humidity']
# create the figures:
#figure1 = plt.figure()
#figure2 = plt.figure()
#figure3 = plt.figure()
#figure4 = plt.figure()
#figure5 = plt.figure()
#figure6 = plt.figure()
#list_of_figs = [figure1, figure2, figure3, figure4, figure5, figure6]
## could be:
list_of_figs = [plt.figure() for i in range(6)]
# create the axis:
#ax1 = figure1.add_subplot(111)
#ax2 = figure2.add_subplot(111)
#ax3 = figure3.add_subplot(111)
#ax4 = figure4.add_subplot(111)
#ax5 = figure5.add_subplot(111)
#ax6 = figure6.add_subplot(111)
#list_of_axis = [ax1, ax2, ax3, ax4, ax5, ax6]
## could be:
list_of_axis = [fig.add_subplot(111) for fig in list_of_figs]
# plot the histograms
# notice `plt.hist` returns a tuple (n, bins, patches) or
# ([n0, n1, ...], bins, [patches0, patches1,...]) if the input
# contains multiple data
#hist1 = ax1.hist(temperature_graph_array, color="blue")
#hist2 = ax2.hist(feelslike_graph_array, color="blue")
#hist3 = ax3.hist(windspeed_graph_array, color="blue")
#hist4 = ax4.hist(windgustspeed_graph_array, color="blue")
#hist5 = ax5.hist(pressure_graph_array, color="blue")
#hist6 = ax6.hist(humidity_graph_array, color="blue")
#list_of_hists = [hist1, hist2, hist3, hist4, hist5, hist6]
## could be:
list_of_hists = []
for i, ax in enumerate(list_of_axis):
list_of_hists.append(ax.hist(list_of_data[i], color="blue"))
filename = 'output_graph'
for i, fig in enumerate(list_of_figs):
name = list_of_names[i].capitalize()
list_of_axis[i].set_title(name)
fig.tight_layout()
fig.savefig("{}_{}.png".format(filename,name), format='png')
Will not post the resulting figures, but this gives me 6 little .png files in the same folder as the script.
Even better, you can use a function to do all that to your data:
def save_hist(data, name, filename):
fig = plt.figure()
ax = fig.add_subplot(111)
ax.hist(data, color="blue")
ax.set_title(name)
fig.tight_layout()
fig.savefig("{}_{}.png".format(filename,name), format='png')
plt.close(fig)
filename = 'output_graph_2'
for data, name in zip(list_of_data, list_of_names):
save_hist(data, name, filename)
Here is my program in python and I am trying to save multiple plots in a single folder but it doesn't seem to work. How could I do this please?
for i in range(0:244):
plt.figure()
y = numpy.array(Data_EMG[i,:])
x = pylab.linspace(EMG_start, EMG_stop, Amount_samples)
plt.xlabel('Time(ms)')
plt.ylabel('EMG voltage(microV)')
pylab.plot(x, y)
pylab.show(block=True)
You can use the savefig function.
for i in range(0:244):
plt.figure()
y = numpy.array(Data_EMG[i,:])
x = pylab.linspace(EMG_start, EMG_stop, Amount_samples)
plt.xlabel('Time(ms)')
plt.ylabel('EMG voltage(microV)')
plt.savefig('EMG {0}.jpg'.format(i))
plt.close()
First of all check the identation. Hopefully your code actually reads
for i in range(0:244):
plt.figure()
y = numpy.array(Data_EMG[i,:])
x = pylab.linspace(EMG_start, EMG_stop, Amount_samples)
plt.xlabel('Time(ms)')
plt.ylabel('EMG voltage(microV)')
pylab.plot(x, y)
pylab.show(block=True)
At each iteration you completely generate a new figure. That´s very ineffective. Also you just plot your figure on the screen and not actually save it. Better is
from os import path
data = numpy.array(Data_EMG) # convert complete dataset into numpy-array
x = pylab.linspace(EMG_start, EMG_stop, Amount_samples) # doesn´t change in loop anyway
outpath = "path/of/your/folder/"
fig, ax = plt.subplots() # generate figure with axes
image, = ax.plot(x,data[0]) # initialize plot
ax.xlabel('Time(ms)')
ax.ylabel('EMG voltage(microV)')
plt.draw()
fig.savefig(path.join(outpath,"dataname_0.png")
for i in range(1, len(data)):
image.set_data(x,data[i])
plt.draw()
fig.savefig(path.join(outpath,"dataname_{0}.png".format(i))
Should be much faster.
I've got an animation with lines and now I want to label the points.
I tried plt.annotate() and I tried plt.text() but the labes don't move.
This is my example code:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
def update_line(num, data, line):
newData = np.array([[1+num,2+num/2,3,4-num/4,5+num],[7,4,9+num/3,2,3]])
line.set_data(newData)
plt.annotate('A0', xy=(newData[0][0],newData[1][0]))
return line,
fig1 = plt.figure()
data = np.array([[1,2,3,4,5],[7,4,9,2,3]])
l, = plt.plot([], [], 'r-')
plt.xlim(0, 20)
plt.ylim(0, 20)
plt.annotate('A0', xy=(data[0][0], data[1][0]))
# plt.text( data[0][0], data[1][0], 'A0')
line_ani = animation.FuncAnimation(fig1, update_line, 25, fargs=(data, l),
interval=200, blit=True)
plt.show()
Can you help me please?
My next step is:
I have vectors with origin in these Points. These vectors change their length and their direction in each animation step.
How can I animate these?
Without animation this works:
soa =np.array( [ [data[0][0],data[1][0],F_A0[i][0][0],F_A0[i][1][0]],
[data[0][1],data[1][1],F_B0[i][0][0],F_B0[i][1][0]],
[data[0][2],data[1][2],F_D[i][0][0],F_D[i][1][0]] ])
X,Y,U,V = zip(*soa)
ax = plt.gca()
ax.quiver(X,Y,U,V,angles='xy',scale_units='xy',scale=1)
First thanks a lot for your fast and very helpful answer!
My Vector animation problem I have solved with this:
annotation = ax.annotate("C0", xy=(data[0][2], data[1][2]), xycoords='data',
xytext=(data[0][2]+1, data[1][2]+1), textcoords='data',
arrowprops=dict(arrowstyle="->"))
and in the 'update-function' I write:
annotation.xytext = (newData[0][2], newData[1][2])
annotation.xy = (data[0][2]+num, data[1][2]+num)
to change the start and end position of the vectors (arrows).
But what is, wehn I have 100 vectors or more? It is not practicable to write:
annotation1 = ...
annotation2 = ...
.
:
annotation100 = ...
I tried with a list:
...
annotation = [annotation1, annotation2, ... , annotation100]
...
def update(num):
...
return line, annotation
and got this error:
AttributeError: 'list' object has no attribute 'axes'
What can I do? Have you any idea?
I'm coming here from this question, where an annotation should be updated that uses both xy and xytext. It appears that, in order to update the annotation correctly, one needs to set the attribute .xy of the annotation to set the position of the annotated point and to use the .set_position() method of the annotation to set the position of the annotation. Setting the .xytext attribute has no effect -- somewhat confusing in my opinion. Below a complete example:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
fig, ax = plt.subplots()
ax.set_xlim([-1,1])
ax.set_ylim([-1,1])
L = 50
theta = np.linspace(0,2*np.pi,L)
r = np.ones_like(theta)
x = r*np.cos(theta)
y = r*np.sin(theta)
line, = ax.plot(1,0, 'ro')
annotation = ax.annotate(
'annotation', xy=(1,0), xytext=(-1,0),
arrowprops = {'arrowstyle': "->"}
)
def update(i):
new_x = x[i%L]
new_y = y[i%L]
line.set_data(new_x,new_y)
##annotation.xytext = (-new_x,-new_y) <-- does not work
annotation.set_position((-new_x,-new_y))
annotation.xy = (new_x,new_y)
return line, annotation
ani = animation.FuncAnimation(
fig, update, interval = 500, blit = False
)
plt.show()
The result looks something like this:
In case that versions matter, this code has been tested on Python 2.7 and 3.6 with matplotlib version 2.1.1, and in both cases setting .xytext had no effect, while .set_position() and .xy worked as expected.
You have the return all objects that changed from your update function. So since your annotation changed it's position you should return it also:
line.set_data(newData)
annotation = plt.annotate('A0', xy=(newData[0][0],newData[1][0]))
return line, annotation
You can read more about matplotlib animations in this tutorial
You should also specify the init function so that the FuncAnimation knows which elements to remove from the plot when redrawing on the first update. So the full example would be:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
# Create initial data
data = np.array([[1,2,3,4,5], [7,4,9,2,3]])
# Create figure and axes
fig = plt.figure()
ax = plt.axes(xlim=(0, 20), ylim=(0, 20))
# Create initial objects
line, = ax.plot([], [], 'r-')
annotation = ax.annotate('A0', xy=(data[0][0], data[1][0]))
annotation.set_animated(True)
# Create the init function that returns the objects
# that will change during the animation process
def init():
return line, annotation
# Create the update function that returns all the
# objects that have changed
def update(num):
newData = np.array([[1 + num, 2 + num / 2, 3, 4 - num / 4, 5 + num],
[7, 4, 9 + num / 3, 2, 3]])
line.set_data(newData)
# This is not working i 1.2.1
# annotation.set_position((newData[0][0], newData[1][0]))
annotation.xytext = (newData[0][0], newData[1][0])
return line, annotation
anim = animation.FuncAnimation(fig, update, frames=25, init_func=init,
interval=200, blit=True)
plt.show()
I think I figured out how to animate multiple annotations through a list. First you just create your annotations list:
for i in range(0,len(someMatrix)):
annotations.append(ax.annotate(str(i), xy=(someMatrix.item(0,i), someMatrix.item(1,i))))
Then in your "animate" function you do as you have already written:
for num, annot in enumerate(annotations):
annot.set_position((someMatrix.item((time,num)), someMatrix.item((time,num))))
(You can write it as a traditional for loop as well if you don't like the enumerate way). Don't forget to return the whole annotations list in your return statement.
Then the important thing is to set "blit=False" in your FuncAnimation:
animation.FuncAnimation(fig, animate, frames="yourframecount",
interval="yourpreferredinterval", blit=False, init_func=init)
It is good to point out that blit=False might slow things down. But its unfortunately the only way I could get animation of annotations in lists to work...