Create 4D upper diagonal array from 3D - python

Let's say that I have a (x, y, z) sized matrix. Now, I wish to create a new matrix of dimension (x, y, i, i), where the (i, i) matrix is upper diagonal and constructed from the values on the z-dimension. Is there some easy way of doing this in numpy without using more than 1 for-loop (looping over x)? Thanks.
EDIT
original = np.array([
[
[0, 1, 3],
[4, 5, 6]
],
[
[7, 8, 9],
[3, 2, 1]
],
])
new = np.array([
[
[
[0, 1],
[0, 3]
],
[
[4, 5],
[0, 6]
]
],
[
[
[7, 8],
[0, 9]
],
[
[3, 2],
[0, 1]
]
]
])
So, using the above we see that
original[0, 0, :] = [0 1 3]
new[0, 0, :, :] = [[0 1]
[0 3]]

Here's an approach using boolean-indexing -
n = 2 # This would depend on a.shape[-1]
out = np.zeros(a.shape[:2] + (n,n,),dtype=a.dtype)
out[:,:,np.arange(n)[:,None] <= np.arange(n)] = a
Sample run -
In [247]: a
Out[247]:
array([[[0, 1, 3],
[4, 5, 6]],
[[7, 8, 9],
[3, 2, 1]]])
In [248]: out
Out[248]:
array([[[[0, 1],
[0, 3]],
[[4, 5],
[0, 6]]],
[[[7, 8],
[0, 9]],
[[3, 2],
[0, 1]]]])
Another approach could be suggested using subscripted-indexing to replace the last step -
r,c = np.triu_indices(n)
out[:,:,r,c] = a
Note : As stated earlier, n would depend on a.shape[-1]. Here, we had a.shape[-1] as 3, so n was 2. If a.shape[-1] were 6, n would be 3 and so on. The relationship is : (n*(n+1))//2 == a.shape[-1].

Related

Numpy create four other points arrays based on one point array

Suppose I have an array a representing 3 center points of 3 rectangles. I want to create four other copied points based on each of points in array a by add 1 or minus 1 in x, y coordinates like shown in the picture.
a = np.arange(9).reshape(3,3)
>>>a
>>>out:[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]]
I'm very new to numpy. What I can think of is that I can make 4 coupies of a, for the first do a[:,0]+=1, then a[:,1]+=1. for the second do a[:,0]+=1,then a[:,1]-=1.for the third do a[:,0]-=1 then a[:,1]+=1, for the fourth do a[:,0]-=1then a[:,1]-=1. But I know it's stupid. So I 'm wondering if there is a clearer way to do it in numpy?
My expected outputs:
array_1 = [[ 1 2 2]
[ 4 5 5]
[ 7 8 8]]
array_2 = [[ 1 0 2]
[ 4 3 5]
[ 7 6 8]]
array_3 = [[ -1 2 2]
[ 2 5 5]
[ 5 8 8]]
array_4 = [[ -1 0 2]
[ 2 3 5]
[ 5 6 8]]
You can generate a 3D array:
a = np.arange(9).reshape(3,3)
b = np.array([[ 1, 1,0],
[ 1,-1,0],
[-1, 1,0],
[-1,-1,0]])
# or programmatically
from itertools import product
b = np.array(list(product([1,-1], [1,-1], [0])))
out = np.tile(a, (4,1,1))+b[:,None,:]
array([[[ 1, 2, 2],
[ 4, 5, 5],
[ 7, 8, 8]],
[[ 1, 0, 2],
[ 4, 3, 5],
[ 7, 6, 8]],
[[-1, 2, 2],
[ 2, 5, 5],
[ 5, 8, 8]],
[[-1, 0, 2],
[ 2, 3, 5],
[ 5, 6, 8]]])
Subsetting:
out[0]
array([[1, 2, 2],
[4, 5, 5],
[7, 8, 8]])
it seems that what you need is to loop over a Cartesian product, there are many ways of doing so, one is to use itertools, here goes:
import numpy as np
import itertools
a = np.arange(9).reshape(3,3)
list_of_arrays = []
for seq in itertools.product([1, -1], repeat=2):
b = a.copy()
b[:,0]+=seq[0]
b[:,1]+=seq[1]
list_of_arrays.append(b)
list_of_arrays:
[array([[1, 2, 2],
[4, 5, 5],
[7, 8, 8]]),
array([[1, 0, 2],
[4, 3, 5],
[7, 6, 8]]),
array([[-1, 2, 2],
[ 2, 5, 5],
[ 5, 8, 8]]),
array([[-1, 0, 2],
[ 2, 3, 5],
[ 5, 6, 8]])]
Using numpy broadcasting and itertools to generate the shifts:
import itertools
import numpy as np
a = np.arange(9).reshape(3, 3)
shifts = np.array([(dx, dy, 0) for dx, dy in itertools.product([1, -1], repeat=2)])
shifted_a = a + shifts[:, None]

