Python: Expand 2D array to multiple 1D arrays - python

Consider the followoing example from np.meshgrid docs:
nx, ny = (3, 2)
x = np.linspace(0, 1, nx)
y = np.linspace(0, 1, ny)
xv, yv = np.meshgrid(x, y)
In my application, instead of x and y, I've 25 variables. To create a grid out of the 25 variables, one way would be:
v1 = np.linspace(0, 1, 10)
v2 = np.linspace(0, 1, 10)
...
v25 = np.linspace(0, 1, 10)
z_grid = np.meshgrid(v1, v2, ..., v25)
However, the code will look ugly and not modular w.r.t. the number of variables (since each variable is hard-coded). Therefore, I am interested in something like the following:
n_variables = 25
z = np.array([np.linspace(0, 1, 10)] * n_variables)
z_grid = np.dstack(np.meshgrid(z))
However, I am guessing meshgrid(z) is not the correct call, and I should expand z to n_variables arrays. Any thoughts on how I can expand the 2D array into multiple 1D arrays?

this should do it.
n_variables = 25
z = np.array([np.linspace(0, 1, 10)] * n_variables)
z_grid = np.dstack(np.meshgrid(*z))
the * operator before list, unpacks list elements. consider following:
v1 = [1,2,3]
v2 = [4,5,6]
list_of_v = [v1,v2]
some_fucntion(v1,v2) == some_function(*list_ov_v)

Related

Applying linear transformation to whole surface of weird shape

