20

I have a (w,h) np array in 2d. I want to make a 3d dimension that has a value greater than 1 and copy its value over along the 3rd dimensions. I was hoping broadcast would do it but it can't. This is how i'm doing it

arr = np.expand_dims(arr, axis=2)
arr = np.concatenate((arr,arr,arr), axis=2)

is there a a faster way to do so?

kmario23
  • 57,311
  • 13
  • 161
  • 150
user1871528
  • 1,655
  • 3
  • 27
  • 41
  • 4
    `np.stack((arr,arr,arr), -1)` combines the expand dims and concatenate into one expression. No speedup, just shorter. – hpaulj Sep 13 '16 at 06:28

7 Answers7

16

You can push all dims forward, introducing a singleton dim/new axis as the last dim to create a 3D array and then repeat three times along that one with np.repeat, like so -

arr3D = np.repeat(arr[...,None],3,axis=2)

Here's another approach using np.tile -

arr3D = np.tile(arr[...,None],3)
Divakar
  • 218,885
  • 19
  • 262
  • 358
  • 1
    It turned out to be the case that both of these options are [not better than `numpy.concatenate`](https://stackoverflow.com/a/55754233/2956066). Just `numpy.concatenate` would do the job, in the fastest manner! – kmario23 Apr 18 '19 at 22:10
8

Another approach that works:

x_train = np.stack((x_train,) * 3, axis=-1)

liamzebedee
  • 14,010
  • 21
  • 72
  • 118
4

Better helpful in converting gray a-channel matrix into 3 channel matrix.

img3 = np.zeros((gray.shape[0],gray.shape[1],3))
img3[:,:,0] = gray
img3[:,:,1] = gray
img3[:,:,2] = gray
fig = plt.figure(figsize = (15,15))
plt.imshow(img3)
4

Another simple approach is to use matrix multiplication - multiplying by a matrix of ones that will essentially copy the values across the new dimension:

a=np.random.randn(4,4)    #a.shape = (4,4)
a = np.expand_dims(a,-1)  #a.shape = (4,4,1)
a = a*np.ones((1,1,3))
a.shape                   #(4, 4, 3)
Brian
  • 282
  • 4
  • 10
2

I'd suggest you to use the barebones numpy.concatenate() simply because the below piece of code shows that it's the fastest among all other suggested answers:

# sample 2D array to work with
In [51]: arr = np.random.random_sample((12, 34))

# promote the array `arr` to 3D and then concatenate along `axis 2`
In [52]: arr3D = np.concatenate([arr[..., np.newaxis]]*3, axis=2)

# verify for desired shape
In [53]: arr3D.shape
Out[53]: (12, 34, 3)

You can see the timings below to convince yourselves. (ordered: best to worst):

In [42]: %timeit -n 100000 np.concatenate([arr[..., np.newaxis]]*3, axis=2)
1.94 µs ± 32.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [43]: %timeit -n 100000 np.repeat(arr[..., np.newaxis], 3, axis=2)
4.38 µs ± 46.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [44]: %timeit -n 100000 np.dstack([arr]*3)
5.1 µs ± 57.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [49]: %timeit -n 100000 np.stack([arr]*3, -1)
5.12 µs ± 125 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [46]: %timeit -n 100000 np.tile(arr[..., np.newaxis], 3)
7.13 µs ± 85.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Having said that, if you're looking for shortest piece of code, then you can use:

# wrap your 2D array in an iterable and then multiply it by the needed depth
arr3D = np.dstack([arr]*3)

# verify shape
print(arr3D.shape)
(12, 34, 3)
kmario23
  • 57,311
  • 13
  • 161
  • 150
2

This would work. (I think this would not a recommended way :-) But maybe this is the most closest way you thought.)

np.array([img, img, img]).transpose(1,2,0)

just stacking targets(img) any time you want(3), and make the channel(3) go to the last axis.

plhn
  • 5,017
  • 4
  • 47
  • 47
1

Not sure if I understood correctly, but broadcasting seems working to me in this case:

>>> a = numpy.array([[1,2], [3,4]])
>>> c = numpy.zeros((4, 2, 2))
>>> c[0] = a
>>> c[1:] = a+1
>>> c
array([[[ 1.,  2.],
        [ 3.,  4.]],

       [[ 2.,  3.],
        [ 4.,  5.]],

       [[ 2.,  3.],
        [ 4.,  5.]],

       [[ 2.,  3.],
        [ 4.,  5.]]])
6502
  • 112,025
  • 15
  • 165
  • 265
  • what i meant was to use numpy built in broadcast function, which you do not use in your example above. Bt your method seems to work as well. – user1871528 Sep 13 '16 at 22:50