Flattening a numpy array with indexes

Given a NumPy array of shape (X, Y, 2) representing an array of "frames" including "points" and every point having an (x,y) coordinate, I'd like to consolidate the first and second dimensions to an (X*Y, 4) array that now represents all of the points, and indexes of the X and Y dimensions.
For example, If my array is:
[
[ # Frame 0
[1, 2], # Point 0
[2, 3] # Point 1
],
[ # Frame 1
[4, 5], # Point 0
[6, 7] # Point 1
]
]
I'd like to get the array:
[
[0, 0, 1, 2], # Frame 0, Point 0
[0, 1, 2, 3] # Frame 0, Point 1
[1, 0, 4, 5], # Frame 1, Point 0
[1, 1, 6, 7] # Frame 1, Point 1
]
Slow solution:
arr = np.array([[[1, 2],[2, 3]],[[4, 5],[6, 7]]])
new_arr = []
for i, points in enumerate(arr):
for j, point in enumerate(points):
new_arr.append([i, j] + point.tolist())
Is there a faster way?
You can solve each part separately using numpy.ndindex to get indices and .reshape(). Then you can use numpy.c_ to stack them.
a = np.array([[[1, 2],[2, 3]],[[4, 5],[6, 7]]])
c = a.reshape(-1, a.shape[-1])
print(c)
# [[1 2]
# [2 3]
# [4 5]
# [6 7]]
indices = list(np.ndindex(a.shape[:-1]))
print(indices)
# [(0, 0), (0, 1), (1, 0), (1, 1)]
print(np.c_[indices, c])
# [[0 0 1 2]
# [0 1 2 3]
# [1 0 4 5]
# [1 1 6 7]]
A larger example array is used in this code so that it could be tested with different sizes in each dimension:
import numpy as np
arr = np.array(
[
[
[1, 2],
[2, 3],
[3, 4]
],
[
[4, 5],
[6, 7],
[8, 7]
],
[
[14, 5],
[16, 7],
[18, 7]
],
[
[24, 5],
[26, 7],
[28, 7]
]
]
)
x, y = arr.shape[:2]
assert(arr.shape[2] == 2)
ay, ax = (a.reshape(x, y, 1) for a in np.meshgrid(np.arange(y), np.arange(x)))
new_array = np.concatenate([ax, ay, arr], axis=2).reshape(x * y, 4)
print(repr(new_array))
gives the following:
array([[ 0, 0, 1, 2],
[ 0, 1, 2, 3],
[ 0, 2, 3, 4],
[ 1, 0, 4, 5],
[ 1, 1, 6, 7],
[ 1, 2, 8, 7],
[ 2, 0, 14, 5],
[ 2, 1, 16, 7],
[ 2, 2, 18, 7],
[ 3, 0, 24, 5],
[ 3, 1, 26, 7],
[ 3, 2, 28, 7]])
And using your original example array gives:
array([[0, 0, 1, 2],
[0, 1, 2, 3],
[1, 0, 4, 5],
[1, 1, 6, 7]])
There are no explicit loops, so it ought to be faster. (Any looping is inside numpy and will be implemented in optimised C code.)
I'm also new to NumPy but I think this should work (someone correct me if I'm wrong): arr.reshape(-1,4)

numpy select values based on list of indices. Process batch at once [duplicate]

