Reputation: 31
I have a 3D array of the dimensions 400*800*3. I want to replace all the elements in the third dimension with the average of that dimension. Right now I have been able to accomplish this by using loops.
test_data=np.random.randint(0,256,size=(400,800,3))
for i in range(400):
for j in range(800):
mn = np.mean(test_data[i,j])
test_data[i,j]=mn
I want to know if there is a, efficient and less verbose way of achieving the same objective. Thanks.
Also, what if instead of a mean I would like a weighted average of the three elements i.e the last line should be replaced by
test_data[i,j,0]=test_data[i,j,0]*0.2
test_data[i,j,1]=test_data[i,j,1]*0.5
test_data[i,j,2]=test_data[i,j,2]*0.3
Upvotes: 3
Views: 2693
Reputation: 221574
Get the mean values along the last axis and assign into all places with [:]
-
test_data[:] = test_data.mean(axis=-1,keepdims=1)
Alternatively, we can create a new array with replication along the last axis -
mean_vals = test_data.mean(axis=-1,keepdims=1).astype(test_data.dtype)
test_data_out = np.repeat(mean_vals,3,axis=-1)
For a read-only version and much faster way, use np.broadcast_to
-
test_data_out = np.broadcast_to(mean_vals, test_data.shape)
For the weighted average part, if you meant :
test_data[i,j]= test_data[i,j,0]*0.2 + test_data[i,j,1]*0.5 + test_data[i,j,2]*0.3
For that, we could use multi-dim tensor reduction : np.tensordot
-
W = [0.2,0.5,0.3] # weights
W_mean = np.tensordot(test_data,W, axes=((-1,-1)))[...,None]
test_data[:] = W_mean.astype(test_data.dtype)
Instead, if you meant :
test_data[i,j,0]=test_data[i,j,0]*0.2
test_data[i,j,1]=test_data[i,j,1]*0.5
test_data[i,j,2]=test_data[i,j,2]*0.3
For that, there's no sum-reduction
, so we could simply make use of broadcasting
-
test_data[:] = (test_data*[0.2, 0.5, 0.3]).astype(test_data.dtype)
Upvotes: 3
Reputation: 231385
In [556]: test_data=np.arange(24.).reshape(2,4,3)
In [557]: test_data
Out[557]:
array([[[ 0., 1., 2.],
[ 3., 4., 5.],
[ 6., 7., 8.],
[ 9., 10., 11.]],
[[ 12., 13., 14.],
[ 15., 16., 17.],
[ 18., 19., 20.],
[ 21., 22., 23.]]])
Scaling the values along the last dimension is just a multiplication:
In [558]: test_data*np.array([0.2, 0.5, 0.3])
Out[558]:
array([[[ 0. , 0.5, 0.6],
[ 0.6, 2. , 1.5],
[ 1.2, 3.5, 2.4],
[ 1.8, 5. , 3.3]],
[[ 2.4, 6.5, 4.2],
[ 3. , 8. , 5.1],
[ 3.6, 9.5, 6. ],
[ 4.2, 11. , 6.9]]])
It could be done in place with test_data *= np.array([....])
Taking the mean on the last dimension is just:
In [559]: test_data.mean(axis=-1)
Out[559]:
array([[ 1., 4., 7., 10.],
[ 13., 16., 19., 22.]])
This is 2d. @divakar has shown how to keep it 3d (keep_dims
) or even full size (2,4,1) or even (2,4,3).
In [561]: test_data.mean(axis=-1,keepdims=True)
Out[561]:
array([[[ 1.],
[ 4.],
[ 7.],
[ 10.]],
[[ 13.],
[ 16.],
[ 19.],
[ 22.]]])
np.average
lets you specify weights (weighted average):
In [568]: np.average(test_data, axis=-1, weights=[0.2,0.5,0.3])
Out[568]:
array([[ 1.1, 4.1, 7.1, 10.1],
[ 13.1, 16.1, 19.1, 22.1]])
This is the same as taking a dot product on the last dimension
In [569]: np.dot(test_data, [0.2,0.5,0.3])
Out[569]:
array([[ 1.1, 4.1, 7.1, 10.1],
[ 13.1, 16.1, 19.1, 22.1]])
Upvotes: 0
Reputation: 114521
This should work:
test_data[:,:,0] += test_data[:,:,1]
test_data[:,:,0] += test_data[:,:,2]
test_data[:,:,0] *= 1.0/3
test_data[:,:,1] = test_data[:,:,0]*0.5
test_data[:,:,2] = test_data[:,:,0]*0.3
test_data[:,:,0] *= 0.2
This approach will work "inplace" without requiring extra memory
Upvotes: 0