Insert item and change the array's dimension - python

I want to add dimensions to an array, but expand_dims always adds dimension of size 1.
Input:
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
What expand_dims does:
[[[1], [2], [3]], [[4], [5], [6]], [[7], [8], [9]]]
What I want:
[[[1, 1], [1, 2], [1, 3]], [[1, 4], [1, 5], [1, 6]], [[1, 7], [1, 8], [1, 9]]]
Basically I want to replace each scalar in the matrix by a vector [1, x] where x is the original scalar.

Here's one way using broadcasting and np.insert() function:
In [32]: a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
In [33]: np.insert(a[:,:,None], 0, 1, 2)
Out[33]:
array([[[1, 1],
[1, 2],
[1, 3]],
[[1, 4],
[1, 5],
[1, 6]],
[[1, 7],
[1, 8],
[1, 9]]])

There are lots of ways of constructing the new array.
You could initial the array with right shape and fill, and copy values:
In [402]: arr = np.arange(1,10).reshape(3,3)
In [403]: arr
Out[403]:
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
In [404]: res = np.ones((3,3,2),int)
In [405]: res[:,:,1] = arr
In [406]: res
Out[406]:
array([[[1, 1],
[1, 2],
[1, 3]],
[[1, 4],
[1, 5],
[1, 6]],
[[1, 7],
[1, 8],
[1, 9]]])
You could join the array with a like size array of 1s. concatenate is the basic joining function:
In [407]: np.concatenate((np.ones((3,3,1),int), arr[:,:,None]), axis=2)
Out[407]:
array([[[1, 1],
[1, 2],
[1, 3]],
[[1, 4],
[1, 5],
[1, 6]],
[[1, 7],
[1, 8],
[1, 9]]])
np.stack((np.ones((3,3),int), arr), axis=2) does the same thing under the covers. np.dstack ('d' for depth) does it as well. The insert in the other answer also does this.

Related

Apply numpy broadcast_to on each vector in an array

I want to apply something like this:
a = np.array([1,2,3])
np.broadcast_to(a, (3,3))
array([[1, 2, 3],
[1, 2, 3],
[1, 2, 3]])
On each vector in a multi-vector array:
a = np.array([[1,2,3], [4,5,6]])
np.broadcast_to(a, (2,3,3))
ValueError: operands could not be broadcast together with remapped shapes [original->remapped]: (2,3) and requested shape (2,3,3)
To get something like this:
array([[[1, 2, 3],
[1, 2, 3],
[1, 2, 3]],
[[4, 5, 6],
[4, 5, 6],
[4, 5, 6]]])
One way is to use list-comprehension and broadcast each of the inner array:
>>> np.array([np.broadcast_to(i, (3,3)) for i in a])
array([[[1, 2, 3],
[1, 2, 3],
[1, 2, 3]],
[[4, 5, 6],
[4, 5, 6],
[4, 5, 6]]])
Or, you can just add an extra dimension to a then call broadcast_to over it:
>>> np.broadcast_to(a[:,None], (2,3,3))
array([[[1, 2, 3],
[1, 2, 3],
[1, 2, 3]],
[[4, 5, 6],
[4, 5, 6],
[4, 5, 6]]])

building a 3d Numpy array from a 2d numpy array

So I've got a numpy array like this:
a = np.array([[1, 2, 3],
[2, 3, 4],
[4, 5, 6]])
and I want to convert it into an array like this:
[[[1, 1, 1], [2, 2, 2], [3, 3, 3]],
[[2, 2, 2], [3, 3, 3], [4, 4, 4]],
[[4, 4, 4], [5, 5, 5], [6, 6, 6]]]
How would you do it?
Use numpy.repeat on the array with one extra dimension:
np.repeat(a[...,None], 3, axis=2)
Or numpy.tile:
np.tile(a[...,None], (1,1,3))
Output:
array([[[1, 1, 1],
[2, 2, 2],
[3, 3, 3]],
[[2, 2, 2],
[3, 3, 3],
[4, 4, 4]],
[[4, 4, 4],
[5, 5, 5],
[6, 6, 6]]])

numpy fancy indexing axis order

I have a problem using numpy fancy indexing which I somehow can't get my head around.
I know, that I can get an array of submatrices of rows like this:
A = np.array([[1,2,3],[4,5,6],[7,8,9]])
B = A[np.array([[0,1],[1,2]])]
This gives:
array([[[1, 2, 3],
[4, 5, 6]],
[[4, 5, 6],
[7, 8, 9]]])
a threedimensional numpy array containing matrices comprising the first,second row and second,third row of A, respectively.
What I want is now basically the same operation for the cols of A which should give
array([[[1, 2],
[4, 5],
[7, 8]],
[[2, 3],
[5, 6],
[8, 9]]])
But
B = A[:,np.array([[0,1],[1,2]])]
does not work (probably because of the order of the index evaluations). It gives
array([[[1, 2],
[2, 3]],
[[4, 5],
[5, 6]],
[[7, 8],
[8, 9]]])
How can I accomplish this in the best way? Should I work with transposed matrices?
You get a (3,2,2) array:
In [417]: B
Out[417]:
array([[[1, 2],
[2, 3]],
[[4, 5],
[5, 6]],
[[7, 8],
[8, 9]]])
The 3 is from the first axis of A. The (2,2) from B.
Swap the first 2 axes:
In [418]: B.transpose(1,0,2)
Out[418]:
array([[[1, 2],
[4, 5],
[7, 8]],
[[2, 3],
[5, 6],
[8, 9]]])
A (2,3,2) array
try this:
A = np.array([[1,2,3],[4,5,6],[7,8,9]])
B = A[np.array([[0,1,2],[0,1,2]])]
C = [list(), list()]
for i in range(2):
for j in range(3):
C[i].append(list(B[i][j][:2]) if i==0 else list(B[i][j][1:3]))
C = np.array(C)
C
output:
array([[[1, 2],
[4, 5],
[7, 8]],
[[2, 3],
[5, 6],
[8, 9]]])
One way could be to create B from A.T and then swapaxes:
import numpy as np
A = np.array([[1,2,3],[4,5,6],[7,8,9]])
B = A.T[np.array([[0,1],[1,2]])]
C = B.swapaxes(-2,-1)
To check intermediate step and result:
B
array([[[1, 4, 7],
[2, 5, 8]],
[[2, 5, 8],
[3, 6, 9]]])
C
array([[[1, 2],
[4, 5],
[7, 8]],
[[2, 3],
[5, 6],
[8, 9]]])

