Related
I am trying to generate a polynomial dataset. I wrote a code
def generate_dataset1():
n = 500
X = 2 - 3 * np.random.normal(0, 1, n)
y = X - 2 * (X ** 2) + 0.5 * ( X ** 3) + np.random.normal(-3, 3, n)
m = np.random.uniform(0.3, 0.5, (n, ))
b = np.random.uniform(5, 10, (n, ))
plt.scatter(X, y, s=10)
plt.show()
Now, if I want to generate a dataset using the given formula (from Wikipedia), could you tell me what I have to change?
y = B_0 + B_1*x, B_2*x2 + B_3*x3 + ... + e
Here, x2 means x (square), x3 means x (cube), so on and e is the unobserved random error with mean zero.
There are many ways to multiply x with B, such as dot product. But I think for loop should be good enough. Just loop through element of B and x:
def generate_dataset(B, n):
# B is beta, n is number of sample
e = np.random.normal(-3, 3, n)
X = 2 - 3 * np.random.normal(0, 1, n)
y = 0
for i in range(len(B)):
y += B[i] * X**i
y += e
return X, y
def plot_dataset(X, y):
#m = np.random.uniform(0.3, 0.5, (n, )) # not sure why you need this
#b = np.random.uniform(5, 10, (n, )) # not sure why you need this
plt.scatter(X, y, s=10)
plt.show()
n = 500
B = [0, 1, -2, 0.5] # [beta0, beta1, beta2, beta3]
X, y = generate_dataset(B, 500)
plot_dataset(X, y)
I have a radially symmetric function evaluated on a 3D Cartesian grid. How can I numerically calculate the radial derivative of the function?
For a simple example (spherical Gaussian), calculate derivatives df/dx, df/dy and df/dz:
# Parameters
start = 0
end = 5
n = 20
# Variables
x = np.linspace(start, end, num=n)
y = np.linspace(start, end, num=n)
z = np.linspace(start, end, num=n)
dx = (end - start) / n
dy = (end - start) / n
dz = (end - start) / n
x_grid, y_grid, z_grid = np.meshgrid(x, y, z)
eval_xyz = np.exp(-(x_grid ** 2 + y_grid ** 2 + z_grid ** 2))
# Allocate
df_dx = np.zeros((n, n, n))
df_dy = np.zeros((n, n, n))
df_dz = np.zeros((n, n, n))
# Calculate Cartesian gradient numerically
for x in range(eval_xyz.shape[0] - 1):
for y in range(eval_xyz.shape[1] - 1):
for z in range(eval_xyz.shape[2] - 1):
df_dx[x, y, z] = (eval_xyz[x + 1, y, z] - eval_xyz[x, y, z]) / dx
df_dy[x, y, z] = (eval_xyz[x, y + 1, z] - eval_xyz[x, y, z]) / dy
df_dz[x, y, z] = (eval_xyz[x, y, z + 1] - eval_xyz[x, y, z]) / dz
Is it then possible to easily calculate the radial derivative df/dr from the Cartesian derivatives?
The trick is to express the radial derivatives as sum of Cartesian derivatives, taking into account theta and phi at each point which can be expressed in Cartesian coordiantes as:
The code therefore becomes:
theta_val = theta(i * dx, j * dy, k * dz)
phi_val = phi(i * dx, j * dy)
df_dr[i, j, k] = df_dx[i, j, k] * np.sin(theta_val) * np.cos(phi_val) \
+ df_dy[i, j, k] * np.sin(theta_val) * np.sin(phi_val) \
+ df_dz[i, j, k] * np.cos(theta_val)
Where theta and phi are calculated carefully to deal with divide by zero
def theta(x, y, z):
if x == 0 and y == 0 and z == 0:
return 0
elif z == 0:
return np.pi / 2
elif x == 0 and y == 0:
return 0
else:
return np.arctan(np.sqrt(x ** 2 + y ** 2) / z)
def phi(x, y):
if x == 0 and y == 0:
return 0
elif x == 0:
return np.pi / 2
elif y == 0:
return 0
else:
return math.atan2(y, x)
Your own answer is a step in the right direction, but there are some issues both in the answer and in the code generating the Cartesian derivatives.
These lines have a problem:
x = np.linspace(start, end, num=n)
dx = (end - start) / n
The step size is actually (end-start)/(n-1).
Here:
x_grid, y_grid, z_grid = np.meshgrid(x, y, z)
df_dx[x, y, z] = (eval_xyz[x + 1, y, z] - eval_xyz[x, y, z]) / dx
you fell in the trap of meshgrid's default setting: meshgrid(np.arange(n1), np.arange(n2)) will return arrays in the shape (n2, n1) unless you add the parameter indexing='ij'. Because you have size n in all dimensions, you will not get indexing errors to alert you, but you might be spending a lot of time trying to debug why the numbers make no sense.
When you manipulate multidimensional arrays, it's a good idea to set the sizes in different directions to slightly different values, so that you can easily check that the array shapes are what you want them to be.
Also, you should generally evaluate the derivative as (f[i+1]-f[i-1])/(2*dx), which is correct up to the second order in x.
for x in range(eval_xyz.shape[0] - 1):
for y in range(eval_xyz.shape[1] - 1):
for z in range(eval_xyz.shape[2] - 1):
When working with numpy, you should always try to vectorize operations rather than writing out for loops that potentially need to iterate over thousands of elements.
Here is code that calculates the Cartesian derivative and then the radial derivative.
import numpy as np
def get_cartesian_gradient(f, xyzsteps):
"""For f shape (nx, ny, nz), return gradient as (3, nx, ny, nz) shape.
xyzsteps is a (3,) array.
Note: edge points of the gradient array are set to NaN.
(Exercise for the reader to implement those).
"""
fshape = f.shape
grad = np.full((3,) + fshape, np.nan, dtype=np.float64)
sl, sm, sr = slice(0, -2), slice(1, -1), slice(2, None)
# Note: multiplying is faster than dividing.
grad[0, sm, sm, sm] = (f[sr, sm, sm] - f[sl, sm, sm]) * (0.5/xyzsteps[0])
grad[1, sm, sm, sm] = (f[sm, sr, sm] - f[sm, sl, sm]) * (0.5/xyzsteps[1])
grad[2, sm, sm, sm] = (f[sm, sm, sr] - f[sm, sm, sl]) * (0.5/xyzsteps[2])
return grad
def get_dfdr_from_cartesian(grad, x1s, y1s, z1s):
"""Return df/dr array from gradient(f).
grad.shape must be (3, nx, ny, nz)
return shape (nx, ny, nz).
"""
_, nx, ny, nz = grad.shape
# we need sin(theta), cos(theta), sin(phi), and cos(phi)
# rxy: shape (nx, ny, 1)
rxy = np.sqrt(x1s.reshape(-1, 1, 1)**2 + y1s.reshape(1, -1, 1)**2)
# r: shape (nx, ny, nz)
r = np.sqrt(rxy**2 + z1s.reshape(1, 1, -1)**2)
# change zeros to NaN
r = np.where(r==0, np.nan, r)
rxy = np.where(rxy==0, np.nan, rxy)
cos_theta = z1s.reshape(1, 1, -1) / r
sin_theta = rxy / r
cos_phi = x1s.reshape(-1, 1, 1) / rxy
sin_phi = y1s.reshape(1, -1, 1) / rxy
# and the derivative
dfdr = (grad[0]*cos_phi + grad[1]*sin_phi)*sin_theta + grad[2]*cos_theta
return dfdr
x1s = np.linspace(-1, 1, 19)
y1s = np.linspace(-1, 1, 21)
z1s = np.linspace(-1, 1, 23)
xs, ys, zs = np.meshgrid(x1s, y1s, z1s, indexing='ij')
xyzsteps = [x1s[1]-x1s[0], y1s[1]-y1s[0], z1s[1]-z1s[0]]
def func(x, y, z):
return x**2 + y**2 + z**2
def dfdr_analytical(x, y, z):
r = np.sqrt(x**2 + y**2 + z**2)
return 2*r
# grad has shape (3, nx, ny, nz)
grad = get_cartesian_gradient(func(xs, ys, zs), xyzsteps)
dfdr = get_dfdr_from_cartesian(grad, x1s, y1s, z1s)
# test
diff = dfdr - dfdr_analytical(xs, ys, zs)
assert np.nanmax(np.abs(diff)) < 1e-14
Note that I've chosen to return NaN values for points on the z-axis, because df/dr is not defined there unless f(x,y,z) is rotationally symmetric around the z-axis and has df/dr=0 in all directions. This is something that is not guaranteed for an arbitrary dataset.
The reason for replacing zeros in the denominators by np.nan using np.where is because dividing by zero will give warning messages, whereas dividing by nan won't.
if I had an equation with more than one variable, lets say
x**2+x*y+y**2, I could find Coefficients with CoefficientList from Mathematica. It would print the desired matrix as 3x3.
In Python, I found sympy.coeff(x, n) but I can not return the coefficient for more than one variable.
Is there a way you know?
For the quadratic case, the following matches the output of Mathematica:
def CoefficientList(p, v):
"""
>>> CoefficientList(x**2+2*x*y+3*y**2+4*x+5*y+6,(x,y))
Matrix([
[6, 5, 3],
[4, 2, 0],
[1, 0, 0]])
"""
assert len(v) == 2
n = len(v)
t = [prod(i) for i in subsets([S.One] + list(v), n, repetition=n)]
p = p.expand()
m = zeros(n + 1)
r = n + 1
while t:
if r > n:
n -= 1
r = 0
c = n
x = t.pop()
if x == 1:
d = p
else:
p, d = p.as_independent(x, as_Add=True)
co = d.as_coeff_Mul()[0]
m[r, c] = co
r += 1
c -= 1
return m
But the monomials method is a good one to use. To get all the coefficients of all monomials I would recommend storing them in a defaultdict with a default value of 0. You can then retrieve the coefficients as you wish:
def coeflist(p, v):
"""
>>> coeflist(x**2+2*x*y+3*y**2+4*x+5*y+6, [x])
defaultdict(<class 'int'>, {x**2: 1, x: 2*y + 4, 1: 3*y**2 + 5*y + 6})
>>> coeflist(x**2+2*x*y+3*y**2+4*x+5*y+6, [x, y])
defaultdict(<class 'int'>, {x**2: 1, x*y: 2, x: 4, y**2: 3, y: 5, 1: 6})
>>> _[y**2]
3
"""
from collections import defaultdict
p = Poly(p, *v)
rv = defaultdict(int)
for i in p.monoms():
rv[prod(i**j for i,j in zip(p.gens, i))] = p.coeff_monomial(i)
return rv
Here is a way to find each of the coefficients of the quadratic form and represent them as a matrix:
import sympy as sy
from sympy.abc import x, y
def quadratic_form_matrix(expr, x, y):
a00, axx, ayy, ax, ay, axy = sy.symbols('a00 axx ayy ax ay axy')
quad_coeffs = sy.solve([sy.Eq(expr.subs({x: 0, y: 0}), a00),
sy.Eq(expr.diff(x).subs({x: 0, y: 0}), 2 * ax),
sy.Eq(expr.diff(x, 2).subs({y: 0}), 2 * axx),
sy.Eq(expr.diff(y).subs({x: 0, y: 0}), 2 * ay),
sy.Eq(expr.diff(y, 2).subs({x: 0}), 2 * ayy),
sy.Eq(expr.diff(x).diff(y), 2 * axy)],
(a00, axx, ayy, ax, ay, axy))
return sy.Matrix([[axx, axy, ax], [axy, ayy, ay], [ax, ay, a00]]).subs(quad_coeffs)
expr = x**2 + 2*x*y + 3*y**2 + 7
M = quadratic_form_matrix(expr, x, y)
print(M)
XY1 = sy.Matrix([x, y, 1])
quadatric_form = (XY1.T * M * XY1)[0]
print(quadatric_form.expand())
PS: Applying a conversion to a multivariate polygon as suggested #Marcus' reference, and then converting to a matrix would result in following code. Note that to get the constant term, 1 can be passed to coeff_monomial(1). The non-diagonal elements of the symmetric matrix for the quadratic form need to be half of the corresponding coefficients.
import sympy as sy
from sympy.abc import x, y
def quadratic_form_matrix_using_poly(expr, x, y):
p = sy.poly(expr)
axx = p.coeff_monomial(x * x)
ayy = p.coeff_monomial(y * y)
a00 = p.coeff_monomial(1)
ax = p.coeff_monomial(x) / 2
ay = p.coeff_monomial(y) / 2
axy = p.coeff_monomial(x * y) / 2
return sy.Matrix([[axx, axy, ax], [axy, ayy, ay], [ax, ay, a00]])
expr = x**2 + 2*x*y + 3*y**2 + 7 + 11*x + 23*y
M = quadratic_form_matrix(expr, x, y)
print(M)
XY1 = sy.Matrix([x, y, 1])
quadatric_form = (XY1.T * M * XY1)[0]
print(quadatric_form.expand())
I was trying to optimize this function using Numba, but I am unable to do it. I think this has no part of the code which can be accelerated. If anyone can help me with an optimized version of this, My program would become blazing fast. Please tell if any dataset or other info is needed. When I apply direct #jit on this, It is not working.
def c_a(x, y, z, counter, p_l):
# start = time.time()
if counter == 1:
l = x
m = y
n = z
path = "c_r.pdb"
global r_a_t
p = Bio.PDB.PDBParser()
structure = p.get_structure('mSN1', path)
c_r = [a.get_coord() for a in structure.get_atoms()]
lengthnew = len(c_r)
m_d = np.array([-45, -45, -45])
a_s_r = np.zeros((128, 128, 128), np.complex)
for i in range(0, lengthnew):
x = int(math.floor((c_r[i][0] - m_d[0]) / 1.2))
y = int(math.floor((c_r[i][1] - m_d[1]) / 1.2))
z = int(math.floor((c_r[i][2] - m_d[2]) / 1.2))
with open("Ei.txt", 'r') as ei_values:
for row in ei_values:
s_v = row.split()
if s_v[0] == r_a_t[i] :
a_s_r[x, y, z] = np.complex(s_v[1])
n_n = lambda x, y, z : [(x2, y2, z2) for x2 in range(x - 5, x + 6)
for y2 in range(y - 5, y + 6)
for z2 in range(z - 5, z + 6)
if (-1 < x < X and
-1 < y < Y and
-1 < z < Z and
(x != x2 or y != y2 or z != z2) and
(0 <= x2 < X) and
(0 <= y2 < Y) and
(0 <= z2 < Z) and
((( abs(x - x2)) ** 2 + (abs(y - y2)) ** 2 + (abs(z - z2)) ** 2 ) <= 25))]
m = n_n(l, m, n)
result = 0
for i in range(0, len(m)):
a = m[i][0]
b = m[i][1]
c = m[i][2]
result = result + a_s_r[a][b][c]
return result
else:
l = x
m = y
n = z
path = p_l
global l_a_t
p = Bio.PDB.PDBParser()
structure = p.get_structure('mSN1', path)
c_l = [a.get_coord() for a in structure.get_atoms()]
lengthnew = len(c_l)
m_d = np.array([-45, -45, -45])
a_s_l = np.zeros((128, 128, 128), np.complex)
for i in range(0, lengthnew):
x = int(math.floor((c_l[i][0] - m_d[0]) / 1.2))
y = int(math.floor((c_l[i][1] - m_d[1]) / 1.2))
z = int(math.floor((c_l[i][2] - m_d[2]) / 1.2))
with open("E.txt", 'r') as e_v:
for row in e_v:
s_v = row.split()
if s_v[0] == l_a_t[i] :
a_s_l[x, y, z] = np.complex(s_v[1])
n_n = lambda x, y, z : [(x2, y2, z2) for x2 in range(x - 5, x + 6)
for y2 in range(y - 5, y + 6)
for z2 in range(z - 5, z + 6)
if (-1 < x < X and
-1 < y < Y and
-1 < z < Z and
(x != x2 or y != y2 or z != z2) and
(0 <= x2 < X) and
(0 <= y2 < Y) and
(0 <= z2 < Z) and
(((abs(x - x2)) ** 2 + (abs(y - y2)) ** 2 + (abs(z - z2)) ** 2 ) <= 25))]
m = n_n(l, m, n)
result = 0
for i in range(0, len(m)):
a = m[i][0]
b = m[i][1]
c = m[i][2]
result = result + a_s_l[a][b][c]
# print "c_a : ", time.time() - start
return result
Solved.
Brought out all the file reading steps outside the function, as they were being executed many times. It gave a 70x boost.
Just left the lambda functions in the function as they are dependent on x, y & z.
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.