Массив Numpy расстояний до списка (строка, столбец, расстояние)

У меня есть массив nd, который выглядит следующим образом:

[[ 0.          1.73205081  6.40312424  7.21110255  2.44948974]
 [ 1.73205081  0.          5.09901951  5.91607978  1.        ]
 [ 6.40312424  5.09901951  0.          1.          4.35889894]
 [ 7.21110255  5.91607978  1.          0.          5.09901951]
 [ 2.44948974  1.          4.35889894  5.09901951  0.        ]]

Каждый элемент в этом массиве представляет собой расстояние, и мне нужно превратить его в список со строкой, столбцом, расстоянием следующим образом:

l = [(0,0,0),(0,1, 1.73205081),(0,2, 6.40312424),...,(1,0, 1.73205081),(1,1,0),...,(4,4,0)] 

Кроме того, было бы неплохо удалить диагональные элементы, а также элементы (j,i), поскольку (i,j) уже есть. По сути, можно ли взять только верхнюю треугольную матрицу этого?

Можно ли это сделать эффективно (без большого количества циклов)? Я создал этот массив с квадратной формой, но не смог найти никаких документов для этого.


person Mike El Jackson    schedule 30.01.2017    source источник
comment
Ответ на все ваши вопросы утвердительный. Да, все возможно. Начать работать.   -  person DYZ    schedule 30.01.2017
comment
лол, у меня все работает, но медленно. У меня есть матрица размером 76800 x 3900. Я просто смотрел, есть ли более быстрая реализация, но я думаю, что у Джона было хорошее предложение попробовать это.   -  person Mike El Jackson    schedule 30.01.2017


Ответы (4)


squareform делает все это. Читайте документы и экспериментируйте. Он работает в обоих направлениях. Если вы дадите ему матрицу, он вернет значения верхнего треугольника (сжатая форма). Если вы дадите ему эти значения, он вернет матрицу.

In [668]: M
Out[668]: 
array([[ 0. ,  0.1,  0.5,  0.2],
       [ 0.1,  0. ,  2. ,  0.3],
       [ 0.5,  2. ,  0. ,  0.2],
       [ 0.2,  0.3,  0.2,  0. ]])
In [669]: spatial.distance.squareform(M)
Out[669]: array([ 0.1,  0.5,  0.2,  2. ,  0.3,  0.2])
In [670]: v=spatial.distance.squareform(M)
In [671]: v
Out[671]: array([ 0.1,  0.5,  0.2,  2. ,  0.3,  0.2])
In [672]: spatial.distance.squareform(v)
Out[672]: 
array([[ 0. ,  0.1,  0.5,  0.2],
       [ 0.1,  0. ,  2. ,  0.3],
       [ 0.5,  2. ,  0. ,  0.2],
       [ 0.2,  0.3,  0.2,  0. ]])

Вы также можете указать параметры force и checks, но без них все будет просто по форме.

Индексы могут исходить от triu

In [677]: np.triu_indices(4,1)
Out[677]: 
(array([0, 0, 0, 1, 1, 2], dtype=int32),
 array([1, 2, 3, 2, 3, 3], dtype=int32))

In [680]: np.vstack((np.triu_indices(4,1),v)).T
Out[680]: 
array([[ 0. ,  1. ,  0.1],
       [ 0. ,  2. ,  0.5],
       [ 0. ,  3. ,  0.2],
       [ 1. ,  2. ,  2. ],
       [ 1. ,  3. ,  0.3],
       [ 2. ,  3. ,  0.2]])

Просто для проверки мы можем заполнить матрицу 4x4 этими значениями.

In [686]: A=np.vstack((np.triu_indices(4,1),v)).T
In [687]: MM = np.zeros((4,4))
In [688]: MM[A[:,0].astype(int),A[:,1].astype(int)]=A[:,2]
In [689]: MM
Out[689]: 
array([[ 0. ,  0.1,  0.5,  0.2],
       [ 0. ,  0. ,  2. ,  0.3],
       [ 0. ,  0. ,  0. ,  0.2],
       [ 0. ,  0. ,  0. ,  0. ]])

Эти индексы triu также могут извлекать значения из M:

In [693]: I,J = np.triu_indices(4,1)
In [694]: M[I,J]
Out[694]: array([ 0.1,  0.5,  0.2,  2. ,  0.3,  0.2])

squareform использует скомпилированный код в spatial.distance._distance_wrap, поэтому я ожидаю, что он будет достаточно быстрым для больших массивов. Единственная проблема: он просто возвращает значения сжатой формы, но не индексы. Но учитывая форму, индексы всегда можно рассчитать. Их не нужно хранить вместе со значениями.

person hpaulj    schedule 30.01.2017

Если ваш ввод x, сначала создайте индексы:

i0,i1 = np.indices(x.shape)

Затем:

np.concatenate((i1,i0,x)).reshape(3,5,5).T

Это дает вам первый результат — для всей матрицы.

Что касается выбора только верхнего треугольника, вы можете попробовать np.triu(), но я точно не знаю, какой результат вы ищете. Вы, вероятно, уже можете понять, как замаскировать те части, которые вам не нужны.

person John Zwinck    schedule 30.01.2017
comment
Следующее будет хорошо работать, если x выше диагонали не равно нулю: i0,i1 = np.triu(x,k=1).nonzero(); X = np.concatenate( (i0,i1,x[i0,i1]) ).reshape(3,len(i0)).T , но обратите внимание, что X будет массивом типа float64, а индексы (например, X[0,0], равные float(i0[0])) нужно будет преобразовать в int позже, чтобы получить доступ к x. - person HAL 9001; 30.01.2017

Вы можете попробовать это,

print([(x,y, value) for (x,y), value in np.ndenumerate(numpymatrixarray)])

output [(0, 0, 0.0), (0, 1, 1.7320508100000001), (0, 2, 6.4031242400000004), (0, 3, 7.2111025499999997), (0, 4, 2.4494897400000002), (1, 0, 1.7320508100000001), (1, 1, 0.0), (1, 2, 5.0990195099999998), (1, 3, 5.9160797799999996), (1, 4, 1.0), (2, 0, 6.4031242400000004), (2, 1, 5.0990195099999998), (2, 2, 0.0), (2, 3, 1.0), (2, 4, 4.3588989400000004), (3, 0, 7.2111025499999997), (3, 1, 5.9160797799999996), (3, 2, 1.0), (3, 3, 0.0), (3, 4, 5.0990195099999998), (4, 0, 2.4494897400000002), (4, 1, 1.0), (4, 2, 4.3588989400000004), (4, 3, 5.0990195099999998), (4, 4, 0.0)]
person rajeshcis    schedule 30.01.2017

Вам действительно нужна верхняя треугольная матрица для матрицы [nxm], где n>m? Это даст вам (nxn-n)/2 элементов и потеряет все данные, где m⊖n.

Вероятно, вам нужна нижняя треугольная матрица:

def tri_reduce(m):
    n=m.shape
    if n[0]>n[1]:
        i=np.tril_indices(n[0],1,n[1])
    else:
        i=np.triu_indices(n[0],1,n[1])
    return np.vstack((i,m[i])).T

Хотя я считаю, что перестроение его в список кортежей потребует цикла. list(tri_reduce(m)) даст список nd массивов.

person Daniel F    schedule 30.01.2017