Suppose I have a matrix A with some arbitrary values:
array([[ 2, 4, 5, 3],
[ 1, 6, 8, 9],
[ 8, 7, 0, 2]])
And a matrix B which contains indices of elements in A:
array([[0, 0, 1, 2],
[0, 3, 2, 1],
[3, 2, 1, 0]])
How do I select values from A pointed by B, i.e.:
A[B] = [[2, 2, 4, 5],
[1, 9, 8, 6],
[2, 0, 7, 8]]
EDIT: np.take_along_axis is a builtin function for this use case implemented since numpy 1.15. See #hpaulj 's answer below for how to use it.
You can use NumPy's advanced indexing -
A[np.arange(A.shape[0])[:,None],B]
One can also use linear indexing -
m,n = A.shape
out = np.take(A,B + n*np.arange(m)[:,None])
Sample run -
In [40]: A
Out[40]:
array([[2, 4, 5, 3],
[1, 6, 8, 9],
[8, 7, 0, 2]])
In [41]: B
Out[41]:
array([[0, 0, 1, 2],
[0, 3, 2, 1],
[3, 2, 1, 0]])
In [42]: A[np.arange(A.shape[0])[:,None],B]
Out[42]:
array([[2, 2, 4, 5],
[1, 9, 8, 6],
[2, 0, 7, 8]])
In [43]: m,n = A.shape
In [44]: np.take(A,B + n*np.arange(m)[:,None])
Out[44]:
array([[2, 2, 4, 5],
[1, 9, 8, 6],
[2, 0, 7, 8]])
More recent versions have added a take_along_axis function that does the job:
A = np.array([[ 2, 4, 5, 3],
[ 1, 6, 8, 9],
[ 8, 7, 0, 2]])
B = np.array([[0, 0, 1, 2],
[0, 3, 2, 1],
[3, 2, 1, 0]])
np.take_along_axis(A, B, 1)
Out[]:
array([[2, 2, 4, 5],
[1, 9, 8, 6],
[2, 0, 7, 8]])
There's also a put_along_axis.
I know this is an old question, but another way of doing it using indices is:
A[np.indices(B.shape)[0], B]
output:
[[2 2 4 5]
[1 9 8 6]
[2 0 7 8]]
Following is the solution using for loop:
outlist = []
for i in range(len(B)):
lst = []
for j in range(len(B[i])):
lst.append(A[i][B[i][j]])
outlist.append(lst)
outarray = np.asarray(outlist)
print(outarray)
Above can also be written in more succinct list comprehension form:
outlist = [ [A[i][B[i][j]] for j in range(len(B[i]))]
for i in range(len(B)) ]
outarray = np.asarray(outlist)
print(outarray)
Output:
[[2 2 4 5]
[1 9 8 6]
[2 0 7 8]]

Numpy append and normal append

x = [[1,2],[2,3],[10,1],[10,10]]
def duplicatingRows(x, l):
severity = x[l][1]
if severity == 1 or severity == 2:
for k in range(1,6):
x.append(x[l])
for l in range(len(x)):
duplicatingRows(x,l)
print(x)
x = np.array([[1,2],[2,3],[10,1],[10,10]])
def duplicatingRows(x, l):
severity = x[l][1]
if severity == 1 or severity == 2:
for k in range(1,6):
x = np.append(x, x[l])
for l in range(len(x)):
duplicatingRows(x,l)
print(x)
I would like it to print an array with extra appended rows.
Giving out a list of [[1, 2], [2, 3], [10, 1], [10, 10], [1, 2], [1, 2], [1, 2], [1, 2], [1, 2], [10, 1], [10, 1], [10, 1], [10, 1], [10, 1]]. Why does it not work? I tried different combinations with concatenate as well, but it didnt work.
You have some bugs in your code. Here's a little bit improved, correct, and (partially) vectorized implementation of your code which prints your desired output.
Here we leverage numpy.tile for repeating the rows, followed by a reshape so that we can append it along axis 0, which is what is needed.
In [24]: x = np.array([[1,2],[2,3],[10,1],[10,10]])
def duplicatingRows(x, l):
severity = x[l][1]
if severity == 1 or severity == 2:
# replaced your `for` loop
# 5 corresponds to `range(1, 6)`
reps = np.tile(x[l], 5).reshape(5, -1)
x = np.append(x, reps, axis=0)
return x
for l in range(len(x)):
x = duplicatingRows(x,l)
print(x)
Output:
[[ 1 2]
[ 2 3]
[10 1]
[10 10]
[ 1 2]
[ 1 2]
[ 1 2]
[ 1 2]
[ 1 2]
[10 1]
[10 1]
[10 1]
[10 1]
[10 1]]
Let's take a whole-array approach
In [140]: arr = np.array([[1,2],[2,3],[10,1],[10,10]])
In [141]: arr
Out[141]:
array([[ 1, 2],
[ 2, 3],
[10, 1],
[10, 10]])
We want to replicate the rows where the 2nd column has a 1 or 2, right? isin makes a nice 'mask' (we could also use == and any):
In [142]: np.isin(arr[:,1],[1,2])
Out[142]: array([ True, False, True, False])
In [143]: torepeat = arr[np.isin(arr[:,1],[1,2])]
In [144]: torepeat
Out[144]:
array([[ 1, 2],
[10, 1]])
np.repeat does a nice job of replicating the values, which we can simply concatenate with the original:
In [145]: repeated = np.repeat(torepeat,5, axis=0)
In [146]: np.concatenate((arr, repeated),axis=0)
Out[146]:
array([[ 1, 2],
[ 2, 3],
[10, 1],
[10, 10],
[ 1, 2],
[ 1, 2],
[ 1, 2],
[ 1, 2],
[ 1, 2],
[10, 1],
[10, 1],
[10, 1],
[10, 1],
[10, 1]])
np.append uses concatenate. It works ok with adding a single element to a 1d array, but becomes trickier to use with higher dimensions. It's a poor imitation of the list append. Also repeated concatenates in a loop is relatively slow. We usually recommend list appends, with a single array build at the end.
Another way to use repeat iteratively:
In [164]: np.concatenate([np.repeat(a[None,:], 5, axis=0) for a in arr if (a[1]==1 or a[1]==2)], axis=0)
Out[164]:
array([[ 1, 2],
[ 1, 2],
[ 1, 2],
[ 1, 2],
[ 1, 2],
[10, 1],
[10, 1],
[10, 1],
[10, 1],
[10, 1]])

