label Taylor diagram with number by Python - python

My Python code here:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.projections import PolarAxes
import mpl_toolkits.axisartist.grid_finder as gf
import mpl_toolkits.axisartist.floating_axes as fa
class TaylorDiagram(object):
def __init__(self, STD ,fig=None, rect=111, label='_'):
self.STD = STD
tr = PolarAxes.PolarTransform()
# Correlation labels
rlocs = np.concatenate(((np.arange(11.0) / 10.0), [0.95, 0.99]))
tlocs = np.arccos(rlocs) # Conversion to polar angles
gl1 = gf.FixedLocator(tlocs) # Positions
tf1 = gf.DictFormatter(dict(zip(tlocs, map(str, rlocs))))
# Standard deviation axis extent
self.smin = 0
self.smax = 1.6 * self.STD
gh = fa.GridHelperCurveLinear(tr,extremes=(0,(np.pi/2),self.smin,self.smax),grid_locator1=gl1,tick_formatter1=tf1,)
if fig is None:
fig = plt.figure()
ax = fa.FloatingSubplot(fig, rect, grid_helper=gh)
fig.add_subplot(ax)
# Angle axis
ax.axis['top'].set_axis_direction('bottom')
ax.axis['top'].label.set_text("Correlation coefficient")
ax.axis['top'].toggle(ticklabels=True, label=True)
ax.axis['top'].major_ticklabels.set_axis_direction('top')
ax.axis['top'].label.set_axis_direction('top')
# X axis
ax.axis['left'].set_axis_direction('bottom')
ax.axis['left'].label.set_text("Standard deviation")
ax.axis['left'].toggle(ticklabels=True, label=True)
ax.axis['left'].major_ticklabels.set_axis_direction('bottom')
ax.axis['left'].label.set_axis_direction('bottom')
# Y axis
ax.axis['right'].set_axis_direction('top')
ax.axis['right'].label.set_text("Standard deviation")
ax.axis['right'].toggle(ticklabels=True, label=True)
ax.axis['right'].major_ticklabels.set_axis_direction('left')
ax.axis['right'].label.set_axis_direction('top')
# Useless
ax.axis['bottom'].set_visible(False)
# Contours along standard deviations
ax.grid()
self._ax = ax # Graphical axes
self.ax = ax.get_aux_axes(tr) # Polar coordinates
# Add reference point and STD contour
l , = self.ax.plot([0], self.STD, 'k*', ls='', ms=12, label=label)
l1 , = self.ax.plot([0], self.STD, 'k*', ls='', ms=12, label=label)
t = np.linspace(0, (np.pi / 2.0))
t1 = np.linspace(0, (np.pi / 2.0))
r = np.zeros_like(t) + self.STD
r1 = np.zeros_like(t) + self.STD
self.ax.plot(t, r, 'k--', label='_')
# Collect sample points for latter use (e.g. legend)
self.samplePoints = [l]
self.samplePoints = [l1]
def add_sample(self,STD,r,*args,**kwargs):
l,= self.ax.plot(np.arccos(r), STD, *args, **kwargs) # (theta, radius)
self.samplePoints.append(l)
return l
def add_sample(self,STD,r1,*args,**kwargs):
l1,= self.ax.plot(np.arccos(r1), STD, *args, **kwargs) # (theta, radius)
self.samplePoints.append(l1)
return l1
def add_contours(self,levels=5,**kwargs):
rs, ts = np.meshgrid(np.linspace(self.smin, self.smax), np.linspace(0, (np.pi / 2.0)))
RMSE=np.sqrt(np.power(self.STD, 2) + np.power(rs, 2) - (2.0 * self.STD * rs *np.cos(ts)))
contours = self.ax.contour(ts, rs, RMSE, levels, **kwargs)
return contours
def srl(obsSTD, s, s1, r, r1, l, l1, fname):
fig=plt.figure(figsize=(8,8))
dia=TaylorDiagram(obsSTD, fig=fig, rect=111, label='ref')
plt.clabel(dia.add_contours(colors='#808080'), inline=1, fontsize=10)
srlc = zip(s, r, l)
srlc1 = zip(s1, r1, l1)
for i in srlc:
dia.add_sample(i[0], i[1], label=i[2], marker='o',mec = 'red', mfc = 'none', mew=1.6)
for i in srlc1:
dia.add_sample(i[0], i[1], label=i[2], marker='^', mec = 'blue', mfc = 'none', mew=1.6)
spl = [p.get_label() for p in dia.samplePoints]
fig.legend(dia.samplePoints, spl, numpoints=1, prop=dict(size='small'), loc=[0.7,0.3])
plt.show()
obsSTD = 1
s = [1.275412605,1.391302157,1.424314937]
s1 = [0.980035327,0.997244197, 1.003002031]
r = [0.572272,0.533529,0.477572]
r1 = [0.82,0.72,0.8]
l = ['A', 'B','C']
l1 = ['A', 'B','C']
fname = 'TaylorDiagram.jpg'
srl(obsSTD, s, s1, r, r1, l,l1, fname)
And give a result that: taylor_dig that the red is before processing, the blue for after one. How could I label the position of A, B, C models as 1,2,3 (enumerate) with the position of before and after processing as well as legends without repeating?

