Как я могу хранить пары индексов с использованием значений True из булевского квадратного массива симметрии c numpy? - PullRequest
2 голосов
/ 10 июля 2020

У меня есть Numpy Массив с целочисленными значениями 1 или 0 (при необходимости может быть преобразован в логические значения). Массив квадратный и симметричный c (см. Примечание ниже), и мне нужен список индексов, где появляется 1:

Обратите внимание, что array[i][j] == array[j][i] и array[i][i] == 0 по дизайну. Также у меня не может быть дубликатов.

import numpy as np
array = np.array([
    [0, 0, 1, 0, 1, 0, 1],
    [0, 0, 1, 1, 0, 1, 0],
    [1, 1, 0, 0, 0, 0, 1],
    [0, 1, 0, 0, 1, 1, 0],
    [1, 0, 0, 1, 0, 0, 1],
    [0, 1, 0, 1, 0, 0, 0],
    [1, 0, 1, 0, 1, 0, 0]
])

Я хотел бы получить такой результат (порядок каждого подсписка не важен, равно как и порядок каждого элемента в подсписке):

[
    [0, 2], 
    [0, 4], 
    [0, 6], 
    [1, 2], 
    [1, 3],
    [1, 5],
    [2, 6],
    [3, 4],
    [3, 5],
    [4, 6]
]

Еще одно замечание: я бы предпочел не использовать l oop по всем индексам дважды, используя условие j<i, потому что размер моего массива может быть большим, но я знаю, что это возможность - я написал пример этого, используя два цикла for:

result = []
for i in range(array.shape[0]):
    for j in range(i):
        if array[i][j]:
            result.append([i, j])
print(pd.DataFrame(result).sort_values(1).values)


# using dataframes and arrays for formatting but looking for
# 'result' which is a list

# Returns (same as above but columns are the opposite way round):
[[2 0]
 [4 0]
 [6 0]
 [2 1]
 [3 1]
 [5 1]
 [6 2]
 [4 3]
 [5 3]
 [6 4]]

Ответы [ 3 ]

3 голосов
/ 10 июля 2020
In [249]: arr = np.array([ 
     ...:     [0, 0, 1, 0, 1, 0, 1], 
     ...:     [0, 0, 1, 1, 0, 1, 0], 
     ...:     [1, 1, 0, 0, 0, 0, 1], 
     ...:     [0, 1, 0, 0, 1, 1, 0], 
     ...:     [1, 0, 0, 1, 0, 0, 1], 
     ...:     [0, 1, 0, 1, 0, 0, 0], 
     ...:     [1, 0, 1, 0, 1, 0, 0] 
     ...: ])   

Наиболее распространенный способ получения индексов для ненулевых (True) - с помощью np.nonzero (он же np.where):

In [250]: idx = np.nonzero(arr)                                                                      
In [251]: idx                                                                                        
Out[251]: 
(array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6]),
 array([2, 4, 6, 2, 3, 5, 0, 1, 6, 1, 4, 5, 0, 3, 6, 1, 3, 0, 2, 4]))

Это кортеж - 2 массива для 2d массив. Его можно использовать напрямую для индексации массива (или чего-то подобного): arr[idx] выдаст все единицы.

Примените np.transpose к этому и получите массив «пар»:

In [252]: np.argwhere(arr)                                                                           
Out[252]: 
array([[0, 2],
       [0, 4],
       [0, 6],
       [1, 2],
       [1, 3],
       [1, 5],
       [2, 0],
       [2, 1],
       [2, 6],
       [3, 1],
       [3, 4],
       [3, 5],
       [4, 0],
       [4, 3],
       [4, 6],
       [5, 1],
       [5, 3],
       [6, 0],
       [6, 2],
       [6, 4]])

Использование такого массива для индексации arr сложнее - требуется al oop и преобразование в кортеж.

Чтобы отсеять дубликаты симметрии c, мы могли бы создать тройной нижний массив:

In [253]: np.tril(arr)                                                                               
Out[253]: 
array([[0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0],
       [1, 1, 0, 0, 0, 0, 0],
       [0, 1, 0, 0, 0, 0, 0],
       [1, 0, 0, 1, 0, 0, 0],
       [0, 1, 0, 1, 0, 0, 0],
       [1, 0, 1, 0, 1, 0, 0]])

In [254]: np.argwhere(np.tril(arr))                                                                  
Out[254]: 
array([[2, 0],
       [2, 1],
       [3, 1],
       [4, 0],
       [4, 3],
       [5, 1],
       [5, 3],
       [6, 0],
       [6, 2],
       [6, 4]])
     

     
3 голосов
/ 10 июля 2020
idx = np.argwhere(array)
idx = idx[idx[:,0]<idx[:,1]]

Другой способ:

idx = np.argwhere(np.triu(array))

вывод:

[[0 2]
 [0 4]
 [0 6]
 [1 2]
 [1 3]
 [1 5]
 [2 6]
 [3 4]
 [3 5]
 [4 6]]

Сравнение :

#@bousof solution
def method1(array):
  return np.vstack(np.where(np.logical_and(array, np.diff(np.ogrid[:array.shape[0],:array.shape[0]])[0]>=0))).transpose()[:,::-1]

#Also mentioned by @hpaulj
def method2(array):
  return np.argwhere(np.triu(array))

def method3(array):
  idx = np.argwhere(array)
  return idx[idx[:,0]<idx[:,1]]

#The original method in question by OP(d-man)
def method4(array):
  result = []
  for i in range(array.shape[0]):
      for j in range(i):
          if array[i][j]:
              result.append([i, j])
  return result

#suggestd by @bousof in comments
def method5(array):
  return np.vstack(np.where(np.triu(array))).transpose()

inputs = [np.random.randint(0,2,(n,n)) for n in [10,100,1000,10000]]

Похоже на method1 , method2 и method5 немного быстрее для больших массивов, а method3 быстрее для небольших случаев:

введите описание изображения здесь

1 голос
/ 10 июля 2020

Вы можете использовать numpy. Где:

>>> np.vstack(np.where(np.logical_and(array, np.diff(np.ogrid[:array.shape[0],:array.shape[0]])[0]<=0))).transpose()
array([[2, 0],
       [2, 1],
       [3, 1],
       [4, 0],
       [4, 3],
       [5, 1],
       [5, 3],
       [6, 0],
       [6, 2],
       [6, 4]])

np.diff(np.ogrid[:array.shape[0],:array.shape[0]])[0]<=0 истинно только в нижней части матрицы. Если порядок важен, вы можете получить тот же порядок, что и в вопросе, используя:

>>> np.vstack(np.where(np.logical_and(array, np.diff(np.ogrid[:array.shape[0],:array.shape[0]])[0]>=0))).transpose()[:,::-1]
array([[2, 0],
       [4, 0],
       [6, 0],
       [2, 1],
       [3, 1],
       [5, 1],
       [6, 2],
       [4, 3],
       [5, 3],
       [6, 4]])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...