Remove annotation while keeping plot matplotlib - python

I'm producing a series of scatterplots, where I keep most of the plot (besides the scatter plot) between each plot. This is done like so: Keeping map overlay between plots in matplotlib
Now I want to add annotation to the plot:
for j in range(len(n)):
plt.annotate(n[j], xy = (x[j],y[j]), color = "#ecf0f1", fontsize = 4)
However, this annotation stays on the plot between plots. How can I clear the annotation after each figure is saved?

You can remove an artist using remove().
ann = plt.annotate (...)
ann.remove()
After removal it may be necessary to redraw the canvas.
Here is a complete example, removing several annotations within an animation:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation
fig, ax = plt.subplots()
x = np.arange(0, 2*np.pi, 0.01)
f = lambda x: np.sin(x)
line, = ax.plot(x, f(x))
scat = plt.scatter([], [], s=20, alpha=1, color="purple", edgecolors='none')
ann_list = []
def animate(j):
for i, a in enumerate(ann_list):
a.remove()
ann_list[:] = []
n = np.random.rand(5)*6
scat.set_offsets([(r, f(r)) for r in n])
for j in range(len(n)):
ann = plt.annotate("{:.2f}".format(n[j]), xy = (n[j],f(n[j])), color = "purple", fontsize = 12)
ann_list.append(ann)
ani = matplotlib.animation.FuncAnimation(fig, animate, frames=20, interval=360)
ani.save(__file__+".gif",writer='imagemagick', fps=3)
plt.show()

Related

matplotlib: colorbar make subplots unequal size

I make two subplots with a common shared colorbar. So naturally I want to plot the colorbar only once.
However, when I do so, then my subplots become unequal in size.
How to place the colorbar outside the subplots on the right?
Minimal working example below
import numpy as np
from matplotlib import colors
import matplotlib.pyplot as plt
res = 100
x = np.linspace(0, 2*np.pi, res)
y = np.sin(x)
z = np.cos(x)
y2 = -np.sin(x)+0.4
z2 = 0.5*np.cos(2*x)
fig_width = 200/25.4
fig_height = 100/25.4
fig = plt.figure(figsize=(fig_width, fig_height))
gs = fig.add_gridspec(1, 2, wspace=0)
(ax, ax2) = gs.subplots(sharey='row')
images = []
images.append(ax.scatter(x, y, c=z))
images.append(ax2.scatter(x, y2, c=z2))
vmin = min(image.get_array().min() for image in images)
vmax = max(image.get_array().max() for image in images)
norm = colors.Normalize(vmin=vmin, vmax=vmax)
for im in images:
im.set_norm(norm)
cbar = fig.colorbar(images[0], ax=ax2)
cbar.set_label("mylabel", loc='top')
fig.tight_layout()
plt.show()
Try 1) pass the two axes as ax, and 2) move tight_layout before colorbar:
# other stuff
fig.tight_layout()
cbar = plt.colorbar(images[0], ax=(ax,ax2))
# other - other stuff
Output:

Changing graphs in real-time using matplotlib

This is the code.
import matplotlib.pyplot as plt
import random
x = []
y = []
for i in range(10):
x.append(i)
y.append(random.randint(0,100))
graph = plt.bar(x,y)
plt.show()
Whenever I change any value of y, say y[4] = 7, then I want that to be
reflected in the graph. I want that graph to move.
I tried searching the solution for this but none of them worked for me.
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import random
x = []
y = []
for i in range(10):
x.append(i)
y.append(random.randint(0,100))
fig, ax = plt.subplots()
bar, = ax.plot(x,y)
def animate(i):
x = []
y = []
for i in range(10):
x.append(i)
y.append(random.randint(0,100))
bar.set_xdata(x)
bar.set_ydata(y)
return bar,
animation = FuncAnimation(fig, animate, interval = 1000)
plt.show()
I want similar results, but in form of bar graph. Any help is appreciated.
The data displayed in the bar chart is not linked to the data in your list. There is no listener attached to the list that lets pyplot know when the list has been modified.
You will need to change the heights of the bars manually. You can do this by grabbing the children of your graph object, which is a list of bars, and updating the height of the bar.
Please note, the code below works because x and the indices of the bars are the same. If x started at 1 or a was range(0, 100, 10), the code gets more complicated.
import matplotlib.pyplot as plt
import random
# turn on interactive graphing
plt.ion()
x = []
y = []
for i in range(10):
x.append(i)
y.append(random.randint(0,100))
graph = plt.bar(x,y)
plt.show()
y[4] = 7
graph.get_children()[4].set_height(7)
Finally got what I wanted
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import random
X = []
Y = []
for i in range(20):
Y.append(random.randint(1, 100))
X.append(i)
fig, ax = plt.subplots(figsize=(10, 6))
ax.bar(X, Y, color="#ff7f7f")
ax.set_yticks([])
ax.set_xticks(X)
for number in range(len(Y)):
ax.text(number, Y[number], Y[number],
horizontalalignment='center', va="baseline", fontsize=13)
def draw_barchart(year):
ax.clear()
X.clear()
Y.clear()
for i in range(20):
Y.append(random.randint(1, 100))
X.append(i)
ax.bar(X, Y, color="#ff7f7f")
ax.set_yticks([])
ax.set_xticks(X)
ax.set_yticks([])
ax.set_xticks(X)
for number in range(len(Y)):
ax.text(number, Y[number], Y[number], horizontalalignment='center', va="baseline", fontsize=13)
plt.box(False)
animator = animation.FuncAnimation(fig, draw_barchart, interval=1000)
plt.show()