Related

How to plot circles in polar plot in python

I have a list with x,y,z, r coordinates (cartesians). I need to plot some circles in a polar plot, but I don't know how to do it with cartesians coordinates.
I am trying to do it with this line
circle1 = plt.Circle((x[i], y[i]), r[i], transform=ax3.transData._b, color = 'r', alpha=0.5, fill=False)
but this doesn't seem to work because I obtain the circles too far away from the center of the origin.
any help?
data1 = pd.read_csv('Uchuu_lightcone_0_11.9_voids.txt', sep='\s+', header=None)
data1 = pd.DataFrame(data1)
x = data1[0]
y = data1[1]
r = data1[3]
z = data1[2]
azvoids, elvoids, rvoids = cart2sph(x,y,z)
d = ax3.scatter(azvoids, rvoids, s=3, c='red', alpha=1, marker='.')
for i in range(len(x)):
if elvoids[i] > 35 and elvoids[i] < 45:
circle1 = plt.Circle((x[i], y[i]), r[i], transform=ax3.transData._b, color = 'r', alpha=0.5, fill=False)
ax3.add_artist(circle1)
# The cart2sph function is
def cart2sph(x,y,z):
""" x, y, z : ndarray coordinates
ceval: backend to use:
- eval : pure Numpy
- numexpr.evaluate: Numexpr """
azimuth = arctan2(y,x)*180/math.pi
xy2 = x**2 + y**2
elevation = arctan2(z, sqrt(xy2))*180/math.pi
r = sqrt(xy2 + z**2)
return azimuth, elevation, r
You should use azvoids and rvoids to plot the center of the circle since you use those to show tham in the scatter plot
import math
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.transforms as transforms
def cart2sph(x, y, z):
""" x, y, z : ndarray coordinates
ceval: backend to use:
- eval : pure Numpy
- numexpr.evaluate: Numexpr """
azimuth = np.arctan2(y, x) * 180 / math.pi
xy2 = x ** 2 + y ** 2
elevation = np.arctan2(z, np.sqrt(xy2)) * 180 / math.pi
r = np.sqrt(xy2 + z ** 2)
return azimuth, elevation, r
#
# data1 = pd.read_csv('Uchuu_lightcone_0_11.9_voids.txt', sep='\s+', header=None)
# data1 = pd.DataFrame(data1)
N=100
x = (np.random.rand(N)-0.5)*100
y = (np.random.rand(N)-0.5)*100
z = (np.random.rand(N)-0.5)*100
r = np.random.rand(N)*10
azvoids, elvoids, rvoids = cart2sph(x, y, z)
fig = plt.figure()
ax3 = plt.subplot(111 )
d = plt.scatter(azvoids, elvoids, s=3 , c='red', alpha=1, marker='.' )
for i in range(len(x)):
if elvoids[i] > 35 and elvoids[i] < 45:
# circle1 = plt.Circle((azvoids[i], elvoids[i]), rvoids[i], color='r', alpha=0.5, fill=False)
x, y = ax3.transData.transform((azvoids[i], elvoids[i]))
trans = (fig.dpi_scale_trans +
transforms.ScaledTranslation(azvoids[i], elvoids[i], ax3.transData))
circle1 = plt.Circle((azvoids[i], elvoids[i]), rvoids[i] , color='r', alpha=0.5, fill=None)
ax3.add_artist(circle1)
plt.axis('equal')
plt.show()
# The cart2sph function is

