The following code :
import matplotlib.pyplot as plt
import numpy as np
def autopct_format(values):
def my_format(pct):
total = sum(values)
val = int(round(pct*total/100.0)/1000000)
if val == 0:
return ""
else:
return '{:.1f}%\n({v:,d})'.format(pct, v=val)
return my_format
fig, ax = plt.subplots(1,1,figsize=(10,10),dpi=100,layout="constrained")
ax.axis('equal')
width = 0.3
#Color
A, B, C=[plt.cm.Blues, plt.cm.Reds, plt.cm.Greens]
#OUTSIDE
cin = [A(0.5),A(0.4),A(0.3),B(0.5),B(0.4),B(0.3),B(0.2),B(0.1), C(0.5),C(0.4),C(0.3)]
Labels_Smalls = ['groupA', 'groupB', 'groupC']
labels = ['A.1', 'A.2', 'A.3', 'B.1', 'B.2', 'C.1', 'C.2', 'C.3',
'C.4', 'C.5']
Sizes_Detail = [4,3,5,6,5,10,5,5,4,6]
Sizes = [12,11,30]
pie2, _ ,junk = ax.pie(Sizes_Detail ,radius=1,
labels=labels,labeldistance=0.85,
autopct=autopct_format(Sizes_Detail) ,pctdistance = 1.15,
colors=cin)
for ea, eb in zip(pie2, _):
mang =(ea.theta1 + ea.theta2)/2
tourner = 360 - mang
eb.set_rotation(mang+tourner) # rotate the label by (mean_angle + 270)
eb.set_va("center")
eb.set_ha("center")
plt.setp(pie2, width=width, edgecolor='white')
#INSIDE
pie, _, junk = ax.pie(Sizes, radius=1-width,
autopct=autopct_format(Sizes) ,pctdistance = 0.8,
colors = [A(0.6), B(0.6), C(0.6)])
plt.setp(pie, width=width, edgecolor='white')
plt.margins(0,0)
bbox_props = dict(boxstyle="square,pad=0.3", fc="w", ec="k", lw=0.72)
kw = dict(arrowprops=dict(arrowstyle="-"),
bbox=bbox_props, zorder=3, va="center")
for i, p in enumerate(pie):
ang = (p.theta2 - p.theta1)/2. + p.theta1
y = np.sin(np.deg2rad(ang))
x = np.cos(np.deg2rad(ang))
horizontalalignment = {-1: "right", 1: "left"}[int(np.sign(x))]
connectionstyle = "angle,angleA=0,angleB={}".format(ang)
kw["arrowprops"].update({"connectionstyle": connectionstyle})
ax.annotate(Labels_Smalls[i], xy=(x, y), xytext=(1.35*np.sign(x), 1.4*y),
horizontalalignment=horizontalalignment, **kw)
for ea, eb in zip(pie, _):
mang =(ea.theta1 + ea.theta2)/2. # get mean_angle of the wedge
#print(mang, eb.get_rotation())
tourner = 360 - mang
eb.set_rotation(mang+tourner) # rotate the label by (mean_angle + 270)
eb.set_va("center")
eb.set_ha("center")
gives the following output :
But It does only link the outside pie and not the inside one, so how would I do the same chart but with the arrow linking the inside pie to the bbox ?
Related
I need help to generate this graph, especially with domains limits
and the arrow indicating the domain
all I can do is generate the domains name but not the limits and arrow
The following code will produce something like what you require:
from matplotlib import pyplot as plt
from matplotlib.patches import Wedge
import numpy as np
labels = ["Obésité\nmassive", "Obésité", "Surpoids", "Normal", "Maigreur"]
innerlabels = [">40", "30 à 40", "25 à 30", "18,5 à 25", "< 18,5"]
colours = ["red", "darkorange", "orange", "green", "blue"]
fig, ax = plt.subplots(figsize=(6, 6), dpi=200)
theta = 0
dtheta = 180 / len(labels)
width = 0.35
def pol2cart(rho, phi):
x = rho * np.cos(phi)
y = rho * np.sin(phi)
return(x, y)
patches = []
for i in range(len(labels)):
# outer wedge
wedge = Wedge(0, r=1, width=width, theta1=theta, theta2=(theta + dtheta), fc=colours[i], alpha=0.6, edgecolor="whitesmoke")
ax.add_patch(wedge)
# inner wedge
wedge = Wedge(0, r=1 - width, width=width, theta1=theta, theta2=(theta + dtheta), fc=colours[i], edgecolor="whitesmoke")
ax.add_patch(wedge)
theta += dtheta
# add text label
tr = 1 - (width / 2)
ta = theta - dtheta / 2
x, y = pol2cart(tr, np.deg2rad(ta))
textangle = -np.fmod(90 - ta, 180)
ax.text(x, y, labels[i], rotation=textangle, va="center", ha="center", color="white", fontweight="bold")
# inner labels
tr = (1 - width) - (width / 2)
x, y = pol2cart(tr, np.deg2rad(ta))
textangle = -np.fmod(90 - ta, 180)
ax.text(x, y, innerlabels[i], rotation=textangle, va="center", ha="center", color="white")
ax.set_xlim([-1, 1])
ax.set_ylim([0, 1])
ax.set_axis_off()
ax.set_aspect("equal")
def bmiposition(bmi):
"""
Get angular position of BMI arrow.
"""
from scipy.interpolate import interp1d
bmiranges = [(0, 18.5), (18.5, 25), (25, 30), (30, 40), (40, 80)]
angrange = [(180 - dtheta * i, 180 - dtheta * (i + 1)) for i in range(len(bmiranges))]
interpfuncs = []
for i in range(len(bmiranges)):
interpfuncs.append(interp1d(bmiranges[i], angrange[i], kind="linear"))
bmiang = np.piecewise(
bmi,
[bmiranges[i][0] < bmi <= bmiranges[i][1] for i in range(len(bmiranges))],
interpfuncs,
)
return bmiang
bmi = 22.5 # set BMI
# add arrow
pos = bmiposition(bmi) # get BMI angle
x, y = pol2cart(0.25, np.deg2rad(pos))
ax.arrow(0, 0, x, y, head_length=0.125, width=0.025, fc="k")
ax.plot(0, 0, 'ko', ms=10) # circle at origin
giving:
I was trying to animate arrow and used the script from the following answer. Plus my animation has scatter-points and text too.
This is the script that I am using:-
from my_func import Pitch
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
## Pitch for plotting the football-pitch in the background
pitch = Pitch(line_color='grey', pitch_color='#121212', orientation='horizontal')
fig, ax = pitch.create_pitch()
x_start, y_start = (50, 35)
x_end, y_end = (90, 45)
x_1, y_1, x_2, y_2 = 50.55, 35.1375, 89.45, 44.8625
x = np.linspace(x_1, x_2, 20)
y = np.linspace(y_1, y_2, 20)
sc_1 = ax.scatter([], [], color="crimson", zorder=4, s=150, alpha=0.7, edgecolor="w")
sc_2 = ax.scatter([], [], color="crimson", zorder=4, s=150, alpha=0.7, edgecolor="w")
title = ax.text(50, 65, "", bbox={'facecolor':'w', 'alpha':0.5, 'pad':5}, ha="center")
def animate(i):
if i == 1:
sc_1.set_offsets([x_start, y_start])
title.set_text("Start of Action 01")
if i == 2:
plt.pause(0.5)
title.set_text("Action 02")
## plot arrow
## haven't included ax.cla() as it was removing the pitch in the background
if i <= len(x):
patch = plt.Arrow(x_start, y_start, x[i] - x_start, y[i] - y_start)
ax.add_patch(patch)
## plot scatter point
if i > len(x):
plt.pause(0.2)
title.set_text("Action 03")
sc_2.set_offsets([x_end, y_end])
return sc_1, patch, sc_2, title,
ani = animation.FuncAnimation(
fig=fig, func=animate, interval=50, blit=True)
plt.show()
The result is showing a little bit of animation and then giving me the following error:
File "test.py", line 33, in animate
patch = plt.Arrow(x_start, y_start, x[i] - x_start, y[i] - y_start)
IndexError: index 20 is out of bounds for axis 0 with size 20
[1] 14116 abort (core dumped) python test.py
I am a begineer in matplotlib animations and don't know how to solve this error what should I change in my code to remove the error and generate the animated output.
The modifications are as follows: 1) plt.axes() is added to ax. 2) The return value of the animation function is set to ax. 3) The number of objects to be animated is 20, so frames=20 is used.
# from my_func import Pitch
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
## Pitch for plotting the football-pitch in the background
# pitch = Pitch(line_color='grey', pitch_color='#121212', orientation='horizontal')
# fig, ax = pitch.create_pitch()
fig,ax = plt.subplots()
x_start, y_start = 50, 35
x_end, y_end = 90, 45
x_1, y_1, x_2, y_2 = 50.55, 35.1375, 89.45, 44.8625
x = np.linspace(x_1, x_2, 20)
y = np.linspace(y_1, y_2, 20)
ax = plt.axes(xlim=(50, 90), ylim=(35, 45))
sc_1 = ax.scatter([], [], color="crimson", zorder=1, s=150, alpha=0.7, edgecolor="w")
sc_2 = ax.scatter([], [], color="crimson", zorder=1, s=150, alpha=0.7, edgecolor="w")
title = ax.text(50, 65, "", bbox={'facecolor':'w', 'alpha':0.5, 'pad':5}, ha="center")
def animate(i):
if i == 1:
sc_1.set_offsets([x_start, y_start])
title.set_text("Start of Action 01")
if i == 2:
plt.pause(0.5)
title.set_text("Action 02")
## plot arrow
## haven't included ax.cla() as it was removing the pitch in the background
if i <= len(x):
ax.clear()
#patch = plt.Arrow(x_start, y_start, x[i] - x_start, y[i] - y_start)
#ax.add_patch(patch)
ax.arrow(x_start, y_start, x[i]-x_start, y[i]-y_start, head_width=1, head_length=1, fc='black', ec='black')
ax.set(xlim=(50,90), ylim=(35,45))
## plot scatter point
if i == len(x)-1:
plt.pause(0.2)
title.set_text("Action 03")
sc_2.set_offsets([x_end, y_end])
return sc_1, sc_2, title, ax
ani = animation.FuncAnimation(fig=fig, func=animate, frames=20, interval=200, repeat=False, blit=True)
plt.show()
The code below animates a bar chart and associated label values. The issue I'm having is positioning the label when the integer is negative. Specifically, I want the label to be positioned on top of the bar, not inside it. It's working for the first frame but the subsequent frames of animation revert back to plotting the label inside the bar chart for negative integers.
def autolabel(rects, ax):
# Get y-axis height to calculate label position from.
ts = []
(y_bottom, y_top) = ax.get_ylim()
y_height = y_top - y_bottom
for rect in rects:
height = 0
if rect.get_y() < 0:
height = rect.get_y()
else:
height = rect.get_height()
p_height = (height / y_height)
if p_height > 0.95:
label_position = height - (y_height * 0.05) if (height > -0.01) else height + (y_height * 0.05)
else:
label_position = height + (y_height * 0.01) if (height > -0.01) else height - (y_height * 0.05)
t = ax.text(rect.get_x() + rect.get_width() / 2., label_position,
'%d' % int(height),
ha='center', va='bottom')
ts.append(t)
return ts
def gradientbars(bars, ax, cmap, vmin, vmax):
g = np.linspace(vmin,vmax,100)
grad = np.vstack([g,g]).T
xmin,xmax = ax.get_xlim()
ymin,ymax = ax.get_ylim()
ims = []
for bar in bars:
bar.set_facecolor('none')
im = ax.imshow(grad, aspect="auto", zorder=0, cmap=cmap, vmin=vmin, vmax=vmax, extent=(xmin,xmax,ymin,ymax))
im.set_clip_path(bar)
ims.append(im)
return ims
vmin = -6
vmax = 6
cmap = 'PRGn'
data = np.random.randint(-5,5, size=(10, 4))
x = [chr(ord('A')+i) for i in range(4)]
fig, ax = plt.subplots()
ax.grid(False)
ax.set_ylim(vmin, vmax)
rects = ax.bar(x,data[0])
labels = autolabel(rects, ax)
imgs = gradientbars(rects, ax, cmap=cmap, vmin=vmin, vmax=vmax)
def animate(i):
for rect,label,img,yi in zip(rects, labels, imgs, data[i]):
rect.set_height(yi)
label.set_text('%d'%int(yi))
label.set_y(yi)
img.set_clip_path(rect)
anim = animation.FuncAnimation(fig, animate, frames = len(data), interval = 500)
plt.show()
It's working for the first frame.
You call autolabel(rects, ax) in the first plot, so the label is well placed.
The subsequent frames of animation revert back to plotting the label inside the bar chart for negative integers.
The label position of subsequent frames is set by label.set_y(yi). yi is from data[i], you didn't consider the negative value here.
I create a function named get_label_position(height) to calculate the right label position for give height. It uses a global variable y_height. And call this function before label.set_y().
import matplotlib.pyplot as plt
from matplotlib import animation
import pandas as pd
import numpy as np
def get_label_position(height):
p_height = (height / y_height)
label_position = 0
if p_height > 0.95:
label_position = height - (y_height * 0.05) if (height > -0.01) else height + (y_height * 0.05)
else:
label_position = height + (y_height * 0.01) if (height > -0.01) else height - (y_height * 0.05)
return label_position
def autolabel(rects, ax):
# Get y-axis height to calculate label position from.
ts = []
(y_bottom, y_top) = ax.get_ylim()
y_height = y_top - y_bottom
for rect in rects:
height = 0
if rect.get_y() < 0:
height = rect.get_y()
else:
height = rect.get_height()
p_height = (height / y_height)
if p_height > 0.95:
label_position = height - (y_height * 0.05) if (height > -0.01) else height + (y_height * 0.05)
else:
label_position = height + (y_height * 0.01) if (height > -0.01) else height - (y_height * 0.05)
t = ax.text(rect.get_x() + rect.get_width() / 2., label_position,
'%d' % int(height),
ha='center', va='bottom')
ts.append(t)
return ts
def gradientbars(bars, ax, cmap, vmin, vmax):
g = np.linspace(vmin,vmax,100)
grad = np.vstack([g,g]).T
xmin,xmax = ax.get_xlim()
ymin,ymax = ax.get_ylim()
ims = []
for bar in bars:
bar.set_facecolor('none')
im = ax.imshow(grad, aspect="auto", zorder=0, cmap=cmap, vmin=vmin, vmax=vmax, extent=(xmin,xmax,ymin,ymax))
im.set_clip_path(bar)
ims.append(im)
return ims
vmin = -6
vmax = 6
cmap = 'PRGn'
data = np.random.randint(-5,5, size=(10, 4))
x = [chr(ord('A')+i) for i in range(4)]
fig, ax = plt.subplots()
ax.grid(False)
ax.set_ylim(vmin, vmax)
rects = ax.bar(x,data[0])
labels = autolabel(rects, ax)
imgs = gradientbars(rects, ax, cmap=cmap, vmin=vmin, vmax=vmax)
(y_bottom, y_top) = ax.get_ylim()
y_height = y_top - y_bottom
def animate(i):
for rect,label,img,yi in zip(rects, labels, imgs, data[i]):
rect.set_height(yi)
label.set_text('%d'%int(yi))
label.set_y(get_label_position(yi))
img.set_clip_path(rect)
anim = animation.FuncAnimation(fig, animate, frames = len(data), interval = 500)
plt.show()
I have a program with an interactive figure where occasionally many artists are drawn. In this figure, you can also zoom and pan using the mouse. However, the performace during zooming an panning is not very good because every artist is always redrawn. Is there a way to check which artists are in the currently displayed area and only redraw those? (In the example below the perfomace is still relatively good, but it can be made arbitrarily worse by using more or more complex artists)
I had a similar performace problem with the hover method that whenever it was called it ran canvas.draw() at the end. But as you can see I found a neat workaround for that by making use of caching and restoring the background of the axes (based on this). This significantly improved the performace and now even with many artists it runs very smooth. Maybe there is a similar way of doing this but for the pan and zoom method?
Sorry for the long code sample, most of it is not directly relevant for the question but necessary for a working example to highlight the issue.
EDIT
I updated the MWE to something that is more representative of my actual code.
import numpy as np
import sys
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import \
FigureCanvasQTAgg
import matplotlib.patheffects as PathEffects
from matplotlib.text import Annotation
from matplotlib.collections import LineCollection
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QDialog
def check_limits(base_xlim, base_ylim, new_xlim, new_ylim):
if new_xlim[0] < base_xlim[0]:
overlap = base_xlim[0] - new_xlim[0]
new_xlim[0] = base_xlim[0]
if new_xlim[1] + overlap > base_xlim[1]:
new_xlim[1] = base_xlim[1]
else:
new_xlim[1] += overlap
if new_xlim[1] > base_xlim[1]:
overlap = new_xlim[1] - base_xlim[1]
new_xlim[1] = base_xlim[1]
if new_xlim[0] - overlap < base_xlim[0]:
new_xlim[0] = base_xlim[0]
else:
new_xlim[0] -= overlap
if new_ylim[1] < base_ylim[1]:
overlap = base_ylim[1] - new_ylim[1]
new_ylim[1] = base_ylim[1]
if new_ylim[0] + overlap > base_ylim[0]:
new_ylim[0] = base_ylim[0]
else:
new_ylim[0] += overlap
if new_ylim[0] > base_ylim[0]:
overlap = new_ylim[0] - base_ylim[0]
new_ylim[0] = base_ylim[0]
if new_ylim[1] - overlap < base_ylim[1]:
new_ylim[1] = base_ylim[1]
else:
new_ylim[1] -= overlap
return new_xlim, new_ylim
class FigureCanvas(FigureCanvasQTAgg):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.bg_cache = None
def draw(self):
ax = self.figure.axes[0]
hid_annotation = False
if ax.annot.get_visible():
ax.annot.set_visible(False)
hid_annotation = True
hid_highlight = False
if ax.last_artist:
ax.last_artist.set_path_effects([PathEffects.Normal()])
hid_highlight = True
super().draw()
self.bg_cache = self.copy_from_bbox(self.figure.bbox)
if hid_highlight:
ax.last_artist.set_path_effects(
[PathEffects.withStroke(
linewidth=7, foreground="c", alpha=0.4
)]
)
ax.draw_artist(ax.last_artist)
if hid_annotation:
ax.annot.set_visible(True)
ax.draw_artist(ax.annot)
if hid_highlight:
self.update()
def position(t_, coeff, var=0.1):
x_ = np.random.normal(np.polyval(coeff[:, 0], t_), var)
y_ = np.random.normal(np.polyval(coeff[:, 1], t_), var)
return x_, y_
class Data:
def __init__(self, times):
self.length = np.random.randint(1, 20)
self.t = np.sort(
np.random.choice(times, size=self.length, replace=False)
)
self.vel = [np.random.uniform(-2, 2), np.random.uniform(-2, 2)]
self.accel = [np.random.uniform(-0.01, 0.01), np.random.uniform(-0.01,
0.01)]
x0, y0 = np.random.uniform(0, 1000, 2)
self.x, self.y = position(
self.t, np.array([self.accel, self.vel, [x0, y0]])
)
class Test(QDialog):
def __init__(self):
super().__init__()
self.fig, self.ax = plt.subplots()
self.canvas = FigureCanvas(self.fig)
self.artists = []
self.zoom_factor = 1.5
self.x_press = None
self.y_press = None
self.annot = Annotation(
"", xy=(0, 0), xytext=(-20, 20), textcoords="offset points",
bbox=dict(boxstyle="round", fc="w", alpha=0.7), color='black',
arrowprops=dict(arrowstyle="->"), zorder=6, visible=False,
annotation_clip=False, in_layout=False,
)
self.annot.set_clip_on(False)
setattr(self.ax, 'annot', self.annot)
self.ax.add_artist(self.annot)
self.last_artist = None
setattr(self.ax, 'last_artist', self.last_artist)
self.image = np.random.uniform(0, 100, 1000000).reshape((1000, 1000))
self.ax.imshow(self.image, cmap='gray', interpolation='nearest')
self.times = np.linspace(0, 20)
for i in range(1000):
data = Data(self.times)
points = np.array([data.x, data.y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
z = np.linspace(0, 1, data.length)
norm = plt.Normalize(z.min(), z.max())
lc = LineCollection(
segments, cmap='autumn', norm=norm, alpha=1,
linewidths=2, picker=8, capstyle='round',
joinstyle='round'
)
setattr(lc, 'data_id', i)
lc.set_array(z)
self.ax.add_artist(lc)
self.artists.append(lc)
self.default_xlim = self.ax.get_xlim()
self.default_ylim = self.ax.get_ylim()
self.canvas.draw()
self.cid_motion = self.fig.canvas.mpl_connect(
'motion_notify_event', self.motion_event
)
self.cid_button = self.fig.canvas.mpl_connect(
'button_press_event', self.pan_press
)
self.cid_zoom = self.fig.canvas.mpl_connect(
'scroll_event', self.zoom
)
layout = QVBoxLayout()
layout.addWidget(self.canvas)
self.setLayout(layout)
def zoom(self, event):
if event.inaxes == self.ax:
scale_factor = np.power(self.zoom_factor, -event.step)
xdata = event.xdata
ydata = event.ydata
cur_xlim = self.ax.get_xlim()
cur_ylim = self.ax.get_ylim()
x_left = xdata - cur_xlim[0]
x_right = cur_xlim[1] - xdata
y_top = ydata - cur_ylim[0]
y_bottom = cur_ylim[1] - ydata
new_xlim = [
xdata - x_left * scale_factor, xdata + x_right * scale_factor
]
new_ylim = [
ydata - y_top * scale_factor, ydata + y_bottom * scale_factor
]
# intercept new plot parameters if they are out of bounds
new_xlim, new_ylim = check_limits(
self.default_xlim, self.default_ylim, new_xlim, new_ylim
)
if cur_xlim != tuple(new_xlim) or cur_ylim != tuple(new_ylim):
self.ax.set_xlim(new_xlim)
self.ax.set_ylim(new_ylim)
self.canvas.draw_idle()
def motion_event(self, event):
if event.button == 1:
self.pan_move(event)
else:
self.hover(event)
def pan_press(self, event):
if event.inaxes == self.ax:
self.x_press = event.xdata
self.y_press = event.ydata
def pan_move(self, event):
if event.inaxes == self.ax:
xdata = event.xdata
ydata = event.ydata
cur_xlim = self.ax.get_xlim()
cur_ylim = self.ax.get_ylim()
dx = xdata - self.x_press
dy = ydata - self.y_press
new_xlim = [cur_xlim[0] - dx, cur_xlim[1] - dx]
new_ylim = [cur_ylim[0] - dy, cur_ylim[1] - dy]
# intercept new plot parameters that are out of bound
new_xlim, new_ylim = check_limits(
self.default_xlim, self.default_ylim, new_xlim, new_ylim
)
if cur_xlim != tuple(new_xlim) or cur_ylim != tuple(new_ylim):
self.ax.set_xlim(new_xlim)
self.ax.set_ylim(new_ylim)
self.canvas.draw_idle()
def update_annot(self, event, artist):
self.ax.annot.xy = (event.xdata, event.ydata)
text = f'Data #{artist.data_id}'
self.ax.annot.set_text(text)
self.ax.annot.set_visible(True)
self.ax.draw_artist(self.ax.annot)
def hover(self, event):
vis = self.ax.annot.get_visible()
if event.inaxes == self.ax:
ind = 0
cont = None
while (
ind in range(len(self.artists))
and not cont
):
artist = self.artists[ind]
cont, _ = artist.contains(event)
if cont and artist is not self.ax.last_artist:
if self.ax.last_artist is not None:
self.canvas.restore_region(self.canvas.bg_cache)
self.ax.last_artist.set_path_effects(
[PathEffects.Normal()]
)
self.ax.last_artist = None
artist.set_path_effects(
[PathEffects.withStroke(
linewidth=7, foreground="c", alpha=0.4
)]
)
self.ax.last_artist = artist
self.ax.draw_artist(self.ax.last_artist)
self.update_annot(event, self.ax.last_artist)
ind += 1
if vis and not cont and self.ax.last_artist:
self.canvas.restore_region(self.canvas.bg_cache)
self.ax.last_artist.set_path_effects([PathEffects.Normal()])
self.ax.last_artist = None
self.ax.annot.set_visible(False)
elif vis:
self.canvas.restore_region(self.canvas.bg_cache)
self.ax.last_artist.set_path_effects([PathEffects.Normal()])
self.ax.last_artist = None
self.ax.annot.set_visible(False)
self.canvas.update()
self.canvas.flush_events()
if __name__ == '__main__':
app = QApplication(sys.argv)
test = Test()
test.show()
sys.exit(app.exec_())
You can find which artists are in the current area of the axes if you focus on the data the artists are plotting.
For example if you put your points data (a and b arrays) in a numpy array like this:
self.points = np.random.randint(0, 100, (1000, 2))
you can get the list of points inside the current x and y limits:
xmin, xmax = self.ax.get_xlim()
ymin, ymax = self.ax.get_ylim()
p = self.points
indices_of_visible_points = (np.argwhere((p[:, 0] > xmin) & (p[:, 0] < xmax) & (p[:, 1] > ymin) & (p[:, 1] < ymax))).flatten()
you can use indices_of_visible_points to index your related self.artists list
I would like to do an animation in python representing each point, dot by dot. I am doing it as I always have done it, but it does not work. Any help?
I tried 2 different ways.
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
%matplotlib nbagg
def mcd(a, b):
resto = 0
while(b > 0):
resto = b
b = a % b
a = resto
return a
N = 1200
n = list (range (N))
an = [1,1]
for i in range (2,N):
k = i-1
if mcd (n[i], an[k]) == 1:
an.append (n[i] + 1 + an[k])
else:
an.append (an[k]/mcd (n[i], an[k]))
fig = plt.figure ()
ax = fig.add_subplot (111)
ax.grid (True)
ax.set_xlim(0, N*1.1)
pt, = ax.plot ([],[],'ko', markersize=2)
ax.plot (n,an, 'ko', markersize=2)
def init ():
pt.set_data([],[])
return (pt)
def animate (i,pt):
pt.set_data (n[:i],an[:i])
return (pt)
ani = FuncAnimation (fig, animate, fargs = (pt), frames=N, init_func=init, interval=50, blit = True)
plt.show ()
And the second way:
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
%matplotlib nbagg
def mcd(a, b):
resto = 0
while(b > 0):
resto = b
b = a % b
a = resto
return a
N = 1200
n = list (range (N))
an = [1,1]
for i in range (2,N):
k = i-1
if mcd (n[i], an[k]) == 1:
an.append (n[i] + 1 + an[k])
else:
an.append (an[k]/mcd (n[i], an[k]))
xdata, ydata = [],[]
fig = plt.figure ()
ax = fig.add_subplot(111)
ax.grid (True)
pt, = ax.plot ([],[],'ko', markersize=2)
ax.plot (n,an, 'ko', markersize=2)
def init ():
ax.set_xlim(0, N*1.1)
pt.set_data([],[])
return (pt)
def animate (pt):
xdata.append (n[i])
ydata.append (an[i])
pt.set_data (xdata,ydata)
return (pt)
ani = FuncAnimation (fig, animate, fargs = (pt), frames=N, init_func=init, interval=50, blit = True)
plt.show ()
Using those codes, I get the entire figure with all the points. I would like to fill the graph point by point in an animated way.
The following will work
%matplotlib nbagg
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
def mcd(a, b):
resto = 0
while(b > 0):
resto = b
b = a % b
a = resto
return a
N = 1200
n = list (range (N))
an = [1,1]
for i in range (2,N):
k = i-1
if mcd (n[i], an[k]) == 1:
an.append (n[i] + 1 + an[k])
else:
an.append (an[k]/mcd (n[i], an[k]))
fig = plt.figure ()
ax = fig.add_subplot (111)
ax.grid (True)
ax.set_xlim(0, N*1.1)
ax.set_ylim(min(an), max(an))
pt, = ax.plot([],[],'ko', markersize=2)
def init ():
pt.set_data([], [])
return pt,
def animate(i):
pt.set_data (n[:i], an[:i])
return pt,
ani = FuncAnimation (fig, animate, frames=N, init_func=init, interval=50, blit = True)
plt.show ()
EDIT: I tested it in normal Python Shell and it draws black dots on red dots but Jupyter draws black dots hidden behind red dots so it needs these lines in different order - first red dots, next empty plot for black dots.
ax.plot(n, an, 'ro', markersize=2) # red dots
pt, = ax.plot([], [], 'ko', markersize=2)
First: I get error message
TypeError: 'Line2D' object is not iterable.
All because () doesn't create tuple - you have to use comma , to create tuple in
return pt, and fargs=(pt,)
Problem is because you draw all point at start using
ax.plot(n, an, 'ko', markersize=2)
and later it draws dots in the same places so you don't see animation.
If you use different color - ie. red
ax.plot(n, an, 'ro', markersize=2)
then you will see animation of black points on red points.
Or remove this line and it will draw dots in empty window.
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
#%matplotlib nbagg
def mcd(a, b):
resto = 0
while(b > 0):
resto = b
b = a % b
a = resto
return a
N = 1200
n = list(range(N))
an = [1, 1]
for i in range(2, N):
k = i-1
if mcd(n[i], an[k]) == 1:
an.append(n[i] + 1 + an[k])
else:
an.append(an[k]/mcd(n[i], an[k]))
fig = plt.figure()
ax = fig.add_subplot(111)
ax.grid(True)
ax.set_xlim(0, N*1.1)
pt, = ax.plot([], [], 'ko', markersize=2)
ax.plot(n, an, 'ro', markersize=2) # red dots
def init():
pt.set_data([], [])
return (pt,)
def animate(i, pt):
pt.set_data(n[:i], an[:i])
return (pt,)
ani = FuncAnimation(fig, animate, fargs=(pt,), frames=N, init_func=init, interval=50, blit=True)
plt.show ()
In second code you have the same problems and you also forgot i in def animate(i, pt):
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
#%matplotlib nbagg
def mcd(a, b):
resto = 0
while(b > 0):
resto = b
b = a % b
a = resto
return a
N = 1200
n = list(range (N))
an = [1, 1]
for i in range(2, N):
k = i-1
if mcd(n[i], an[k]) == 1:
an.append(n[i] + 1 + an[k])
else:
an.append(an[k]/mcd (n[i], an[k]))
xdata, ydata = [], []
fig = plt.figure()
ax = fig.add_subplot(111)
ax.grid(True)
pt, = ax.plot([], [], 'ko', markersize=2)
ax.plot(n, an, 'ro', markersize=2)
def init():
ax.set_xlim(0, N*1.1)
pt.set_data([], [])
return (pt,)
def animate(i, pt):
xdata.append(n[i])
ydata.append(an[i])
pt.set_data(xdata, ydata)
return (pt,)
ani = FuncAnimation(fig, animate, fargs=(pt,), frames=N, init_func=init, interval=50, blit=True)
plt.show ()