sagivmal
sagivmal

Reputation: 86

Summing matrix rows excluding indices from other array

Say I have some matrix, W = MxN and a long array of indices z with shape of Mx1.

Now, assume I'd like to sum up the element of each row in W, excluding the index appears for that row in z.

1-d example:

import numpy as np

W = np.array([1.0, 2.0, 8.0])
z = 2
np.sum(np.delete(W,z))

MxN example and desired output:

import numpy as np

W = np.array([[1.0,2.0,8.0], [5.0,15.0,3.0]])
z = np.array([0,2]).reshape(2,1)

# desired output
# [10. 20.]

I tried to use np.delete and axis=1 with no success

I managed to get around it using tricks like:

W = np.array([[1.0,2.0,8.0], [5.0,15.0,3.0]])
z = np.array([0,2])
W[np.arange(z.shape[0]), z]=0
print(np.sum(W, axis=1))
# [10. 20.]

but I'm wondering if there's a more elegant way.

Upvotes: 3

Views: 640

Answers (2)

Andy L.
Andy L.

Reputation: 25249

@Divaka answers are pretty good. I just give another perspective on your question. If you need masking to ignore certain indices and doing multiple operations on array, you should use numpy masked array np.ma.array instead of regular np.array. Masked array is truly for the purpose of ignore certain indices.

document of masked array for more info

z = np.array([0,2]).reshape(2,1)
W_ma = np.ma.array(W, mask=z == np.arange(W.shape[-1]))

In [36]: W_ma
Out[36]:
masked_array(
  data=[[--, 2.0, 8.0],
        [5.0, 15.0, --]],
  mask=[[ True, False, False],
        [False, False,  True]],
  fill_value=1e+20)

From this W_ma masked array, you may do almost all operations the same as np.array. For sum

W_ma.sum(1)

Out[44]:
masked_array(data=[10.0, 20.0],
             mask=[False, False],
       fill_value=1e+20)

To turn masked array to regular array, you may use compressed, filled, or compress_rowcols

In [46]: W_ma.sum(1).compressed()
Out[46]: array([10., 20.])

Note: I emphasize masked array is useful when you do multiple operations on ignore indices. If you only need to do one or two operations on ignore indices, there is no point to use masked array.

Upvotes: 2

Divakar
Divakar

Reputation: 221574

Using broadcasting to get the mask to simulate deletion and then sum-reduce -

(W*(z != np.arange(W.shape[-1]))).sum(-1)

Sample runs -

For 2D case :

In [61]: W = np.array([[1.0,2.0,8.0], [5.0,15.0,3.0]])
    ...: z = np.array([0,2]).reshape(2,1)

In [62]: (W*(z != np.arange(W.shape[-1]))).sum(-1)
Out[62]: array([10., 20.])

Works just as well for the 1D case :

In [59]: W = np.array([1.0, 2.0, 8.0])
    ...: z = 2

In [60]: (W*(z != np.arange(W.shape[-1]))).sum(-1)
Out[60]: 3.0

For 2D case :

With np.einsum for the sum-reduction -

In [53]: np.einsum('ij,ij->i',W,z != np.arange(W.shape[1]))
Out[53]: array([10., 20.])

Summing and then subtracting the z-indexed values for 2D case -

In [134]: W.sum(1) - np.take_along_axis(W,z,axis=1).squeeze(1)
Out[134]: array([10., 20.])

Extend to handle both 2D and 1D cases -

W.sum(-1)-np.take_along_axis(W,np.atleast_1d(z),axis=-1).squeeze(-1)

Upvotes: 3

Related Questions