Using an image as a matplotlib marker and drawing a circle around that marker - python-3.x

I was using an image as a matplotlib marker , I have attached the code below
import matplotlib.pyplot as plt
from matplotlib import patches
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
import random
plt.rcParams["figure.figsize"] = [10.0,10.0]
plt.rcParams["figure.autolayout"] = True
def getImage(path):
return OffsetImage(plt.imread(path, format="png"), zoom=.1)
paths = ['BS.png']
for i in range(49):
paths.append('BS.png',)
#print(paths)
x = []
for i in range(50):
x.append(random.randint(0,10))
print(x)
y = []
for i in range(50):
y.append(random.randint(0,10))
print(y)
fig, ax = plt.subplots()
circle1 = patches.Circle((5, 5), radius=20, fill = False ,edgecolor = 'black')
for x0,y0,path in zip(x, y, paths):
ab = AnnotationBbox(getImage(path), (x0, y0), frameon=False)
ax.add_artist(ab)
plt.xticks(range(10))
plt.yticks(range(10))
ax.axis('off')
plt.show()
The output is as follows
Now how do I draw a circle around these markers in the graph? A dashed circle is preferrable.
I tried to use circle1 = patches.Circle((x0, y0), radius=20, fill = False ,edgecolor = 'black')
So that a circle is drawn around every marker but it does not draw anything.

You cannot see the circle because the set radius is too large to be displayed inside the canvas (radius = 20, while the axis range is (0, 10)).
These lines of code
fig, ax = plt.subplots()
for x0,y0,path in zip(x, y, paths):
ab = AnnotationBbox(getImage(path), (x0, y0), frameon=False)
ax.add_artist(ab)
circle = plt.Circle((x0, y0), radius=0.5, color='black', fill=False, linestyle='--')
ax.add_artist(circle)
plt.xticks(range(-1, 12))
plt.yticks(range(-1, 12))
ax.axis('off')
plt.show()
produces

Related

How to a reach solid surface in 3d surface in matplotlib?