Animating Damped Oscillator

I was able to simulate a mass-spring system under damped oscillations. However, I wanted to add a subplot of position vs time and another subplot velocity vs position (phase path) so that I will be having three animations. How can I add them? The source code that I used is shown below
Update: I tried adding the first subplot position vs time but I cannot get the desired curve for it.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
#Constants
w = np.pi #angular frequency
b = np.pi * 0.2 #damping parameter
#Function that implements rk4 integration
def rk4(t, x, f, dt):
dx1 = f(t, x)*dt
dx2 = f(t+0.5*dt, x+0.5*dx1)*dt
dx3 = f(t+0.5*dt, x+0.5*dx2)*dt
dx4 = f(t+dt, x+dx3)*dt
return x+dx1/6.0+dx2/3.0+dx3/3.0+dx4/6.0
#Function that returns dX/dt for the linearly damped oscillator
def dXdt(t, X):
x = X[0]
vx = X[1]
ax = -2*b*vx - w**2*x
return np.array([vx, ax])
#Initialize Variables
x0 = 5.0 #Initial x position
vx0 = 1.0 #Initial x Velocity
#Setting time array for graph visualization
dt = 0.05
N = 200
x = np.zeros(N)
vx = np.zeros(N)
y = []
# integrate equations of motion using rk4;
# X is a vector that contains the positions and velocities being integrated
X = np.array([x0, vx0])
for i in range(N):
x[i] = X[0]
vx[i] = X[1]
y.append(0)
# update the vector X to the next time step
X = rk4(i*dt, X, dXdt, dt)
fig, (ax1, ax2) = plt.subplots(2,1, figsize=(8,6))
fig.suptitle(r' Damped Oscillation with $\beta$$\approx$' + str(round(b,2)) + r' and $\omega$$\approx$'
+ str(round(w,2)), fontsize=16)
line1, = ax1.plot([], [], lw=10,c="blue",ls="-",ms=50,marker="s",mfc="gray",fillstyle="none",mec="black",markevery=2)
line2, = ax2.plot([], [], lw=2, color='r')
time_template = '\nTime = %.1fs'
time_text = ax1.text(0.1, 0.9, '', transform=ax1.transAxes)
for ax in [ax1, ax2]:
ax1.set_xlim(1.2*min(x), 1.2*max(x))
ax2.set_ylim(1.2*min(x), 1.2*max(x),1)
ax2.set_xlim(0, N*dt)
ax1.set_yticklabels([])
def init():
line1.set_data([], [])
line2.set_data([], [])
time_text.set_text('')
return line1, line2, time_text
def animate(i):
thisx1 = [x[i],0]
thisy1 = [y[i],0]
thisx2 = [i*dt,0]
thisy2 = [x[i],0]
line1.set_data(thisx1, thisy1)
line2.set_data(thisx2, thisy2)
time_text.set_text(time_template % (i*dt))
return line1, line2, time_text
ani = animation.FuncAnimation(fig, animate, np.arange(1, N),
interval=50, blit=True, init_func=init,repeat=False)
After some minors changes to your initial code, the most noteworthy being:
thisx1 = [x[i],0]
thisy1 = [y[i],0]
thisx2 = [i*dt,0]
thisy2 = [x[i],0]
line1.set_data(thisx1, thisy1)
line2.set_data(thisx2, thisy2)
# should be written like this
line1.set_data([x[i],0], [y[i],0])
line2.set_data(t[:i], x[:i])
line3.set_data(x[:i], vx[:i])
The working version, with phase space plot in green, is as follows:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
#Constants
w = np.pi #angular frequency
b = np.pi * 0.2 #damping parameter
#Function that implements rk4 integration
def rk4(t, x, f, dt):
dx1 = f(t, x)*dt
dx2 = f(t+0.5*dt, x+0.5*dx1)*dt
dx3 = f(t+0.5*dt, x+0.5*dx2)*dt
dx4 = f(t+dt, x+dx3)*dt
return x+dx1/6.0+dx2/3.0+dx3/3.0+dx4/6.0
#Function that returns dX/dt for the linearly damped oscillator
def dXdt(t, X):
x = X[0]
vx = X[1]
ax = -2*b*vx - w**2*x
return np.array([vx, ax])
#Initialize Variables
x0 = 5.0 #Initial x position
vx0 = 1.0 #Initial x Velocity
#Setting time array for graph visualization
dt = 0.05
N = 200
t = np.linspace(0,N*dt,N,endpoint=False)
x = np.zeros(N)
vx = np.zeros(N)
y = np.zeros(N)
# integrate equations of motion using rk4;
# X is a vector that contains the positions and velocities being integrated
X = np.array([x0, vx0])
for i in range(N):
x[i] = X[0]
vx[i] = X[1]
# update the vector X to the next time step
X = rk4(i*dt, X, dXdt, dt)
fig, (ax1, ax2, ax3) = plt.subplots(3,1, figsize=(8,12))
fig.suptitle(r' Damped Oscillation with $\beta$$\approx$' + str(round(b,2)) + r' and $\omega$$\approx$'
+ str(round(w,2)), fontsize=16)
line1, = ax1.plot([], [], lw=10,c="blue",ls="-",ms=50,marker="s",mfc="gray",fillstyle="none",mec="black",markevery=2)
line2, = ax2.plot([], [], lw=1, color='r')
line3, = ax3.plot([], [], lw=1, color='g')
time_template = '\nTime = %.1fs'
time_text = ax1.text(0.1, 0.9, '', transform=ax1.transAxes)
ax1.set_xlim(1.2*min(x), 1.2*max(x))
ax1.set_xlabel('x')
ax1.set_ylabel('y')
ax2.set_ylim(1.2*min(x), 1.2*max(x),1)
ax2.set_xlim(0, N*dt)
ax2.set_xlabel('t')
ax2.set_ylabel('x')
ax3.set_xlim(1.2*min(x), 1.2*max(x),1)
ax3.set_ylim(1.2*min(vx), 1.2*max(vx),1)
ax3.set_xlabel('x')
ax3.set_ylabel('vx')
def init():
line1.set_data([], [])
line2.set_data([], [])
line3.set_data([], [])
time_text.set_text('')
return line1, line2, line3, time_text
def animate(i):
line1.set_data([x[i],0], [y[i],0])
line2.set_data(t[:i], x[:i])
line3.set_data(x[:i], vx[:i])
time_text.set_text(time_template % (i*dt))
return line1, line2, line3, time_text
ani = animation.FuncAnimation(fig, animate, np.arange(1, N),
interval=50, blit=True, init_func=init,repeat=False)
ani.save('anim.gif')
and gives:

How to plot upper and lower boundary with a LINEAR line on a scatter plot?

I have a data frame df with columns A and Q. I am using this code to draw a line of equation on it.
#Actual line of equation, which has to be plotted: Q=alpha*A^beta : ln(Q)=a+b*ln(A) : y = a+b(x)
x = np.log(df['A'])
y = np.log(df['Q'])
#deriving b,a
b,a = np.polyfit(np.log(x), y, 1)
#deriving alpha and beta. By using a = ln(alpha); b = beta -1
alpha = np.exp(a)
beta = b + 1
Q = df['Q'].values
A = df['A'].values
#equation of line
q = alpha * np.power(A,beta)
#plotting the points and line
plt.scatter(A,Q)
plt.plot(A,q, '-r')
plt.yscale('log')
plt.xscale('log')
This gives the following output, which is similar to a regression line.
But I am interested in plotting the same line of the equation as the upper and lower curve/boundary joining the farthest points(perpendicular to the green line) on both sides as shown below with the same slope as that of the continuous green line.
The idea is to first search the index of the point where the difference between the line and the plot is minimal (cf. maximal). With this point, alpha_min can be calculated such that
Q[pos_min] == alpha_min * np.power(A[pos_min], beta), thus
alpha_min = Q[pos_min] / np.power(A[pos_min], beta).
As such lines can extend quite far away from the original points, it can help to restore the x and y limits (thus clipping the plot to the original region).
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
df = pd.DataFrame()
df['A'] = 10 ** np.random.uniform(0, 1, 1000) ** 2
df['Q'] = 10 ** np.random.uniform(0, 1, 1000) ** 2
x = np.log(df['A'])
y = np.log(df['Q'])
# deriving b,a
b, a = np.polyfit(np.log(x), y, 1)
# deriving alpha and beta. By using a = ln(alpha); b = beta - 1
alpha = np.exp(a)
beta = b + 1
Q = df['Q'].values
A = df['A'].values
# plotting the points and line
plt.yscale('log')
plt.xscale('log')
plt.scatter(A, Q, color='b')
# equation of line
xmin, xmax = plt.xlim() # the limits of the x-axis for drawing the line
x = np.linspace(xmin, xmax, 50)
q = alpha * np.power(x, beta)
plt.plot(x, q, '-r')
ymin, ymax = plt.ylim() # store the limits of the scatter and line plot so they can be restored later
pos_min = np.argmin(Q / np.power(A, beta))
pos_max = np.argmax(Q / np.power(A, beta))
alpha_min = Q[pos_min] / np.power(A[pos_min], beta)
alpha_max = Q[pos_max] / np.power(A[pos_max], beta)
# plt.scatter(A[pos_min], Q[pos_min], s=100, fc='none', ec='r', lw=3)
# plt.scatter(A[pos_max], Q[pos_max], s=100, fc='none', ec='g', lw=3)
plt.plot(x, (alpha_max) * np.power(x, beta), '--r')
plt.plot(x, (alpha_min) * np.power(x, beta), '--r')
plt.xlim(xmin, xmax) # restore the limits of the scatter plot
plt.ylim(ymin, ymax)
plt.show()

