I have checked python non linear ODE with 2 variables , which is not my case. Maybe my case is not called as nonlinear ODE, correct me please.
The question isFrenet Frame actually, in which there are 3 vectors T(s), N(s) and B(s); the parameter s>=0. And there are 2 scalar with known math formula expression t(s) and k(s). I have the initial value T(0), N(0) and B(0).
diff(T(s), s) = k(s)*N(s)
diff(N(s), s) = -k(s)*T(s) + t(s)*B(s)
diff(B(s), s) = -t(s)*N(s)
Then how can I get T(s), N(s) and B(s) numerically or symbolically?
I have checked scipy.integrate.ode but I don't know how to pass k(s)*N(s) into its first parameter at all
def model (z, tspan):
T = z[0]
N = z[1]
B = z[2]
dTds = k(s) * N # how to express function k(s)?
dNds = -k(s) * T + t(s) * B
dBds = -t(s)* N
return [dTds, dNds, dBds]
z = scipy.integrate.ode(model, [T0, N0, B0]
Here is a code using solve_ivp interface from Scipy (instead of odeint) to obtain a numerical solution:
import numpy as np
from scipy.integrate import solve_ivp
from scipy.integrate import cumtrapz
import matplotlib.pylab as plt
# Define the parameters as regular Python function:
def k(s):
return 1
def t(s):
return 0
# The equations: dz/dt = model(s, z):
def model(s, z):
T = z[:3] # z is a (9, ) shaped array, the concatenation of T, N and B
N = z[3:6]
B = z[6:]
dTds = k(s) * N
dNds = -k(s) * T + t(s) * B
dBds = -t(s)* N
return np.hstack([dTds, dNds, dBds])
T0, N0, B0 = [1, 0, 0], [0, 1, 0], [0, 0, 1]
z0 = np.hstack([T0, N0, B0])
s_span = (0, 6) # start and final "time"
t_eval = np.linspace(*s_span, 100) # define the number of point wanted in-between,
# It is not necessary as the solver automatically
# define the number of points.
# It is used here to obtain a relatively correct
# integration of the coordinates, see the graph
# Solve:
sol = solve_ivp(model, s_span, z0, t_eval=t_eval, method='RK45')
print(sol.message)
# >> The solver successfully reached the end of the integration interval.
# Unpack the solution:
T, N, B = np.split(sol.y, 3) # another way to unpack the z array
s = sol.t
# Bonus: integration of the normal vector in order to get the coordinates
# to plot the curve (there is certainly better way to do this)
coords = cumtrapz(T, x=s)
plt.plot(coords[0, :], coords[1, :]);
plt.axis('equal'); plt.xlabel('x'); plt.xlabel('y');
T, N and B are vectors. Therefore, there are 9 equations to solve: z is a (9,) array.
For constant curvature and no torsion, the result is a circle:
thanks for your example. And I thought it again, found that since there is formula for dZ where Z is matrix(T, N, B), we can calculate Z[i] = Z[i-1] + dZ[i-1]*deltaS according to the concept of derivative. Then I code and find this idea can solve the circle example. So
is Z[i] = Z[i-1] + dZ[i-1]*deltaS suitable for other ODE? will it fail in some situation, or does scipy.integrate.solve_ivp/scipy.integrate.ode supply advantage over the direct usage of Z[i] = Z[i-1] + dZ[i-1]*deltaS?
in my code, I have to normalize Z[i] because ||Z[i]|| is not always 1. Why does it happen? A float numerical calculation error?
my answer to my question, at least it works for the circle
import numpy as np
from scipy.integrate import cumtrapz
import matplotlib.pylab as plt
# Define the parameters as regular Python function:
def k(s):
return 1
def t(s):
return 0
def dZ(s, Z):
return np.array(
[k(s) * Z[1], -k(s) * Z[0] + t(s) * Z[2], -t(s)* Z[1]]
)
T0, N0, B0 = np.array([1, 0, 0]), np.array([0, 1, 0]), np.array([0, 0, 1])
deltaS = 0.1 # step to calculate dZ/ds
num = int(2*np.pi*1/deltaS) + 1 # how many points on the curve we have to calculate
T = np.zeros([num, ], dtype=object)
N = np.zeros([num, ], dtype=object)
B = np.zeros([num, ], dtype=object)
T[0] = T0
N[0] = N0
B[0] = B0
for i in range(num-1):
temp_dZ = dZ(i*deltaS, np.array([T[i], N[i], B[i]]))
T[i+1] = T[i] + temp_dZ[0]*deltaS
T[i+1] = T[i+1]/np.linalg.norm(T[i+1]) # have to do this
N[i+1] = N[i] + temp_dZ[1]*deltaS
N[i+1] = N[i+1]/np.linalg.norm(N[i+1])
B[i+1] = B[i] + temp_dZ[2]*deltaS
B[i+1] = B[i+1]/np.linalg.norm(B[i+1])
coords = cumtrapz(
[
[i[0] for i in T], [i[1] for i in T], [i[2] for i in T]
]
, x=np.arange(num)*deltaS
)
plt.figure()
plt.plot(coords[0, :], coords[1, :]);
plt.axis('equal'); plt.xlabel('x'); plt.xlabel('y');
plt.show()
I found that the equation I listed in the first post does not work for my curve. So I read Gray A., Abbena E., Salamon S-Modern Differential Geometry of Curves and Surfaces with Mathematica. 2006 and found that for arbitrary curve, Frenet equation should be written as
diff(T(s), s) = ||r'||* k(s)*N(s)
diff(N(s), s) = ||r'||*(-k(s)*T(s) + t(s)*B(s))
diff(B(s), s) = ||r'||* -t(s)*N(s)
where ||r'||(or ||r'(s)||) is diff([x(s), y(s), z(s)], s).norm()
now the problem has changed to be some different from that in the first post, because there is no r'(s) function or discrete data array. So I think this is suitable for a new reply other than comment.
I met 2 questions while trying to solve the new equation:
how can we program with r'(s) if scipy's solve_ivp is used?
I try to modify my gaussian solution, but the result is totally wrong.
thanks again
import numpy as np
from scipy.integrate import cumtrapz
import matplotlib.pylab as plt
# Define the parameters as regular Python function:
def k(s):
return 1
def t(s):
return 0
def dZ(s, Z, r_norm):
return np.array([
r_norm * k(s) * Z[1],
r_norm*(-k(s) * Z[0] + t(s) * Z[2]),
r_norm*(-t(s)* Z[1])
])
T0, N0, B0 = np.array([1, 0, 0]), np.array([0, 1, 0]), np.array([0, 0, 1])
deltaS = 0.1 # step to calculate dZ/ds
num = int(2*np.pi*1/deltaS) + 1 # how many points on the curve we have to calculate
T = np.zeros([num, ], dtype=object)
N = np.zeros([num, ], dtype=object)
B = np.zeros([num, ], dtype=object)
R0 = N0
T[0] = T0
N[0] = N0
B[0] = B0
for i in range(num-1):
r_norm = np.linalg.norm(R0)
temp_dZ = dZ(i*deltaS, np.array([T[i], N[i], B[i]]), r_norm)
T[i+1] = T[i] + temp_dZ[0]*deltaS
T[i+1] = T[i+1]/np.linalg.norm(T[i+1])
N[i+1] = N[i] + temp_dZ[1]*deltaS
N[i+1] = N[i+1]/np.linalg.norm(N[i+1])
B[i+1] = B[i] + temp_dZ[2]*deltaS
B[i+1] = B[i+1]/np.linalg.norm(B[i+1])
R0 = R0 + T[i]*deltaS
coords = cumtrapz(
[
[i[0] for i in T], [i[1] for i in T], [i[2] for i in T]
]
, x=np.arange(num)*deltaS
)
plt.figure()
plt.plot(coords[0, :], coords[1, :]);
plt.axis('equal'); plt.xlabel('x'); plt.xlabel('y');
plt.show()
Related
I'm trying to implement SABR volatility model using sdeint package. Basically, the model consists of two equations
dx1 = 0*x1*dt + x2*x1^b*dWt
dx2 = 0*x2*dt + a*x2*dZt
I tried to rewrite the second example of the official documentation, but I encountered the following issue:
SDEValueError: y0 has length 2. So G must either be a single function
returning a matrix of shape (2, m), or else a list of m separate
functions each returning a column of G, with shape (2,)
My code
import numpy as np
import sdeint
A = np.array([[0, 0],
[ 0, 0]])
a = 1
b = 0.5
B = np.diag([1, a]).T
tspan = np.linspace(0.0, 10.0, 10001)
x0 = np.array([3.0, 3.0])
def f(x, t):
return A.dot(x)
def G(x, t):
BB = np.array([ x[1]*x[0]**b, x[1] ])
print(BB)
return B.dot(BB)
result = sdeint.itoint(f, G, x0, tspan)
Basically, it should be a square diagonal matrix in your case:
def G(x, t):
BB = np.diag([ x[1]*x[0]**b, x[1] ])
return BB
I have 2 Points A and B and their xyz-coordinates. I need a list of all xyz points that are on the line between those 2 points. The Bresenham's line algorithm was too slow for my case.
Example xyz for A and B:
p = np.array([[ 275.5, 244.2, -27.3],
[ 153.2, 184.3, -0.3]])
Expected output:
x3 = p[0,0] + t*(p[1,0]-p[0,0])
y3 = p[0,1] + t*(p[1,1]-p[0,1])
z3 = p[0,2] + t*(p[1,2]-p[0,2])
p3 = [x3,y3,z3]
There was a very fast approach for 2D:
def connect(ends):
d0, d1 = np.diff(ends, axis=0)[0]
if np.abs(d0) > np.abs(d1):
return np.c_[np.arange(ends[0, 0], ends[1,0] + np.sign(d0), np.sign(d0), dtype=np.int32),
np.arange(ends[0, 1] * np.abs(d0) + np.abs(d0)//2,
ends[0, 1] * np.abs(d0) + np.abs(d0)//2 + (np.abs(d0)+1) * d1, d1, dtype=np.int32) // np.abs(d0)]
else:
return np.c_[np.arange(ends[0, 0] * np.abs(d1) + np.abs(d1)//2,
ends[0, 0] * np.abs(d1) + np.abs(d1)//2 + (np.abs(d1)+1) * d0, d0, dtype=np.int32) // np.abs(d1),
np.arange(ends[0, 1], ends[1,1] + np.sign(d1), np.sign(d1), dtype=np.int32)]
It is impossible to give "all" points on a line, as there are countless points. You could do that for a discrete data type like integers, though.
My answer assumes floating point numbers, as in your example.
Technically, floating point numbers are stored in a binary format with a fixed width, so they are discrete, but I will disregard that fact, as it is most likely not what you want.
As you already typed in your question, every point P on that line satisfies this equation:
P = P1 + t * (P2 - P1), 0 < t < 1
My version uses numpy broadcasting to circumvent explicit loops.
import numpy as np
p = np.array([[ 275.5, 244.2, -27.3],
[ 153.2, 184.3, -0.3]])
def connect(points, n_points):
p1, p2 = points
diff = p2 - p1
t = np.linspace(0, 1, n_points+2)[1:-1]
return p1[np.newaxis, :] + t[:, np.newaxis] * diff[np.newaxis, :]
print(connect(p, n_points=4))
# [[251.04 232.22 -21.9 ]
# [226.58 220.24 -16.5 ]
# [202.12 208.26 -11.1 ]
# [177.66 196.28 -5.7 ]]
Maybe I missunderstand something here, but couldn't you just create a function (like you already did just for one single point) and then create a list of points depending of how manny points you want?
I mean between 2 points are an infinite number of other points, so you have to either define a number or use the function directly, which describes where the points are at.
import numpy as np
p = np.array([[ 275.5, 244.2, -27.3],
[ 153.2, 184.3, -0.3]])
def gen_line(p, n):
points = []
stepsize = 1/n
for t in np.arange(0,1,stepsize):
x = (p[1,0]-p[0,0])
y = (p[1,1]-p[0,1])
z = (p[1,2]-p[0,2])
px = p[0,0]
py = p[0,1]
pz = p[0,2]
x3 = px + t*x
y3 = py + t*y
z3 = pz + t*z
points.append([x3,y3,z3])
return points
# generates list of 30k points
gen_line(p, 30000)
Practicing finite difference implementation and I cannot figure out why my solution looks so strange. Code taken from: http://people.bu.edu/andasari/courses/numericalpython/Week9Lecture15/PythonFiles/FTCS_DirichletBCs.py.
Note: I'm using this lecture example for the heat equation not the reaction-diffusion equation!
I haven't learned the relevant mathematics so this could be why!
My code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
import math as mth
from mpl_toolkits.mplot3d import Axes3D
import pylab as plb
import scipy as sp
import scipy.sparse as sparse
import scipy.sparse.linalg
# First start with diffusion equation with initial condition u(x, 0) = 4x - 4x^2 and u(0, t) = u(L, t) = 0
# First discretise the domain [0, L] X [0, T]
# Then discretise the derivatives
# Generate algorithm:
# 1. Compute initial condition for all i
# 2. For all n:
# 2i. Compute u_i^{n + 1} for internal space points
# 2ii. Set boundary values for i = 0 and i = N_x
M = 40 # number of grid points for space interval
N = 70 # '' '' '' '' '' time ''
x0 = 0
xL = 1 # unit grid differences
dx = (xL - x0) / (M - 1) # space step
t0 = 0
tF = 0.2
dt = (tF - t0) / (N - 1)
D = 0.3 # thermal diffusivity
a = D * dt / dx**2
# Create grid
tspan = np.linspace(t0, tF, N)
xspan = np.linspace(x0, xL, M)
# Initial matrix solution
U = np.zeros((M, N))
# Initial condition
U[:, 0] = 4*xspan - 4*xspan**2
# Boundary conditions
U[0, :] = 0
U[-1, 0] = 0
# Discretised derivative formula
for k in range(0, N-1):
for i in range(1, M-1):
U[i, k+1] = a * U[i-1, k] + (1 - 2 * a) * U[i, k] + a * U[i + 1, k]
X, T = np.meshgrid(tspan, xspan)
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, T, U, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
ax.set_xticks([0, 0.05, 0.1, 0.15, 0.2])
ax.set_xlabel('Space')
ax.set_ylabel('Time')
ax.set_zlabel('U')
plt.tight_layout()
plt.show()
edit: Changed therm diff value to correct one.
The main problem is the time step length. If you look at the differential equation, the numerics become unstable for a>0.5. Translated this means for you that roughly N > 190. I get a nice picture if I increase your N to such value.
However, I thing somewhere the time and space axes are swapped (if you try to interpret the graph then, i.e. boundary conditions and expected dampening of profile over time). I cannot figure out right now why.
Edit: Actually, you swap T and X when you do meshgrid. This should work:
N = 200
...
...
T, X = np.meshgrid(tspan, xspan)
...
surf = ax.plot_surface(T, X, U, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
...
ax.set_xlabel('Time')
ax.set_ylabel('Space')
I am new in Python and I have a sphere of radius (R) and centred at (x0,y0,z0). Now, I need to find those points which are either on the surface of the sphere or inside the sphere e.g. points (x1,y1,z1) which satisfy ((x1-x0)**2+(y1-y0)**2+(z1-x0)*82)**1/2 <= R. I would like to print only those point's coordinates in a form of numpy array. Output would be something like this-[[x11,y11,z11],[x12,y12,z12],...]. I have the following code so far-
import numpy as np
import math
def create_points_around_atom(number,atom_coordinates):
n= number
x0 = atom_coordinates[0]
y0 = atom_coordinates[1]
z0 = atom_coordinates[2]
R = 1.2
for i in range(n):
phi = np.random.uniform(0,2*np.pi,size=(n,))
costheta = np.random.uniform(-1,1,size=(n,))
u = np.random.uniform(0,1,size=(n,))
theta = np.arccos(costheta)
r = R * np.cbrt(u)
x1 = r*np.sin(theta)*np.cos(phi)
y1 = r*np.sin(theta)*np.sin(phi)
z1 = r*np.cos(theta)
dist = np.sqrt((x1-x0)**2+(y1-y0)**2+(z1-z0)**2)
distance = list(dist)
point_on_inside_sphere = []
for j in distance:
if j <= R:
point_on_inside_sphere.append(j)
print('j:',j,'\tR:',R)
print('The list is:', point_on_inside_sphere)
print(len(point_on_inside_sphere))
kk =0
for kk in range(len(point_on_inside_sphere)):
for jj in point_on_inside_sphere:
xx = np.sqrt(jj**2-y1**2-z1**2)
yy = np.sqrt(jj**2-x1**2-z1**2)
zz = np.sqrt(jj**2-y1**2-x1**2)
print("x:", xx, "y:", yy,"z:", zz)
kk +=1
And I am running it-
create_points_around_atom(n=2,structure[1].coords)
where, structure[1].coords is a numpy array of three coordinates.
To sum up what has been discussed in the comments, and some other points:
There is no need to filter the points because u <= 1, which means np.cbrt(u) <= 1 and hence r = R * np.cbrt(u) <= R, i.e. all points will already be inside or on the surface of the sphere.
Calling np.random.uniform with size=(n,) creates an array of n elements, so there's no need to do this n times in a loop.
You are filtering distances from the atom_coordinate, but the points you are generating are centered on [0, 0, 0], because you are not adding this offset.
Passing R as an argument seems more sensible than hard-coding it.
There's no need to "pre-load" arguments in Python like one would sometimes do in C.
Since sin(theta) is non-negative over the sphere, you can directly calculate it from the costheta array using the identity cosĀ²(x) + sinĀ²(x) = 1.
Sample implementation:
# pass radius as an argument
def create_points_around_atom(number, center, radius):
# generate the random quantities
phi = np.random.uniform( 0, 2*np.pi, size=(number,))
theta_cos = np.random.uniform(-1, 1, size=(number,))
u = np.random.uniform( 0, 1, size=(number,))
# calculate sin(theta) from cos(theta)
theta_sin = np.sqrt(1 - theta_cos**2)
r = radius * np.cbrt(u)
# use list comprehension to generate the coordinate array without a loop
# don't forget to offset by the atom's position (center)
return np.array([
np.array([
center[0] + r[i] * theta_sin[i] * np.cos(phi[i]),
center[1] + r[i] * theta_sin[i] * np.sin(phi[i]),
center[2] + r[i] * theta_cos[i]
]) for i in range(number)
])
Is there something like Matlab's procrustes function in NumPy/SciPy or related libraries?
For reference. Procrustes analysis aims to align 2 sets of points (in other words, 2 shapes) to minimize square distance between them by removing scale, translation and rotation warp components.
Example in Matlab:
X = [0 1; 2 3; 4 5; 6 7; 8 9]; % first shape
R = [1 2; 2 1]; % rotation matrix
t = [3 5]; % translation vector
Y = X * R + repmat(t, 5, 1); % warped shape, no scale and no distortion
[d Z] = procrustes(X, Y); % Z is Y aligned back to X
Z
Z =
0.0000 1.0000
2.0000 3.0000
4.0000 5.0000
6.0000 7.0000
8.0000 9.0000
Same task in NumPy:
X = arange(10).reshape((5, 2))
R = array([[1, 2], [2, 1]])
t = array([3, 5])
Y = dot(X, R) + t
Z = ???
Note: I'm only interested in aligned shape, since square error (variable d in Matlab code) is easily computed from 2 shapes.
I'm not aware of any pre-existing implementation in Python, but it's easy to take a look at the MATLAB code using edit procrustes.m and port it to Numpy:
def procrustes(X, Y, scaling=True, reflection='best'):
"""
A port of MATLAB's `procrustes` function to Numpy.
Procrustes analysis determines a linear transformation (translation,
reflection, orthogonal rotation and scaling) of the points in Y to best
conform them to the points in matrix X, using the sum of squared errors
as the goodness of fit criterion.
d, Z, [tform] = procrustes(X, Y)
Inputs:
------------
X, Y
matrices of target and input coordinates. they must have equal
numbers of points (rows), but Y may have fewer dimensions
(columns) than X.
scaling
if False, the scaling component of the transformation is forced
to 1
reflection
if 'best' (default), the transformation solution may or may not
include a reflection component, depending on which fits the data
best. setting reflection to True or False forces a solution with
reflection or no reflection respectively.
Outputs
------------
d
the residual sum of squared errors, normalized according to a
measure of the scale of X, ((X - X.mean(0))**2).sum()
Z
the matrix of transformed Y-values
tform
a dict specifying the rotation, translation and scaling that
maps X --> Y
"""
n,m = X.shape
ny,my = Y.shape
muX = X.mean(0)
muY = Y.mean(0)
X0 = X - muX
Y0 = Y - muY
ssX = (X0**2.).sum()
ssY = (Y0**2.).sum()
# centred Frobenius norm
normX = np.sqrt(ssX)
normY = np.sqrt(ssY)
# scale to equal (unit) norm
X0 /= normX
Y0 /= normY
if my < m:
Y0 = np.concatenate((Y0, np.zeros(n, m-my)),0)
# optimum rotation matrix of Y
A = np.dot(X0.T, Y0)
U,s,Vt = np.linalg.svd(A,full_matrices=False)
V = Vt.T
T = np.dot(V, U.T)
if reflection != 'best':
# does the current solution use a reflection?
have_reflection = np.linalg.det(T) < 0
# if that's not what was specified, force another reflection
if reflection != have_reflection:
V[:,-1] *= -1
s[-1] *= -1
T = np.dot(V, U.T)
traceTA = s.sum()
if scaling:
# optimum scaling of Y
b = traceTA * normX / normY
# standarised distance between X and b*Y*T + c
d = 1 - traceTA**2
# transformed coords
Z = normX*traceTA*np.dot(Y0, T) + muX
else:
b = 1
d = 1 + ssY/ssX - 2 * traceTA * normY / normX
Z = normY*np.dot(Y0, T) + muX
# transformation matrix
if my < m:
T = T[:my,:]
c = muX - b*np.dot(muY, T)
#transformation values
tform = {'rotation':T, 'scale':b, 'translation':c}
return d, Z, tform
There is a Scipy function for it: scipy.spatial.procrustes
I'm just posting its example here:
>>> import numpy as np
>>> from scipy.spatial import procrustes
>>> a = np.array([[1, 3], [1, 2], [1, 1], [2, 1]], 'd')
>>> b = np.array([[4, -2], [4, -4], [4, -6], [2, -6]], 'd')
>>> mtx1, mtx2, disparity = procrustes(a, b)
>>> round(disparity)
0.0
You can have both Ordinary Procrustes Analysis and Generalized Procrustes Analysis in python with something like this:
import numpy as np
def opa(a, b):
aT = a.mean(0)
bT = b.mean(0)
A = a - aT
B = b - bT
aS = np.sum(A * A)**.5
bS = np.sum(B * B)**.5
A /= aS
B /= bS
U, _, V = np.linalg.svd(np.dot(B.T, A))
aR = np.dot(U, V)
if np.linalg.det(aR) < 0:
V[1] *= -1
aR = np.dot(U, V)
aS = aS / bS
aT-= (bT.dot(aR) * aS)
aD = (np.sum((A - B.dot(aR))**2) / len(a))**.5
return aR, aS, aT, aD
def gpa(v, n=-1):
if n < 0:
p = avg(v)
else:
p = v[n]
l = len(v)
r, s, t, d = np.ndarray((4, l), object)
for i in range(l):
r[i], s[i], t[i], d[i] = opa(p, v[i])
return r, s, t, d
def avg(v):
v_= np.copy(v)
l = len(v_)
R, S, T = [list(np.zeros(l)) for _ in range(3)]
for i, j in np.ndindex(l, l):
r, s, t, _ = opa(v_[i], v_[j])
R[j] += np.arccos(min(1, max(-1, np.trace(r[:1])))) * np.sign(r[1][0])
S[j] += s
T[j] += t
for i in range(l):
a = R[i] / l
r = [np.cos(a), -np.sin(a)], [np.sin(a), np.cos(a)]
v_[i] = v_[i].dot(r) * (S[i] / l) + (T[i] / l)
return v_.mean(0)
For testing purposes, the output of each algorithm can be visualized as follows:
import matplotlib.pyplot as p; p.rcParams['toolbar'] = 'None';
def plt(o, e, b):
p.figure(figsize=(10, 10), dpi=72, facecolor='w').add_axes([0.05, 0.05, 0.9, 0.9], aspect='equal')
p.plot(0, 0, marker='x', mew=1, ms=10, c='g', zorder=2, clip_on=False)
p.gcf().canvas.set_window_title('%f' % e)
x = np.ravel(o[0].T[0])
y = np.ravel(o[0].T[1])
p.xlim(min(x), max(x))
p.ylim(min(y), max(y))
a = []
for i, j in np.ndindex(len(o), 2):
a.append(o[i].T[j])
O = p.plot(*a, marker='x', mew=1, ms=10, lw=.25, c='b', zorder=0, clip_on=False)
O[0].set(c='r', zorder=1)
if not b:
O[2].set_color('b')
O[2].set_alpha(0.4)
p.axis('off')
p.show()
# Fly wings example (Klingenberg, 2015 | https://en.wikipedia.org/wiki/Procrustes_analysis)
arr1 = np.array([[588.0, 443.0], [178.0, 443.0], [56.0, 436.0], [50.0, 376.0], [129.0, 360.0], [15.0, 342.0], [92.0, 293.0], [79.0, 269.0], [276.0, 295.0], [281.0, 331.0], [785.0, 260.0], [754.0, 174.0], [405.0, 233.0], [386.0, 167.0], [466.0, 59.0]])
arr2 = np.array([[477.0, 557.0], [130.129, 374.307], [52.0, 334.0], [67.662, 306.953], [111.916, 323.0], [55.119, 275.854], [107.935, 277.723], [101.899, 259.73], [175.0, 329.0], [171.0, 345.0], [589.0, 527.0], [591.0, 468.0], [299.0, 363.0], [306.0, 317.0], [406.0, 288.0]])
def opa_out(a):
r, s, t, d = opa(a[0], a[1])
a[1] = a[1].dot(r) * s + t
return a, d, False
plt(*opa_out([arr1, arr2, np.matrix.copy(arr2)]))
def gpa_out(a):
g = gpa(a, -1)
D = [avg(a)]
for i in range(len(a)):
D.append(a[i].dot(g[0][i]) * g[1][i] + g[2][i])
return D, sum(g[3])/len(a), True
plt(*gpa_out([arr1, arr2]))
Probably you want to try this package with various flavors of different Procrustes methods, https://github.com/theochem/procrustes.