Need to partition a list into parts following a specific rule

I have the following list of lists (the inner lists will be referred to as tuples henceforth, just to avoid confusion) :
[[1, 1], [2, 1], [2, 2], [3, 1], [3, 2], [4, 1], [3, 3], [4, 2], [5, 1], [4, 3], [5, 2], [6, 1], [4, 4], [5, 3], [6, 2], [7, 1]]
and I would like to create another list that contains:
[[[1, 1]], [[2, 1], [2, 2]], [[3, 1], [3, 2]], [[4, 1], [3, 3], [4, 2]], [[5, 1], [4, 3], [5, 2]], [[6, 1], [4, 4], [5, 3], [6, 2]], [[7, 1]]]
What basically I am doing is scanning the list of tuples, and putting the tuples into sublists until I hit a tuple that has a higher first coordinate than the previous tuples (italics is a correction based on a comment, I had meant this but while writing, missed it). For example the first element is [1,1] and the next is [2,1], but since 2>1, the first sublist is [[1,1]]. Then again, when we hit [3,1], the second sublist created is [[2,1],[2,2]] and so on.
How do I implement this in python? Specifically python 3?
The following solution assumes that the input data is never an empty list:
d = [[1, 1], [2, 1], [2, 2], [3, 1], [3, 2], [4, 1], [3, 3], [4, 2], [5, 1], [4, 3], [5, 2], [6, 1], [4, 4], [5, 3], [6, 2], [7, 1]]
d2 = [[d[0]]]
for t in d[1:]:
if t[0] > d2[-1][0][0]:
d2.append([t])
else:
d2[-1].append(t)
print(d2)
The following accommodates the case where the input data is an empty list:
d2 = []
for t in d:
if (not d2) or (t[0] > d2[-1][0][0]):
d2.append([t])
else:
d2[-1].append(t)
a = [[1, 1], [2, 1], [2, 2], [3, 1], [3, 2], [4, 1], [3, 3], [4, 2], [5, 1], [4, 3], [5, 2], [6, 1], [4, 4], [5, 3], [6, 2], [7, 1]]
def cummax(v):
x = v[:1]
for i in v:
x.append(x[-1] if x[-1] > i else i)
return x
d = {}
for i,j in zip(cummax([i for i,j in a]), a):
if not d.get(i):
d[i]=[]
d[i].append(j)
list(d.values())
[[[1, 1]], [[2, 1], [2, 2]], [[3, 1], [3, 2]], [[4, 1], [3, 3], [4, 2]], [[5, 1], [4, 3], [5, 2]], [[6, 1], [4, 4], [5, 3], [6, 2]], [[7, 1]]]

Expanding a vector to a new size

Given a vector, for example
my_list=[1, 2, 3]
how to expand each entry to a new matrix (seen as a multidimensional list or, more likely, a numpy array)?
For example, in the case of matrix being a numpy array of size 2x2 matrix, the output, expanded_my_list, would be:
[[[1, 1], [1, 1]], [[2, 2], [2, 2]], [[3, 3], [3, 3]]]
or as a numpy array:
array([[[1, 1],
[1, 1]],
[[2, 2],
[2, 2]],
[[3, 3],
[3, 3]]])
where expanded_my_list.shape is (3,2,2).
One solution may be:
for i in range(len(my_list)):
expanded[:, i] = my_list[i].expand_as(matrix[:, i])
my_list = [1, 2, 3]
[[[e] * 2 for _ in range(2)] for e in my_list]
output:
[[[1, 1], [1, 1]], [[2, 2], [2, 2]], [[3, 3], [3, 3]]]
You could use numpy.tile().
By creating additional axis via np.newaxis and repeating the elements of the list along these axis you can create your wanted result:
import numpy as np
lst = [1, 2, 3]
arr = np.array(lst)
arr2 = np.tile(arr[:, np.newaxis, np.newaxis], reps=(1, 2, 2))
# Output:
# array([[[1, 1],
# [1, 1]],
# [[2, 2],
# [2, 2]],
# [[3, 3],
# [3, 3]]])
lst2 = arr2.tolist() # If a nested list is required
Or more general:
arr = np.array([[1, 2],
[3, 4]])
expanded_shape = (3, 4)
arr2 = np.tile(arr.reshape(arr.shape + (1,)*len(expanded_shape)),
reps=(1,)*arr.ndim + expanded_shape)
# Output, shape (2, 2, 3, 4)
# array([[[[1, 1, 1, 1],
# [1, 1, 1, 1],
# [1, 1, 1, 1]],
# [[2, 2, 2, 2],
# [2, 2, 2, 2],
# [2, 2, 2, 2]]],
# [[[3, 3, 3, 3],
# [3, 3, 3, 3],
# [3, 3, 3, 3]],
# [[4, 4, 4, 4],
# [4, 4, 4, 4],
# [4, 4, 4, 4]]]])

Categories