Matplotlib: How to combine scatter and line plot to one legend entry

I draw my data points with ax.scatter() and connect the data points with a fit using ax.plot().
How do I create a common entry in the legend that combines the marker for the data point with the line of the fit? I want to get a legend entry as I would get it for ax.plot(x, y, '-o', label = 'abc').
I have created the following minimal example:
import matplotlib.pyplot as plt
import numpy as np
x_scatter = np.linspace(0,10,10)
x_line = np.linspace(0,10,100)
fig, ax = plt.subplots()
for i in range(5):
ax.scatter(x_scatter, np.sin(x_scatter) + i, label = i)
ax.plot(x_line, np.sin(x_line)+i)
plt.legend(loc='best')
plt.show()
This 'hack' should work:
import matplotlib.pyplot as plt
import numpy as np
x_scatter = np.linspace(0,10,10)
x_line = np.linspace(0,10,100)
fig, ax = plt.subplots()
prop = ax._get_lines.prop_cycler
for i in range(5):
color = next(prop)['color']
ax.scatter(x_scatter, np.sin(x_scatter) + i, color=color)
ax.plot(x_line, np.sin(x_line)+i, color=color)
ax.plot([], [], '-o', color=color, label = i)
plt.legend(loc='best')
plt.show()

How do I get a fill_between shape in Funcanimation?

I would like to make a moving plot where the area under the curve gets colored while the curve is getting plotted.
I have googled a bit and found that I should somehow create a patch. However, I do not understand any of the examples they give, so let me ask it here with my specific example:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
import pylab as p
data = np.loadtext('datafile.dat', delimiter=',')
A = data[:,1]
B = data[:,2]
fig = plt.figure(figsize=(25,5), dpi=80)
ax = plt.axes(xlim=(0, 3.428), ylim=(-1,1))
line, = ax.plot([], [], lw=5)
def init():
line.set_data([], [])
return line,
def animate(i):
x = A[0:(i-1)*400]
y = B[0:(i-1)*400]
line.set_data(x,y)
# Here is the problem. I would now like to add the following line
# p.fill_between(x, 0, y, facecolor = 'C0', alpha = 0.2)
return line,
anim = animation.FuncAnimation(fig,animate, init_func=init, frames = 857, interval=20, blit=True)
I hope someone can give me a solution for my problem or at least point me in the correct direction.
So my question would be: how can I add the commented part without any errors?
Assuming you want blit = True, you will need to return the patch produced by fill_between as well.
p = plt.fill_between(x, y, 0)
return line, p,
Complete working example:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
X = np.linspace(0,3.428, num=250)
Y = np.sin(X*3)
fig = plt.figure(figsize=(13,5), dpi=80)
ax = plt.axes(xlim=(0, 3.428), ylim=(-1,1))
line, = ax.plot([], [], lw=5)
def init():
line.set_data([], [])
return line,
def animate(i):
x = X[0:(i-1)]
y = Y[0:(i-1)]
line.set_data(x,y)
p = plt.fill_between(x, y, 0, facecolor = 'C0', alpha = 0.2)
return line, p,
anim = animation.FuncAnimation(fig,animate, init_func=init,
frames = 250, interval=20, blit=True)
plt.show()

Python's matplotlib legend in separate axis with gridspec

Let suppose I have a matplotlib's gridspec instance in a python script. What I want to do is to create two axis and have the plot in one axis and the legend in the other one. Something like
import numpy as np
from matplotlib import gridspec, pyplot as plt
x = np.linspace(0,100)
y = np.sin(x)
gs = gridspec.GridSpec( 100, 100 )
ax1 = fig.add_subplot(gs[ :50, : ])
ax2 = fig.add_subplot(gs[ 55:, : ])
ax1.plot( s, y, label=r'sine' )
ax2.legend() # ?? Here I want legend of ax1
plt.show()
Is there any way of doing that?
You can grab the legend handles and labels from the first subplot using ax1.get_legend_handles_labels(), and then use them when you create the legend on the second subplot.
From the docs:
get_legend_handles_labels(legend_handler_map=None)
Return handles and labels for legend
ax.legend() is equivalent to:
h, l = ax.get_legend_handles_labels()
ax.legend(h, l)
import numpy as np
from matplotlib import gridspec, pyplot as plt
x = np.linspace(0, 100)
y = np.sin(x)
fig = plt.figure()
gs = gridspec.GridSpec(100, 100 )
ax1 = fig.add_subplot(gs[:50, :])
ax2 = fig.add_subplot(gs[55:, :])
ax1.plot(x, y, label=r'sine')
h, l = ax1.get_legend_handles_labels() # get labels and handles from ax1
ax2.legend(h, l) # use them to make legend on ax2
plt.show()

Categories