How to reach a solid surface in 3d surface in matplotlib, please? I tried to apply plot_surface more times, but the refinement is limited and not enough. The transparency is still obvious. More sample also did not help.
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
# Coordinate for the cylinder
def data_for_cylinder_along_z(center_x,center_y,radius,height_z):
z = np.linspace(0, height_z, 200)
theta = np.linspace(0, 2*np.pi, 200)
theta_grid, z_grid=np.meshgrid(theta, z)
x_grid = radius*np.cos(theta_grid) + center_x
y_grid = radius*np.sin(theta_grid) + center_y
return x_grid,y_grid,z_grid
fig = plt.figure(figsize = (5,10))
ax = fig.add_subplot(111, projection='3d')
ax.set_xlim(-2, 2)
ax.set_ylim(-2, 2)
ax.set_zlim(0, 10)
ax.get_proj = lambda: np.dot(Axes3D.get_proj(ax), np.diag([0.4, 0.4, 1.6, 1])) # (x, y, z) ration of sides
theta1 = np.linspace(0, 2*np.pi, 100)
r1 = np.linspace(-2, 0, 100)
t1, R1 = np.meshgrid(theta1, r1)
X1 = R1*np.cos(t1)
Y1 = R1*np.sin(t1)
Z1 = 3+R1*1.5+4
ax.set_xlabel('x axis')
ax.set_ylabel('y axis')
ax.set_zlabel('z axis')
ax.plot_surface(X1, Y1, Z1, color="dimgray")
ax.plot_surface(X1, Y1, Z1, color="dimgray")
# Cylinder
Xc,Yc,Zc = data_for_cylinder_along_z(0,0,2,4)
rep=10
for i in range(rep):
ax.plot_surface(Xc, Yc, Zc, color = 'palegoldenrod')
plt.show()
you need to set antialiased to True (found by trial and error, couldn't find description in the docs):
# Cylinder
Xc,Yc,Zc = data_for_cylinder_along_z(0,0,2,4)
ax.plot_surface(Xc, Yc, Zc, color='palegoldenrod', antialiased=False)

How to show data points on top of matplotlib imshow contour plot?

I have made a contour plot using imshow. The value of z is calculated for a range of x and y values as can be seen from the code below. As a next step I want to plot some calculated z_new values for some random x_new and y_new on top of the contour image which may be marked by closed circles or something similar.
The code reads as:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import colors
x = np.linspace(-0.5, 2.0, 101)
y = np.linspace(-0.5, 2.0, 101)
z = np.zeros((101, 101))
E = 0.0
for i in range(len(x)):
for j in range(len(y)):
z[i,j] = -max(y[j]+0.2+E, 0.5-x[i], 0)
x_new = np.array([1.1168189, 0.8381589, 1.3312789, -0.2149011])
y_new = np.array([1.7571379, 1.5555579, 1.9138179, 0.7912879])
z_new = []
for k, l in zip(x_new, y_new):
#print (k, l, -max(l+0.2+E, 0.5-k, 0)) # z_new = -max(l+0.2+eU, 0.5-k, 0) calculated for some random x_new and y_new
z_new.append(-max(l+0.2+E, 0.5-k, 0)) # I would like to see these z_new points on the contour plot for corresponding x_new and y_new
fig, ax = plt.subplots()
ms = plt.imshow(z.T, cmap='plasma', vmin=-2.5, vmax=0, origin='lower', interpolation='none', extent=[-0.5,2.0,-0.5,2.0])
ax.set_xlabel('x', fontsize=16, fontname = "Helvetica")
ax.set_ylabel('y', fontsize=16, fontname = "Helvetica")
cbar = plt.colorbar(ms)
cbar.ax.tick_params(labelsize=10, direction='out')
cbar.set_label('z', fontsize=16, fontname = "Helvetica")
#plt.savefig('test.pdf')
plt.xticks(fontname = "Helvetica", fontsize=12)
plt.yticks(fontname = "Helvetica", fontsize=12)
plt.show()
You can add a scatter top on top of your heat map with the last line of the code below. The circles are hard to see without the edges, though when you spot them you can get a good feel for how their values compare to the heat map. You can add black edges to the circles by uncommenting the line above the plt.scatter command, which makes the circles easy to locate but the differences in color hard to see.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import colors
x = np.linspace(-0.5, 2.0, 101)
y = np.linspace(-0.5, 2.0, 101)
z = np.zeros((101, 101))
E = 0.0
for i in range(len(x)):
for j in range(len(y)):
z[i,j] = -max(y[j]+0.2+E, 0.5-x[i], 0)
x_new = np.array([1.1168189, 0.8381589, 1.3312789, -0.2149011])
y_new = np.array([1.7571379, 1.5555579, 1.9138179, 0.7912879])
z_new = []
for k, l in zip(x_new, y_new):
#print (k, l, -max(l+0.2+E, 0.5-k, 0)) # z_new = -max(l+0.2+eU, 0.5-k, 0) calculated for some random x_new and y_new
z_new.append(-max(l+0.2+E, 0.5-k, 0)) # I would like to see these z_new points on the contour plot for corresponding x_new and y_new
fig, ax = plt.subplots()
ms = plt.imshow(z.T, cmap='plasma', vmin=-2.5, vmax=0, origin='lower', interpolation='none', extent=[-0.5,2.0,-0.5,2.0])
ax.set_xlabel('x', fontsize=16, fontname = "Helvetica")
ax.set_ylabel('y', fontsize=16, fontname = "Helvetica")
cbar = plt.colorbar(ms)
cbar.ax.tick_params(labelsize=10, direction='out')
cbar.set_label('z', fontsize=16, fontname = "Helvetica")
#plt.savefig('test.pdf')
plt.xticks(fontname = "Helvetica", fontsize=12)
plt.yticks(fontname = "Helvetica", fontsize=12)
## New code ##
# plt.scatter(x_new, y_new, c=z_new, cmap='plasma', vmin=-2.5, vmax=0, edgecolors='black') # Uncomment this if you want the points circled in black
plt.scatter(x_new, y_new, c='green') # Uncomment if you want green circles
# plt.scatter(x_new, y_new, c=z_new, cmap='plasma', vmin=-2.5, vmax=0)
## End of new code ##
plt.show()

Common X and Y axis lable for all subplots in the case of sns.lineplot and axhline? [duplicate]

I have the following plot:
import matplotlib.pyplot as plt
fig2 = plt.figure()
ax3 = fig2.add_subplot(2,1,1)
ax4 = fig2.add_subplot(2,1,2)
ax4.loglog(x1, y1)
ax3.loglog(x2, y2)
ax3.set_ylabel('hello')
I want to be able to create axes labels and titles not just for each of the two subplots, but also common labels that span both subplots. For example, since both plots have identical axes, I only need one set of x and y- axes labels. I do want different titles for each subplot though.
I tried a few things but none of them worked right
You can create a big subplot that covers the two subplots and then set the common labels.
import random
import matplotlib.pyplot as plt
x = range(1, 101)
y1 = [random.randint(1, 100) for _ in range(len(x))]
y2 = [random.randint(1, 100) for _ in range(len(x))]
fig = plt.figure()
ax = fig.add_subplot(111) # The big subplot
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)
# Turn off axis lines and ticks of the big subplot
ax.spines['top'].set_color('none')
ax.spines['bottom'].set_color('none')
ax.spines['left'].set_color('none')
ax.spines['right'].set_color('none')
ax.tick_params(labelcolor='w', top=False, bottom=False, left=False, right=False)
ax1.loglog(x, y1)
ax2.loglog(x, y2)
# Set common labels
ax.set_xlabel('common xlabel')
ax.set_ylabel('common ylabel')
ax1.set_title('ax1 title')
ax2.set_title('ax2 title')
plt.savefig('common_labels.png', dpi=300)
Another way is using fig.text() to set the locations of the common labels directly.
import random
import matplotlib.pyplot as plt
x = range(1, 101)
y1 = [random.randint(1, 100) for _ in range(len(x))]
y2 = [random.randint(1, 100) for _ in range(len(x))]
fig = plt.figure()
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)
ax1.loglog(x, y1)
ax2.loglog(x, y2)
# Set common labels
fig.text(0.5, 0.04, 'common xlabel', ha='center', va='center')
fig.text(0.06, 0.5, 'common ylabel', ha='center', va='center', rotation='vertical')
ax1.set_title('ax1 title')
ax2.set_title('ax2 title')
plt.savefig('common_labels_text.png', dpi=300)
One simple way using subplots:
import matplotlib.pyplot as plt
fig, axes = plt.subplots(3, 4, sharex=True, sharey=True)
# add a big axes, hide frame
fig.add_subplot(111, frameon=False)
# hide tick and tick label of the big axes
plt.tick_params(labelcolor='none', top=False, bottom=False, left=False, right=False)
plt.grid(False)
plt.xlabel("common X")
plt.ylabel("common Y")
New in matplotlib 3.4.0
There are now built-in methods to set common axis labels:
supxlabel
fig.supxlabel('common x label')
supylabel
fig.supylabel('common y label')
To reproduce OP's loglog plots (common labels but separate titles):
x = np.arange(0.01, 10.01, 0.01)
y = 2 ** x
fig, (ax1, ax2) = plt.subplots(2, 1, constrained_layout=True)
ax1.loglog(y, x)
ax2.loglog(x, y)
# separate subplot titles
ax1.set_title('ax1.title')
ax2.set_title('ax2.title')
# common axis labels
fig.supxlabel('fig.supxlabel')
fig.supylabel('fig.supylabel')
plt.setp() will do the job:
# plot something
fig, axs = plt.subplots(3,3, figsize=(15, 8), sharex=True, sharey=True)
for i, ax in enumerate(axs.flat):
ax.scatter(*np.random.normal(size=(2,200)))
ax.set_title(f'Title {i}')
# set labels
plt.setp(axs[-1, :], xlabel='x axis label')
plt.setp(axs[:, 0], ylabel='y axis label')
Wen-wei Liao's answer is good if you are not trying to export vector graphics or that you have set up your matplotlib backends to ignore colorless axes; otherwise the hidden axes would show up in the exported graphic.
My answer suplabel here is similar to the fig.suptitle which uses the fig.text function. Therefore there is no axes artist being created and made colorless.
However, if you try to call it multiple times you will get text added on top of each other (as fig.suptitle does too). Wen-wei Liao's answer doesn't, because fig.add_subplot(111) will return the same Axes object if it is already created.
My function can also be called after the plots have been created.
def suplabel(axis,label,label_prop=None,
labelpad=5,
ha='center',va='center'):
''' Add super ylabel or xlabel to the figure
Similar to matplotlib.suptitle
axis - string: "x" or "y"
label - string
label_prop - keyword dictionary for Text
labelpad - padding from the axis (default: 5)
ha - horizontal alignment (default: "center")
va - vertical alignment (default: "center")
'''
fig = pylab.gcf()
xmin = []
ymin = []
for ax in fig.axes:
xmin.append(ax.get_position().xmin)
ymin.append(ax.get_position().ymin)
xmin,ymin = min(xmin),min(ymin)
dpi = fig.dpi
if axis.lower() == "y":
rotation=90.
x = xmin-float(labelpad)/dpi
y = 0.5
elif axis.lower() == 'x':
rotation = 0.
x = 0.5
y = ymin - float(labelpad)/dpi
else:
raise Exception("Unexpected axis: x or y")
if label_prop is None:
label_prop = dict()
pylab.text(x,y,label,rotation=rotation,
transform=fig.transFigure,
ha=ha,va=va,
**label_prop)
Here is a solution where you set the ylabel of one of the plots and adjust the position of it so it is centered vertically. This way you avoid problems mentioned by KYC.
import numpy as np
import matplotlib.pyplot as plt
def set_shared_ylabel(a, ylabel, labelpad = 0.01):
"""Set a y label shared by multiple axes
Parameters
----------
a: list of axes
ylabel: string
labelpad: float
Sets the padding between ticklabels and axis label"""
f = a[0].get_figure()
f.canvas.draw() #sets f.canvas.renderer needed below
# get the center position for all plots
top = a[0].get_position().y1
bottom = a[-1].get_position().y0
# get the coordinates of the left side of the tick labels
x0 = 1
for at in a:
at.set_ylabel('') # just to make sure we don't and up with multiple labels
bboxes, _ = at.yaxis.get_ticklabel_extents(f.canvas.renderer)
bboxes = bboxes.inverse_transformed(f.transFigure)
xt = bboxes.x0
if xt < x0:
x0 = xt
tick_label_left = x0
# set position of label
a[-1].set_ylabel(ylabel)
a[-1].yaxis.set_label_coords(tick_label_left - labelpad,(bottom + top)/2, transform=f.transFigure)
length = 100
x = np.linspace(0,100, length)
y1 = np.random.random(length) * 1000
y2 = np.random.random(length)
f,a = plt.subplots(2, sharex=True, gridspec_kw={'hspace':0})
a[0].plot(x, y1)
a[1].plot(x, y2)
set_shared_ylabel(a, 'shared y label (a. u.)')
# list loss and acc are your data
fig = plt.figure()
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122)
ax1.plot(iteration1, loss)
ax2.plot(iteration2, acc)
ax1.set_title('Training Loss')
ax2.set_title('Training Accuracy')
ax1.set_xlabel('Iteration')
ax1.set_ylabel('Loss')
ax2.set_xlabel('Iteration')
ax2.set_ylabel('Accuracy')
The methods in the other answers will not work properly when the yticks are large. The ylabel will either overlap with ticks, be clipped on the left or completely invisible/outside of the figure.
I've modified Hagne's answer so it works with more than 1 column of subplots, for both xlabel and ylabel, and it shifts the plot to keep the ylabel visible in the figure.
def set_shared_ylabel(a, xlabel, ylabel, labelpad = 0.01, figleftpad=0.05):
"""Set a y label shared by multiple axes
Parameters
----------
a: list of axes
ylabel: string
labelpad: float
Sets the padding between ticklabels and axis label"""
f = a[0,0].get_figure()
f.canvas.draw() #sets f.canvas.renderer needed below
# get the center position for all plots
top = a[0,0].get_position().y1
bottom = a[-1,-1].get_position().y0
# get the coordinates of the left side of the tick labels
x0 = 1
x1 = 1
for at_row in a:
at = at_row[0]
at.set_ylabel('') # just to make sure we don't and up with multiple labels
bboxes, _ = at.yaxis.get_ticklabel_extents(f.canvas.renderer)
bboxes = bboxes.inverse_transformed(f.transFigure)
xt = bboxes.x0
if xt < x0:
x0 = xt
x1 = bboxes.x1
tick_label_left = x0
# shrink plot on left to prevent ylabel clipping
# (x1 - tick_label_left) is the x coordinate of right end of tick label,
# basically how much padding is needed to fit tick labels in the figure
# figleftpad is additional padding to fit the ylabel
plt.subplots_adjust(left=(x1 - tick_label_left) + figleftpad)
# set position of label,
# note that (figleftpad-labelpad) refers to the middle of the ylabel
a[-1,-1].set_ylabel(ylabel)
a[-1,-1].yaxis.set_label_coords(figleftpad-labelpad,(bottom + top)/2, transform=f.transFigure)
# set xlabel
y0 = 1
for at in axes[-1]:
at.set_xlabel('') # just to make sure we don't and up with multiple labels
bboxes, _ = at.xaxis.get_ticklabel_extents(fig.canvas.renderer)
bboxes = bboxes.inverse_transformed(fig.transFigure)
yt = bboxes.y0
if yt < y0:
y0 = yt
tick_label_bottom = y0
axes[-1, -1].set_xlabel(xlabel)
axes[-1, -1].xaxis.set_label_coords((left + right) / 2, tick_label_bottom - labelpad, transform=fig.transFigure)
It works for the following example, while Hagne's answer won't draw ylabel (since it's outside of the canvas) and KYC's ylabel overlaps with the tick labels:
import matplotlib.pyplot as plt
import itertools
fig, axes = plt.subplots(3, 4, sharey='row', sharex=True, squeeze=False)
fig.subplots_adjust(hspace=.5)
for i, a in enumerate(itertools.chain(*axes)):
a.plot([0,4**i], [0,4**i])
a.set_title(i)
set_shared_ylabel(axes, 'common X', 'common Y')
plt.show()
Alternatively, if you are fine with colorless axis, I've modified Julian Chen's solution so ylabel won't overlap with tick labels.
Basically, we just have to set ylims of the colorless so it matches the largest ylims of the subplots so the colorless tick labels sets the correct location for the ylabel.
Again, we have to shrink the plot to prevent clipping. Here I've hard coded the amount to shrink, but you can play around to find a number that works for you or calculate it like in the method above.
import matplotlib.pyplot as plt
import itertools
fig, axes = plt.subplots(3, 4, sharey='row', sharex=True, squeeze=False)
fig.subplots_adjust(hspace=.5)
miny = maxy = 0
for i, a in enumerate(itertools.chain(*axes)):
a.plot([0,4**i], [0,4**i])
a.set_title(i)
miny = min(miny, a.get_ylim()[0])
maxy = max(maxy, a.get_ylim()[1])
# add a big axes, hide frame
# set ylim to match the largest range of any subplot
ax_invis = fig.add_subplot(111, frameon=False)
ax_invis.set_ylim([miny, maxy])
# hide tick and tick label of the big axis
plt.tick_params(labelcolor='none', top=False, bottom=False, left=False, right=False)
plt.xlabel("common X")
plt.ylabel("common Y")
# shrink plot to prevent clipping
plt.subplots_adjust(left=0.15)
plt.show()
You could use "set" in axes as follows:
axes[0].set(xlabel="KartalOl", ylabel="Labeled")

How to create a grid according to the orientation of a polygon

How can I make a grid according to the orientation of a polygon with some interval, where the polygon has always 4 point data with different orientation, for example my polygon looks like this :
x1 = np.array([50,0,150,200,50])
y1 = np.array([10,-50,-60,0,10])
and I want to make grid like this :
You can use a interpolate function for each coordinate:
from scipy.interpolate import interp2d
x = np.array([0, 1], dtype=np.float)
y = np.array([0, 1], dtype=np.float)
Now we need to create the interpolate function so that on the small unit square (0,1)x(0,1), we get the result we want.
zx = np.array([[0, 150],[50, 200]])
fx = interp2d(x, y, zx)
fx(0.5, 0.5)
Do the same for zy to get the y coordinate inside your polygon.
You can create a LineCollection of "grid lines" by subdividing the polygon edges into equidistant parts as shown below. In the function grid, nx and ny are the number of lines to create per dimension of the polygon.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
from matplotlib.collections import LineCollection
def grid(x,y, nx, ny, **kwargs):
def verts(a,b,c,d,n):
l1x = np.linspace(x[a],x[b], n)
l1y = np.linspace(y[a],y[b], n)
l2x = np.linspace(x[d],x[c], n)
l2y = np.linspace(y[d],y[c], n)
return np.stack((np.c_[l1x, l2x], np.c_[l1y, l2y]), axis=-1)
v = np.concatenate((verts(0,1,2,3,ny), verts(1,2,3,4,nx)), axis=0)
return LineCollection(v, **kwargs)
x1 = np.array([50,0,150,200,50])
y1 = np.array([10,-50,-60,0,10])
fig, ax = plt.subplots()
ax.add_collection(grid(x1,y1,5,6, color="gray"))
rect=Polygon(np.c_[x1,y1], edgecolor="C0", linewidth=2, facecolor="none", zorder=3)
ax.add_patch(rect)
ax.autoscale()
plt.show()

How to change the line formate in animation? [duplicate]

This question already has answers here:
Animate points with labels with matplotlib
(3 answers)
Closed 5 years ago.
I made this animation using matplotlib and it is working properly, however, i need to add some animated labels to be moving with its corresponding points.
The first label to be referring to the intersection point between the circle and the horizontal line from the centre of the ellipse and the other text label is to be in the middle of the inclined line annotating its length.
I tried some ideas but nothing worked properly. Any ideas?
screenshot
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
ax = fig.add_subplot(111, autoscale_on=True, xlim=(-6, 6), ylim=(-7, 17))
# ax.grid()
line, = ax.plot([], [], 'k', lw=1)
line2, = ax.plot([], [], 'k--', lw=1)
a,b = 3,2
x,y = list(),list()
x1 =np.array([item/10 for item in range(-30,31)])
y1 = np.sqrt(b**2 * (1-(x1**2 / a**2)))
x =list(x1)+[-item for item in list(x1)]
y =list(y1)+[-item for item in list(y1)]
plt.plot(x, y, 'k:')
plt.plot((0,0), (0,15), 'k--')
ax.annotate('$A$', xy=(0,15), xytext=(-10, 10),color='b',
textcoords='offset points')
ax.annotate('$O$', xy=(0,-1), xytext=(-10, 10),color='b',ha='center',
textcoords='offset points')
ax.annotate('$4a$', xy=(0,7), xytext=(-10, 10),color='b',ha='center',
textcoords='offset points', family='sans serif')
def animate(i):
thisx = [0, x[i]]
thisy = [15, y[i]]
xx = [x[i], 0]
yy = [y[i], 0]
line.set_data(thisx, thisy)
line2.set_data(xx, yy)
return line, line2
ani = animation.FuncAnimation(fig, animate, np.arange(0, len(x)), interval=20, blit=False)
ax.annotate('$P$', xy=(3,0), xytext=(0, 0),color='b',ha='center',
textcoords='offset points', family='sans serif', style='italic')
plt.show()
# ani.save('circular_motion.mp4', fps=20)
#
plt.close()
You can alter the annotation properties in the same way that you alter the line properties. Just store the Annotation object that is returned from the ax.annotation command and then update its position in the animate function. Note that the function set_position does not work properly, as was also noted in here, therefore you have to use the xy attribute.
Furthermore, I noticed that your animation runs faster when you y values are close to zero. You can fix that (if it needs fixing) by defining a vector of angles and computing the xy coordinates from that. I took the freedom to alter your code to show what I mean.
About the length of the inclined line, I annotated it here as L, as you don't state what the length of the distance OP is, but I guess that you can fill that in yourself.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
ax = fig.add_subplot(111, autoscale_on=True, xlim=(-6, 6), ylim=(-7, 17))
# ax.grid()
line, = ax.plot([], [], 'k', lw=1)
line2, = ax.plot([], [], 'k--', lw=1)
a,b = 3,2
x,y = list(),list()
z = 15
phi = np.linspace(0,2*np.pi,100)
x = a * np.cos(phi)
y = -b * np.sin(phi)
plt.plot(x, y, 'k:')
plt.plot((0,0), (0,z), 'k--')
ax.annotate('$A$', xy=(0,15), xytext=(-10, 10),color='b',
textcoords='offset points')
ax.annotate('$O$', xy=(0,-1), xytext=(-10, 10),color='b',ha='center',
textcoords='offset points')
ax.annotate('$4a$', xy=(0,7), xytext=(-10, 10),color='b',ha='center',
textcoords='offset points', family='sans serif')
def animate(i):
thisx = [0, x[i]]
thisy = [z, y[i]]
xx = [x[i], 0]
yy = [y[i], 0]
line.set_data(thisx, thisy)
line2.set_data(xx, yy)
P.xy = (x[i]*1.05,y[i])
L.xy = ((x[i]/2)*1.05, z/2+y[i]/2)
return line, line2, P, L
ani = animation.FuncAnimation(fig, animate, np.arange(0, len(x)), interval=20, blit=False)
P = ax.annotate('$P$', xy=(a,0), xytext=(0, 0),color='b',ha='center',
textcoords='offset points', family='sans serif', style='italic')
L = ax.annotate('$L$', xy=(a/2,z/2), xytext=(0, 0),color='b',ha='center',
textcoords='offset points', family='sans serif', style='italic')
plt.show()
# ani.save('circular_motion.mp4', fps=20)
#
plt.close()
Hope this helps.

Resources