# How can I combine an array of values and mask into a matrix?

2020-05-23 python numpy matrix scipy sparse-matrix

How can I combine an array of values and mask into a matrix, which contains each value from the array exactly once, but only in places where the mask is non-zero? You could say I want to fill-in the mask-matrix with values from the array.

I don't actually care about the order of the original elements in the result.

``````mask = [[0, 1, 0], [1, 0, 1], [0, 1, 0]]
values = [1, 2, 3, 4]

# output: result = [[0, 1, 0], [2, 0, 3], [0, 4, 0]]

# alternative output: result2 = [[0, 2, 0], [1, 0, 3], [0, 4, 0]]
# alternative output: result3 = [[0, 4, 0], [3, 0, 2], [0, 1, 0]]
``````

Note: my actual matrices are significantly less dense than this.

here is something that i have tried,

``````mask = [[0, 1, 0], [1, 0, 1], [0, 1, 0]]
val = [1, 2, 3, 4]
result=list()
k=0

for j in range(0,len(i)):
if i[j]!=0:
i[j]=val[k]
k+=1
result.append(i)
print(result)
``````

output:

``````[[0, 1, 0], [2, 0, 3], [0, 4, 0]]
``````

Some minor modification will be required to this code also:

1. Check if val[k] does not runs out of index

I don’t know if there is a NumPy function for this, so I created my own:

``````import numpy as np

def some_magical_operation_ndarray(m: np.ndarray, v: np.ndarray) -> np.ndarray:
res = np.zeros(m.shape)
n = 0
for i in range(m.shape[0]):
for j in range(m.shape[1]):
if not m[i, j] == 0:
res[i, j] = v[n]
n += 1
if n == len(v):
n = 0
return res

mask = np.asarray([[0, 1, 0], [1, 0, 1], [0, 1, 0]])
values = np.asarray([1, 2, 3, 4])
print(result)
``````

If you want to work with an list instead of an ndarray this solution would work:

``````def some_magical_operation_list(m: list, v: list) -> list:
res = []
n = 0
for i in range(len(m)):
res.append([])
for j in range(len(m[i])):
if not m[i][j] == 0:
res[i].append(v[n])
n += 1
if n == len(v):
n = 0
else:
res[i].append(0)
return res

mask = [[0, 1, 0], [1, 0, 1], [0, 1, 0]]
values = [1, 2, 3, 4]
print(result)
``````
``````In [229]: mask = [[0, 1, 0], [1, 0, 1], [0, 1, 0]]
...: values = [1, 2, 3, 4]
Out[231]:
array([[False,  True, False],
[ True, False,  True],
[False,  True, False]])
In [232]: res =  np.zeros(mask.shape, int)
``````

If `mask` is boolean, then it can be used to index the relevant slots of `res`, either for fetching values, or for assigning them.

``````In [233]: res[mask]
Out[233]: array([0, 0, 0, 0])

In [235]: res
Out[235]:
array([[0, 1, 0],
[2, 0, 3],
[0, 4, 0]])
``````

To use `scipy.sparse` you could do:

``````In [241]: from scipy import sparse
``````

Make a sparse matrix from `mask`:

``````In [243]: M = sparse.csr_matrix(mask, dtype=int)
In [244]: M
Out[244]:
<3x3 sparse matrix of type '<class 'numpy.longlong'>'
with 4 stored elements in Compressed Sparse Row format>
In [245]: M.A
Out[245]:
array([[0, 1, 0],
[1, 0, 1],
[0, 1, 0]], dtype=int64)
``````

and then replace its `data` values with `values`:

``````In [246]: M.data
Out[246]: array([1, 1, 1, 1], dtype=int64)
In [250]: M.data[:] = values
In [251]: M.A
Out[251]:
array([[0, 1, 0],
[2, 0, 3],
[0, 4, 0]], dtype=int64)
``````

Try this:

``````_mask = np.nonzero(mask)
``````

Should work exactly the way you ask for.

Example with a test set:

``````mask = np.random.randint(0, 2, (7,3))
``````

Result:

``````>>> mask (original):
array([[0, 0, 1],
[1, 0, 0],
[1, 1, 1],
[0, 1, 1],
[0, 1, 1],
[0, 1, 0],
[0, 1, 1]])

Make a copy of `mask` and use `_mask` on the copied array if you do not wish to modify `mask` itself.