2

I have an array A. I am identifying ones in each row except the row number itself and creating a list. For example, in A[0], the ones should be identified for locations 2,3,5 and not 0. I present the current and expected output.

import numpy as np

A=np.array([[1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0],
       [0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0],
       [1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0],
       [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
       [0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0],
       [1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0],
       [0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1],
       [0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0],
       [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1],
       [0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1],
       [0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0],
       [0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1]])

output = []
for i, row in enumerate(A):
    ones_indices = np.where(row == 1)[0]
    other_rows = np.arange(A.shape[0])
    other_rows = np.delete(other_rows, i)
    output.append([[i], other_rows.tolist()])

print(output)

The current output is

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

The expected output is

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

3 Answers3

4

The numpy approach would be to fill_diagonal, then to use where:

np.fill_diagonal(A, 0)

row, idx = np.where(A==1)  # np.where(A) if only 0/1

Output:

(array([ 0,  0,  0,  1,  1,  1,  2,  2,  2,  3,  3,  3,  3,  3,  3,  4,  4,
         4,  5,  5,  5,  5,  5,  5,  6,  6,  6,  6,  6,  6,  7,  7,  7,  8,
         8,  8,  8,  8,  8,  9,  9,  9, 10, 10, 10, 11, 11, 11]),
 array([ 2,  3,  5,  3,  4,  6,  0,  3,  5,  0,  1,  2,  4,  5,  6,  1,  3,
         6,  0,  2,  3,  7,  8, 10,  1,  3,  4,  8,  9, 11,  5,  8, 10,  5,
         6,  7,  9, 10, 11,  6,  8, 11,  5,  7,  8,  6,  8,  9]))

If you really want nested lists:

np.fill_diagonal(A, 0)

out = [[[i], np.where(a==1)[0].tolist()] for i, a in enumerate(A)]

Or:

row, idx = np.where(A==1)

x = np.array_split(idx, np.where(row[1:]!=row[:-1])[0]+1)

out = [[[i], j.tolist()] for i,j in enumerate(x)]

Output:

[[[0], [2, 3, 5]],
 [[1], [3, 4, 6]],
 [[2], [0, 3, 5]],
 [[3], [0, 1, 2, 4, 5, 6]],
 [[4], [1, 3, 6]],
 [[5], [0, 2, 3, 7, 8, 10]],
 [[6], [1, 3, 4, 8, 9, 11]],
 [[7], [5, 8, 10]],
 [[8], [5, 6, 7, 9, 10, 11]],
 [[9], [6, 8, 11]],
 [[10], [5, 7, 8]],
 [[11], [6, 8, 9]]]
mozway
  • 194,879
  • 13
  • 39
  • 75
3

This is possibly a very convoluted way of doing this, but it works.

  1. Fill the diagonal of A with zeros, since you don't want the index of the row the 1 is in.
  2. Use np.where to get the locations of the 1s.
  3. Use this answer to group by row.
  4. Use list comprehension to combine the row number and the locations of the 1s.
import numpy as np

A = np.array([[1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0],
              [0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0],
              [1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0],
              [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
              [0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0],
              [1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0],
              [0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1],
              [0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0],
              [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1],
              [0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1],
              [0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0],
              [0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1]])

# step 1
np.fill_diagonal(A, 0)

# step 2
a = np.vstack(np.where(A == 1)).T

# step 3
k = np.split(a[:, 1], np.unique(a[:, 0], return_index=True)[1][1:])

# step 4
res = [[[i], _k.tolist()] for i, _k in enumerate(k)]

With this, we have res:

[[[0], [2, 3, 5]], [[1], [3, 4, 6]], [[2], [0, 3, 5]], [[3], [0, 1, 2, 4, 5, 6]], [[4], [1, 3, 6]], [[5], [0, 2, 3, 7, 8, 10]], [[6], [1, 3, 4, 8, 9, 11]], [[7], [5, 8, 10]], [[8], [5, 6, 7, 9, 10, 11]], [[9], [6, 8, 11]], [[10], [5, 7, 8]], [[11], [6, 8, 9]]]
jared
  • 4,165
  • 1
  • 8
  • 31
  • 1
    Great minds think alike :) – mozway Jun 15 '23 at 05:52
  • 1
    @mozway Yup, just saw your answer. I think my step 3 is a bit overkill. Probably better to go your route straight from `np.where` for each row. – jared Jun 15 '23 at 05:52
  • 1
    The important step is `fill_diagonal` ;) – mozway Jun 15 '23 at 05:53
  • @mozway Agreed. That was the first thing I thought of when they said they didn't want the matching row index in there. – jared Jun 15 '23 at 05:53
  • 1
    I'm not sure what OP really wants, but `np.where(A==1)` on the de-diagonalized array will be the most efficient. The question is whether or not the nested list structure is really needed. – mozway Jun 15 '23 at 05:55
  • 1
    @mozway It's a little strange. Once I have numpy arrays, I try to stick with them. And for this case, I don't like the jagged result. Better to use a `dict` if they want that relationship of row to indices. – jared Jun 15 '23 at 05:56
  • 1
    I agree, thus my initial comment to stick to a flat array if possible. Btw, I think the `array_split` is not a bad idea, I added a variant using the output of `np.where`. – mozway Jun 15 '23 at 06:05
2

Here is another way. I haven't checked the timings. Yet it gives the expected results.

import numpy as np

A=np.array([[1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0],
       [0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0],
       [1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0],
       [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0],
       [0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0],
       [1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0],
       [0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1],
       [0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0],
       [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1],
       [0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1],
       [0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0],
       [0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1]])


# replace row indices with 0
np.fill_diagonal(A,0)
# create a 1D array 
b = np.arange(A.shape[0])
# multiply element wise with broadcasting 
c = np.multiply(A,b)
# get nonzero indices
rw, cl = c.nonzero()
# get [row,column] coordinates 
a = np.c_[rw,cl]
# groupby row and split
val = np.split(a[:,1], np.unique(a[:, 0], return_index=True)[1][1:])
# list comprehension 
out = [[[i],ar.tolist()] for i,ar in enumerate(val)]
print(out)

>> [[[0], [2, 3, 5]], [[1], [3, 4, 6]], [[2], [0, 3, 5]], [[3], [0, 1, 2, 4, 5, 6]], [[4], [1, 3, 6]], [[5], [0, 2, 3, 7, 8, 10]], [[6], [1, 3, 4, 8, 9, 11]], [[7], [5, 8, 10]], [[8], [5, 6, 7, 9, 10, 11]], [[9], [6, 8, 11]], [[10], [5, 7, 8]], [[11], [6, 8, 9]]]
MSS
  • 3,306
  • 1
  • 19
  • 50