How to remove a matplotlib collection

The Python program below plots a random set of 3 points and circles around them according to the eps slider value.
When changing the slider value, the circles change.
Also, if two circles touch each other, a segment connecting their centers is drawn.
My problem is: how to remove such segment if the eps decreases so there is no intersection anymore?
In resume, how to remove the line collection from the plot?
MWE
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button, RadioButtons
from matplotlib import collections as mc
# create circles
circles = []
def create_circles(N):
for i in range(N):
circles.append(plt.Circle(pts[i], min_distance/10, ec="b", fill=False))
ax.add_patch(circles[i])
# create edges
def create_edges(eps):
edges = []
tmp = np.where(distance_matrix < 2 * eps)
edges_ij = zip(tmp[0], tmp[1])
for e in edges_ij:
if e[0] < e[1]:
edges.append([pts[e[0]], pts[e[1]]])
lc = mc.LineCollection(edges, colors='r', linewidths=1)
ax.add_collection(lc)
# create points
def create_points(N):
ptsx = np.random.random(N)
ptsy = np.random.random(N)
pts = zip(ptsx, ptsy)
return pts
# create distance matrix
def create_distance_matrix(pts):
N = len(pts)
distance_matrix = np.zeros((N, N))
for i in range(N-1):
for j in range(i+1,N):
P = pts[i]
Q = pts[j]
distance_matrix[i, j] = np.sqrt((P[0]-Q[0])**2 + (P[1]-Q[1])**2)
distance_matrix[j, i] = distance_matrix[i, j]
max_distance = np.max(distance_matrix)
min_distance = np.min(distance_matrix[distance_matrix > 0])
return min_distance, max_distance, distance_matrix
# when epsilon slider changes
def update_eps(val):
eps1 = eps_slider.val
create_edges(eps1)
for i in range(len(circles)):
circles[i].set_radius(eps1)
fig.canvas.draw_idle()
axis_color = 'lightgoldenrodyellow'
bullet_size = 4
xmin, xmax = 0, 1
ymin, ymax = 0, 1
delta = .2
xlim = [xmin-delta, xmax+delta]
ylim = [ymin-delta, ymax+delta]
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25)
ax.axis([xlim[0], xlim[1], ylim[0], ylim[1]])
ax.set_aspect("equal")
N_0 = 3
N_max = 10
pts = create_points(N_0)
min_distance, max_distance, distance_matrix = create_distance_matrix(pts)
create_circles(N_0)
eps_0 = min_distance / 3
# create sliders
eps_slider_ax = fig.add_axes([0.25, 0.15, .65, 0.03], facecolor=axis_color)
N_slider_ax = fig.add_axes([0.25, 0.1, 0.65, 0.03], facecolor=axis_color)
eps_slider = Slider(eps_slider_ax, 'eps', min_distance/3, max_distance, valinit=eps_0)
N_slider = Slider(N_slider_ax, 'Num pts', 1, N_max, valinit=2, valfmt="%i")
eps_slider.on_changed(update_eps)
# Draw the initial plot
desenho = ax.scatter([x[0] for x in pts], [x[1] for x in pts], alpha=1, s=bullet_size)
plt.show()