Python - Matrix outer product

Given two matrices
A: m * r
B: n * r
I want to generate another matrix C: m * n, with each entry C_ij being a matrix calculated by the outer product of A_i and B_j.
For example,
A: [[1, 2],
[3, 4]]
B: [[3, 1],
[1, 2]]
gives
C: [[[3, 1], [[1 ,2],
[6, 2]], [2 ,4]],
[9, 3], [[3, 6],
[12,4]], [4, 8]]]
I can do it using for loops, like
for i in range (A.shape(0)):
for j in range (B.shape(0)):
C_ij = np.outer(A_i, B_j)
I wonder If there is a vectorised way of doing this calculation to speed it up?
The Einstein notation expresses this problem nicely
In [85]: np.einsum('ac,bd->abcd',A,B)
Out[85]:
array([[[[ 3, 1],
[ 6, 2]],
[[ 1, 2],
[ 2, 4]]],
[[[ 9, 3],
[12, 4]],
[[ 3, 6],
[ 4, 8]]]])
temp = numpy.multiply.outer(A, B)
C = numpy.swapaxes(temp, 1, 2)
NumPy ufuncs, such as multiply, have an outer method that almost does what you want. The following:
temp = numpy.multiply.outer(A, B)
produces a result such that temp[a, b, c, d] == A[a, b] * B[c, d]. You want C[a, b, c, d] == A[a, c] * B[b, d]. The swapaxes call rearranges temp to put it in the order you want.
Simple Solution with Numpy Array Broadcasting
Since, you want C_ij = A_i * B_j, this can be achieved simply by numpy broadcasting on element-wise-product of column-vector-A and row-vector-B, as shown below:
# import numpy as np
# A = [[1, 2], [3, 4]]
# B = [[3, 1], [1, 2]]
A, B = np.array(A), np.array(B)
C = A.reshape(-1,1) * B.reshape(1,-1)
# same as:
# C = np.einsum('i,j->ij', A.flatten(), B.flatten())
print(C)
Output:
array([[ 3, 1, 1, 2],
[ 6, 2, 2, 4],
[ 9, 3, 3, 6],
[12, 4, 4, 8]])
You could then get your desired four sub-matrices by using numpy.dsplit() or numpy.array_split() as follows:
np.dsplit(C.reshape(2, 2, 4), 2)
# same as:
# np.array_split(C.reshape(2,2,4), 2, axis=2)
Output:
[array([[[ 3, 1],
[ 6, 2]],
[[ 9, 3],
[12, 4]]]),
array([[[1, 2],
[2, 4]],
[[3, 6],
[4, 8]]])]
Use numpy;
In [1]: import numpy as np
In [2]: A = np.array([[1, 2], [3, 4]])
In [3]: B = np.array([[3, 1], [1, 2]])
In [4]: C = np.outer(A, B)
In [5]: C
Out[5]:
array([[ 3, 1, 1, 2],
[ 6, 2, 2, 4],
[ 9, 3, 3, 6],
[12, 4, 4, 8]])
Once you have the desired result, you can use numpy.reshape() to mold it in almost any shape you want;
In [6]: C.reshape([4,2,2])
Out[6]:
array([[[ 3, 1],
[ 1, 2]],
[[ 6, 2],
[ 2, 4]],
[[ 9, 3],
[ 3, 6]],
[[12, 4],
[ 4, 8]]])

Categories

Resources