So, I have this torus that is moved from its usual position given by the function
def toro(u,v,R,r,t):
n = unitary_normal_t(t)
n0, n1, n2 = [n[k][0] for k in [0,1,2]]
b = binormal_t(t)
b0, b1, b2 = [b[k][0] for k in [0,1,2]]
tang = tangente(t)
t0, t1, t2 = [tang[k][0] for k in [0,1,2]]
x = n0*np.cos(u)*(R + r*np.cos(v)) + b0*(R + r*np.cos(v))*np.sin(u) + r*t0*np.sin(v) + n0*R
y = n1*np.cos(u)*(R + r*np.cos(v)) + b1*(R + r*np.cos(v))*np.sin(u) + r*t1*np.sin(v) + n1*R
z = n2*np.cos(u)*(R + r*np.cos(v)) + b2*(R + r*np.cos(v))*np.sin(u) + r*t2*np.sin(v) + n2*R
return np.array([x,y,z])
The actual expression of this tori is not relevant. Since I wish to plot this tori, I do the following:
fig = go.Figure() # this creates the figure object
d = 1 # we add the torus
r = 0.1
R = 0.5
colorscales = ['plotly3','bluered','magma','blugrn','blues','gray']
u = np.linspace(0, 2*np.pi, 240) # discretization of the u parameter
v = np.linspace(0, 2*np.pi, 240) # discretization of the v parameter
uplot,vplot = np.meshgrid(u,v)
t = 0.1
torito = toro(uplot,vplot,R,r,t)
X = torito[0]
Y = torito[1]
Z = torito[2]
colorscale = 'plotly3'
fig.add_surface(x=X, y=Y, z=Z, colorscale = colorscale) # we add a surfaceplot to it
fig.update_traces(showscale=False)
Recall I am using numpy and Plotly. In the code above, torito is the torus and it is an array of shape (3,240,240). Now, I have this 3x3 matrix, let's call it M, that I would like to apply to torito, something like transformed_tori = np.matmul(M,torito) so that I could apply the same code as above to transformed_tori, yet I cannot do so because shapes do not match.
Does anyone know how I could do such a thing? Tryouts can be made with the following matrix: M = np.array([[ 0.00348674, -0.2282992 , 0.97358478], [ 0.07565293, 0.97086078, 0.2273895 ], [-0.99712811, 0.07286169, 0.02065664]])
Thank you in advance!!!
You can use np.einsum() to do the appropriate matrix multiplication along the wanted axis.
transformed_tori = np.einsum("ij,jkl->ikl", M, torito)
Where transformed_tori is a (3, 240, 240) array where transformed_tori[:, i,j] = np.matmul(M, tori[:, i,j]

Is it possible to reshape a Python array stacked in one way into another stacked type? [duplicate]

This question already has answers here:
How do I re-shape an array with shape(band,row,column) to (row,column,band)?
(2 answers)
Closed 4 years ago.
I have this array:
import numpy as np
shape = (3, 2, 2)
x = np.round(np.random.rand(*shape) * 100)
y = np.round(np.random.rand(*shape) * 100)
z = np.round(np.random.rand(*shape) * 100)
w = np.round(np.random.rand(*shape) * 100)
first_stacked = np.stack((x, y, z, w), axis=0)
print(first_stacked.shape) # (4, 3, 2, 2)
And I want to convert into this array:
import numpy as np
shape = (3, 2, 2)
x = np.round(np.random.rand(*shape) * 100)
y = np.round(np.random.rand(*shape) * 100)
z = np.round(np.random.rand(*shape) * 100)
w = np.round(np.random.rand(*shape) * 100)
last_stacked = np.stack((x, y, z, w), axis=-1)
print(last_stacked.shape) # (3, 2, 2, 4)
I tried this:
new_stacked = [i for i in first_stacked]
new_stacked = np.stack(new_stacked, axis=-1)
other_stacked = np.stack(first_stacked, axis=-1)
print(new_stacked.shape)
print(other_stacked.shape)
print(np.array_equal(new_stacked, last_stacked))
print(np.array_equal(new_stacked, other_stacked))
Output:
(3, 2, 2, 4)
(3, 2, 2, 4)
False
True
So neither of my two attempts work. What am I missing? Can it be done with just a reshape on the first_stacked? I worry if my arrays are too big, if it's more than a reshape, it could be a problem, though maybe my fears are unfounded.
Edit: I was randomizing the x,y,z,w arrays twice in the Jupyter Notebook and the second values were obviously not equal to the first. I apologize. Though if there's a better way to do it, I'm still interested.
So, the working code:
import numpy as np
shape = (3, 2, 2)
x = np.round(np.random.rand(*shape) * 100)
y = np.round(np.random.rand(*shape) * 100)
z = np.round(np.random.rand(*shape) * 100)
w = np.round(np.random.rand(*shape) * 100)
first_stacked = np.stack((x, y, z, w), axis=0)
print(first_stacked.shape)
last_stacked = np.stack((x, y, z, w), axis=-1)
print(last_stacked.shape)
new_stacked = [i for i in first_stacked]
new_stacked = np.stack(new_stacked, axis=-1)
other_stacked = np.stack(first_stacked, axis=-1)
print(new_stacked.shape)
print(other_stacked.shape)
print(np.array_equal(new_stacked, last_stacked))
print(np.array_equal(new_stacked, other_stacked))
Output:
(4, 3, 2, 2)
(3, 2, 2, 4)
(3, 2, 2, 4)
(3, 2, 2, 4)
True
True
You can use numpy.moveaxis to move the first axis to the last position.
np.moveaxis(first_stacked, 0, -1)
Or you can roll the axis into the desired position
np.rollaxis(first_stacked, 0, first_stacked.ndim)

numpy iterate over two 2d arrays

Say I have two matrices:
X, Y = np.meshgrid(np.arange(0, 2, 0.1), np.arange(3, 5, 0.1))
And a function, something like:
def func(x) :
return x[0]**2 + x[1]**2
How can I fill a matrix Z (of size np.shape(X)), where each entry is formed by calling func on the two corresponding values of X and Y, i.e.:
Z[i, j] = func([X[i, j], Y[i, j]])
Is there a way without using a double nested for-loop?
This is also works as a vectorized form of function evaluation:
import numpy as np
X, Y = np.meshgrid(np.arange(0, 2, 0.1), np.arange(3, 5, 0.1))
def func(x) :
return x[0]**2 + x[1]**2
Z = func([X,Y])
For given numpy arrays X and Y, you could just do -
Zout = X**2 + Y**2
If you are actually constructing X and Y like that, there is a direct way to get Z with broadcasting and thus avoid np.meshgrid, like so -
Zout = np.arange(0, 2, 0.1)**2 + np.arange(3, 5, 0.1)[:,None]**2

Python 3D polynomial surface fit, order dependent

I am currently working with astronomical data among which I have comet images. I would like to remove the background sky gradient in these images due to the time of capture (twilight). The first program I developed to do so took user selected points from Matplotlib's "ginput" (x,y) pulled the data for each coordinate (z) and then gridded the data in a new array with SciPy's "griddata."
Since the background is assumed to vary only slightly, I would like to fit a 3d low order polynomial to this set of (x,y,z) points. However, the "griddata" does not allow for an input order:
griddata(points,values, (dimension_x,dimension_y), method='nearest/linear/cubic')
Any ideas on another function that may be used or a method for developing a leas-squares fit that will allow me to control the order?
Griddata uses a spline fitting. A 3rd order spline is not the same thing as a 3rd order polynomial (instead, it's a different 3rd order polynomial at every point).
If you just want to fit a 2D, 3rd order polynomial to your data, then do something like the following to estimate the 16 coefficients using all of your data points.
import itertools
import numpy as np
import matplotlib.pyplot as plt
def main():
# Generate Data...
numdata = 100
x = np.random.random(numdata)
y = np.random.random(numdata)
z = x**2 + y**2 + 3*x**3 + y + np.random.random(numdata)
# Fit a 3rd order, 2d polynomial
m = polyfit2d(x,y,z)
# Evaluate it on a grid...
nx, ny = 20, 20
xx, yy = np.meshgrid(np.linspace(x.min(), x.max(), nx),
np.linspace(y.min(), y.max(), ny))
zz = polyval2d(xx, yy, m)
# Plot
plt.imshow(zz, extent=(x.min(), y.max(), x.max(), y.min()))
plt.scatter(x, y, c=z)
plt.show()
def polyfit2d(x, y, z, order=3):
ncols = (order + 1)**2
G = np.zeros((x.size, ncols))
ij = itertools.product(range(order+1), range(order+1))
for k, (i,j) in enumerate(ij):
G[:,k] = x**i * y**j
m, _, _, _ = np.linalg.lstsq(G, z)
return m
def polyval2d(x, y, m):
order = int(np.sqrt(len(m))) - 1
ij = itertools.product(range(order+1), range(order+1))
z = np.zeros_like(x)
for a, (i,j) in zip(m, ij):
z += a * x**i * y**j
return z
main()
The following implementation of polyfit2d uses the available numpy methods numpy.polynomial.polynomial.polyvander2d and numpy.polynomial.polynomial.polyval2d
#!/usr/bin/env python3
import unittest
def polyfit2d(x, y, f, deg):
from numpy.polynomial import polynomial
import numpy as np
x = np.asarray(x)
y = np.asarray(y)
f = np.asarray(f)
deg = np.asarray(deg)
vander = polynomial.polyvander2d(x, y, deg)
vander = vander.reshape((-1,vander.shape[-1]))
f = f.reshape((vander.shape[0],))
c = np.linalg.lstsq(vander, f)[0]
return c.reshape(deg+1)
class MyTest(unittest.TestCase):
def setUp(self):
return self
def test_1(self):
self._test_fit(
[-1,2,3],
[ 4,5,6],
[[1,2,3],[4,5,6],[7,8,9]],
[2,2])
def test_2(self):
self._test_fit(
[-1,2],
[ 4,5],
[[1,2],[4,5]],
[1,1])
def test_3(self):
self._test_fit(
[-1,2,3],
[ 4,5],
[[1,2],[4,5],[7,8]],
[2,1])
def test_4(self):
self._test_fit(
[-1,2,3],
[ 4,5],
[[1,2],[4,5],[0,0]],
[2,1])
def test_5(self):
self._test_fit(
[-1,2,3],
[ 4,5],
[[1,2],[4,5],[0,0]],
[1,1])
def _test_fit(self, x, y, c, deg):
from numpy.polynomial import polynomial
import numpy as np
X = np.array(np.meshgrid(x,y))
f = polynomial.polyval2d(X[0], X[1], c)
c1 = polyfit2d(X[0], X[1], f, deg)
np.testing.assert_allclose(c1,
np.asarray(c)[:deg[0]+1,:deg[1]+1],
atol=1e-12)
unittest.main()
According to the principle of Least squares, and imitate Kington's style,
while move argument m to argument m_1 and argument m_2.
import numpy as np
import matplotlib.pyplot as plt
import itertools
# w = (Phi^T Phi)^{-1} Phi^T t
# where Phi_{k, j + i (m_2 + 1)} = x_k^i y_k^j,
# t_k = z_k,
# i = 0, 1, ..., m_1,
# j = 0, 1, ..., m_2,
# k = 0, 1, ..., n - 1
def polyfit2d(x, y, z, m_1, m_2):
# Generate Phi by setting Phi as x^i y^j
nrows = x.size
ncols = (m_1 + 1) * (m_2 + 1)
Phi = np.zeros((nrows, ncols))
ij = itertools.product(range(m_1 + 1), range(m_2 + 1))
for h, (i, j) in enumerate(ij):
Phi[:, h] = x ** i * y ** j
# Generate t by setting t as Z
t = z
# Generate w by solving (Phi^T Phi) w = Phi^T t
w = np.linalg.solve(Phi.T.dot(Phi), (Phi.T.dot(t)))
return w
# t' = Phi' w
# where Phi'_{k, j + i (m_2 + 1)} = x'_k^i y'_k^j
# t'_k = z'_k,
# i = 0, 1, ..., m_1,
# j = 0, 1, ..., m_2,
# k = 0, 1, ..., n' - 1
def polyval2d(x_, y_, w, m_1, m_2):
# Generate Phi' by setting Phi' as x'^i y'^j
nrows = x_.size
ncols = (m_1 + 1) * (m_2 + 1)
Phi_ = np.zeros((nrows, ncols))
ij = itertools.product(range(m_1 + 1), range(m_2 + 1))
for h, (i, j) in enumerate(ij):
Phi_[:, h] = x_ ** i * y_ ** j
# Generate t' by setting t' as Phi' w
t_ = Phi_.dot(w)
# Generate z_ by setting z_ as t_
z_ = t_
return z_
if __name__ == "__main__":
# Generate x, y, z
n = 100
x = np.random.random(n)
y = np.random.random(n)
z = x ** 2 + y ** 2 + 3 * x ** 3 + y + np.random.random(n)
# Generate w
w = polyfit2d(x, y, z, m_1=3, m_2=2)
# Generate x', y', z'
n_ = 1000
x_, y_ = np.meshgrid(np.linspace(x.min(), x.max(), n_),
np.linspace(y.min(), y.max(), n_))
z_ = np.zeros((n_, n_))
for i in range(n_):
z_[i, :] = polyval2d(x_[i, :], y_[i, :], w, m_1=3, m_2=2)
# Plot
plt.imshow(z_, extent=(x_.min(), y_.max(), x_.max(), y_.min()))
plt.scatter(x, y, c=z)
plt.show()
If anyone is looking for fitting a polynomial of a specific order (rather than polynomials where the highest power is equal to order, you can make this adjustment to the accepted answer's polyfit and polyval:
instead of:
ij = itertools.product(range(order+1), range(order+1))
which, for order=2 gives [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)] (aka up to a 4th degree polynomial), you can use
def xy_powers(order):
powers = itertools.product(range(order + 1), range(order + 1))
return [tup for tup in powers if sum(tup) <= order]
This returns [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (2, 0)] for order=2

numpy: operating on multidimensional arrays

Sorry for title's vagueness. I have two related questions.
First, let's say I have a function "hessian" that given two parameters (x, y) returns a matrix. I now want to compute that matrix for (x,y) running over a two dimensional space. I'd like to do something like:
x = linspace(1, 4, 100).reshape(-1,1)
y = linspace(1, 4, 100).reshape(1,-1)
H = vectorize(hessian)(x, y)
with the resulting H of shape (100,100,2,2). The above doesn't work (ValueError: setting an array element with a sequence). The only thing I came up with is
H = array([ hessian(xx, yy) for xx in x.flat for yy in y.flat ]).reshape(100,100,2,2)
is there a better, more direct, way ?
Second, now H has shape (100,100,2,2) and dominant_eigenvector(X) does exactly what you think.
U, V = hsplit(array(map(dominant_eigenvector, H.reshape(10000,2,2))), 2)
I again need to use list comprehension to do the iteration and repack the result in an array specifying manually the shape. Is there a more direct way to achieve the same result ?
Thanks!
edit: as suggested by Paul and JoshAdel, I implemented a version of hessian that works with arrays, here it is
def hessian(w1, w2):
w1 = atleast_1d(w1)[...,newaxis,newaxis]
w2 = atleast_1d(w2)[...,newaxis,newaxis]
o1, o2 = ix_(*map(xrange, Z.shape))
W = Z * pow(w1, o1) * pow(w2, o2)
A = (W).sum()
B = (W * o1).sum()
C = (W * o2).sum()
D = (W * o1 * o1).sum()
E = (W * o1 * o2).sum()
F = (W * o2 * o2).sum()
return array([[ D/A - B/A*B/A, E/A - B/A*C/A ],
[ E/A - B/A*C/A, F/A - C/A*C/A ]])
Z can be considered a global array of roughly 250x150.
o1 and o2 index the two dimensions of Z to compute
things like $\sum_{i,j} Z_{ij} * i * j$.
The problem with this version is that intermediate arrays
are just too big. If w1 and w2 are arrays like w1_k w2_l
W becomes W_{k,l,i,j} on which numpy gives ValueError: too big.
You could try to use meshgrid, maybe you have to flatten xn, yn:
x = linspace(1, 4, 100)
y = linspace(1, 4, 100)
xn,yn=meshgrid(x,y)
H = vectorize(hessian)(xn, yn)

Categories