How to Copy a 2D Array into a 3Rd Dimension, N Times

How to copy a 2D array into a 3rd dimension, N times?

Probably the cleanest way is to use np.repeat:

a = np.array([[1, 2], [1, 2]])
print(a.shape)
# (2, 2)

# indexing with np.newaxis inserts a new 3rd dimension, which we then repeat the
# array along, (you can achieve the same effect by indexing with None, see below)
b = np.repeat(a[:, :, np.newaxis], 3, axis=2)

print(b.shape)
# (2, 2, 3)

print(b[:, :, 0])
# [[1 2]
# [1 2]]

print(b[:, :, 1])
# [[1 2]
# [1 2]]

print(b[:, :, 2])
# [[1 2]
# [1 2]]

Having said that, you can often avoid repeating your arrays altogether by using broadcasting. For example, let's say I wanted to add a (3,) vector:

c = np.array([1, 2, 3])

to a. I could copy the contents of a 3 times in the third dimension, then copy the contents of c twice in both the first and second dimensions, so that both of my arrays were (2, 2, 3), then compute their sum. However, it's much simpler and quicker to do this:

d = a[..., None] + c[None, None, :]

Here, a[..., None] has shape (2, 2, 1) and c[None, None, :] has shape (1, 1, 3)*. When I compute the sum, the result gets 'broadcast' out along the dimensions of size 1, giving me a result of shape (2, 2, 3):

print(d.shape)
# (2, 2, 3)

print(d[..., 0]) # a + c[0]
# [[2 3]
# [2 3]]

print(d[..., 1]) # a + c[1]
# [[3 4]
# [3 4]]

print(d[..., 2]) # a + c[2]
# [[4 5]
# [4 5]]

Broadcasting is a very powerful technique because it avoids the additional overhead involved in creating repeated copies of your input arrays in memory.


* Although I included them for clarity, the None indices into c aren't actually necessary - you could also do a[..., None] + c, i.e. broadcast a (2, 2, 1) array against a (3,) array. This is because if one of the arrays has fewer dimensions than the other then only the trailing dimensions of the two arrays need to be compatible. To give a more complicated example:

a = np.ones((6, 1, 4, 3, 1))  # 6 x 1 x 4 x 3 x 1
b = np.ones((5, 1, 3, 2)) # 5 x 1 x 3 x 2
result = a + b # 6 x 5 x 4 x 3 x 2

Create 3D array from a 2D array by replicating/repeating along the first axis

Introduce a new axis at the start with None/np.newaxis and replicate along it with np.repeat. This should work for extending any n dim array to n+1 dim array. The implementation would be -

np.repeat(arr[None,...],k,axis=0)

Sample run -

In [143]: arr
Out[143]:
array([[ 1., 2., 3.],
[ 4., 5., 6.],
[ 7., 8., 9.]])

In [144]: np.repeat(arr[None,...],3,axis=0)
Out[144]:
array([[[ 1., 2., 3.],
[ 4., 5., 6.],
[ 7., 8., 9.]],

[[ 1., 2., 3.],
[ 4., 5., 6.],
[ 7., 8., 9.]],

[[ 1., 2., 3.],
[ 4., 5., 6.],
[ 7., 8., 9.]]])

View-output for memory-efficiency

We can also generate a 3D view and achieve virtually free runtime with np.broadcast_to. More info - here. Hence, simply do -

np.broadcast_to(arr,(3,)+arr.shape) # repeat 3 times

Python: Repeat a 2d array K number of times and transform

One way is using np.broadcast_to, specifying the final shape (a being the input array):

k = 2
out = np.broadcast_to(a, (k, *a.shape))

out.shape
# (2, 3, 5)

print(out)
[[[-0.49349673 -0.16749763 1.09365913 0.91916602 0.5942118 ]
[-1.1357679 -1.06851897 -0.72537699 0.06350472 0.1747241 ]
[-0.29972989 -0.3321334 -1.52296231 -1.41765091 -0.53735561]]

[[-0.49349673 -0.16749763 1.09365913 0.91916602 0.5942118 ]
[-1.1357679 -1.06851897 -0.72537699 0.06350472 0.1747241 ]
[-0.29972989 -0.3321334 -1.52296231 -1.41765091 -0.53735561]]]

I want to convert a 2D numpy array to a 3D array, but there's a catch