How to animate a for loop with variable range

I get different plots for each value that I wrote in for loop's upper range as I expected. But I would like to animate the plot from range (0,0) an goes to (0,15) as the upper limit changes 1 by 1, by using matplotlib animation function animation.FuncAnimation(). So there will be 16 frames total in the animation. I messed up with the animation part, so I'm pasting the code that gives 1 plot output. Thanks in advance!
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
g = 1.0
def distance(x1,y1,x2,y2):
r = np.sqrt((x2-x1)**2+(y2-y1)**2)
return r
def gravit(m1,m2,r):
f = g*m1*m2/(r**2)
return f
def angle(y2, y1, x2, x1):
ydif = y1-y2
xdif = x1-x2
angle = np.arctan2(ydif,xdif)
return angle
m1 = 100
x1, y1 = 0,0
m2 = 1
x2, y2 = -15,-10
vx1 = 1
vy1 = 0
ax1 = 0
ay1 = 0
vx2 = 2
vy2 = 3.9
ax2 = 0
ay2 = 0
x1coor = [x1]
y1coor = [y1]
x2coor = [x2]
y2coor = [y2]
for t in range(0,10): #This value of 10 should be change from 0 to 15 in the animation
r = distance(x1,y1,x2,y2)
fx1 = gravit(m1, m2, r) * np.cos(angle(y2,y1,x2,x1))
fy1 = gravit(m1, m2, r) * np.sin(angle(y2,y1,x2,x1))
ax2 = fx1/m2
vx2 = vx2 + ax2
ay2 = fy1/m2
vy2 = vy2 + ay2
x2 = x2 + vx2 + 0.5*ax2
y2 = y2 + vy2 + 0.5*ay2
x1 = x1 + vx1 + 0.5*ax1
y1 = y1 + vy1 + 0.5*ay1
x1coor.append(x1)
y1coor.append(y1)
x2coor.append(x2)
y2coor.append(y2)
plt.axes().set_aspect('equal')
plt.axis([-30,30,-30,30])
plt.plot(x1coor,y1coor, '-.', color='blue')
plt.plot(x2coor,y2coor, '-.', color='black')
plt.scatter(x1,y1,s=m1*20, color='blue')
plt.scatter(x2,y2,s=m2*20, color='red')
The FuncAnimation function of matplotlib's animation module requires a figure and a function to draw each frame, so first initialize your figure following :
fig = plt.figure()
ax1 = fig.add_subplot(1, 1, 1)
ax1.set_aspect("equal")
ax1.set_xlim(-30, 30)
ax1.set_ylim(-30, 30)
l1, = ax1.plot([], [], linestyle = "-.", color = "blue")
l2, = ax1.plot([], [], '-.', color = "black")
s1, = ax1.plot([], [], linestyle = None, marker = "o", markersize = 5, color = "blue")
s2, = ax1.plot([], [], linestyle = None, marker = "o", markersize = 5, color = "red")
Note that the different line plots contain no input data and are saved in the variables l1, l2, s1 and s2.
Now define the function that will be called to draw each frame. The first argument is always the frame number. This function should return all the plot objects that are updated using their methods 'set_data':
def update_fig(i, x1coor, y1coor, x2coor, y2coor):
l1.set_data(x1coor[:i+1], y1coor[:i+1])
l2.set_data(x2coor[:i+1], y2coor[:i+1])
s1.set_data(x1coor[i], y1coor[i])
s2.set_data(x2coor[i], y2coor[i])
return l1, l2, s1, s2,
You can now run your animation (note that 'update_fig' arguments, except 'i', are passed to the function using the keyword 'fargs'):
ani = animation.FuncAnimation(fig, update_fig,
frames = len(x1coor),
fargs = (x1coor, y1coor, x2coor, y2coor),
interval = 100,
repeat = True,
)

Categories