You can use numpy.tile for this

>>> import numpy as np
>>> data = np.array([[0, 2, 6, 3],
[3, 7, 3, 9],
[0, 8, 3, 4],
[4, 6, 2, 1]])
>>> np.tile(data, (3,1,1))
array([[[0, 2, 6, 3],
[3, 7, 3, 9],
[0, 8, 3, 4],
[4, 6, 2, 1]],

[[0, 2, 6, 3],
[3, 7, 3, 9],
[0, 8, 3, 4],
[4, 6, 2, 1]],

[[0, 2, 6, 3],
[3, 7, 3, 9],
[0, 8, 3, 4],
[4, 6, 2, 1]]])

Python: Stretch 2D array into 3D based on corresponding array with 3rd-dimension index

You can use fancy indexing as this, assuming the largest value in b is always less than c.shape[2]:

n1, n2 = a.shape
c[np.arange(n1)[:,None], np.arange(n2), b] = a

c
#array([[[ nan, nan, 1.],
# [ nan, nan, 1.],
# [ nan, nan, 1.]],

# [[ nan, 1., nan],
# [ 1., nan, nan],
# [ nan, nan, 1.]],

# [[ nan, 1., nan],
# [ 1., nan, nan],
# [ 1., nan, nan]]])

Here we use integer arrays for all dimensions to trigger advanced indexing, and the three arrays are broadcasted against each other as follows (here we use numpy.broacast_arrays to visualize this):

i, j, k = np.broadcast_arrays(np.arange(3)[:,None], np.arange(3), b)

print("first dimension index: ")
print(i)
print("second dimension index: ")
print(j)
print("third dimension index: ")
print(k)

first dimension index:
[[0 0 0]
[1 1 1]
[2 2 2]]
second dimension index:
[[0 1 2]
[0 1 2]
[0 1 2]]
third dimension index:
[[2 2 2]
[1 0 2]
[1 0 0]]

Now the advanced indexing goes as (0, 0, 2), (0, 1, 2), (0, 2, 2) ... ,i.e. pick one value from each array at the same positions to form an index for an element:


Some testing cases:

c[0,0,2]
#1.0

c[0,1,2]
#1.0

c[2,1,0]
#1.0

Repeat a 2D NumPy array N times

By my tests, np.repeat is a little faster than np.tile:

X = np.repeat(arr[None,:], 3, axis=0)

Alternatively, use np.concatenate:

X = np.concatenate([[arr]] * 3, axis=0)

arr = np.arange(10000 * 1000).reshape(10000, 1000)

%timeit np.repeat(arr[None,:], 3, axis=0)
%timeit np.tile(arr, (3, 1, 1))
%timeit np.concatenate([[arr]] * 3, axis=0)
# Read-only, array cannot be modified.
%timeit np.broadcast_to(arr, (3, *arr.shape))
# Creating copy of the above.
%timeit np.broadcast_to(arr, (3, *arr.shape)).copy()

170 ms ± 3.82 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
187 ms ± 3.12 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
243 ms ± 3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
10.9 µs ± 218 ns per loop (mean ± std. dev. of 7 runs, 100000 loops
189 ms ± 2.45 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)each)

np.array_equals(np.repeat(arr[None,:], 3, axis=0), 
np.tile(arr, (3, 1, 1))
True

Append 2D array to 3D array, extending third dimension

Use dstack:

>>> np.dstack((A, B)).shape
(480, 640, 4)

This handles the cases where the arrays have different numbers of dimensions and stacks the arrays along the third axis.

Otherwise, to use append or concatenate, you'll have to make B three dimensional yourself and specify the axis you want to join them on:

>>> np.append(A, np.atleast_3d(B), axis=2).shape
(480, 640, 4)

Creating a list repeated n times using an existing variable which is mutable

You can just use .copy():

import numpy as np

R = np.array([[2,3,0], [6,78,8],[1,2,3]])
U = [R.copy() for _ in range(3)]

U[1][2,2] = 0

print(U)

Gives:

[array([[ 2,  3,  0],
[ 6, 78, 8],
[ 1, 2, 3]]),
array([[ 2, 3, 0],
[ 6, 78, 8],
[ 1, 2, 0]]),
array([[ 2, 3, 0],
[ 6, 78, 8],
[ 1, 2, 3]])]


Related Topics



Leave a reply



Submit