我需要将矩阵的对角线元素设置为Inf。
一种简单的方法是使用np.fill_diagonal
。
np.fill_diagonal(my_matrix, float('inf')
然而fill_diagonal
修改输入矩阵而不是返回填充了对角线的新矩阵。
这对我不起作用。我需要填充对角线而不修改原始矩阵。
当然我可以克隆原始矩阵,所以我将始终保留原始矩阵的副本。但是我并不喜欢这个解决方案,因为我会经常更新我的原始矩阵,因此每次我需要对角线为inf时我都必须复制它。
是否存在与fill_diagonal
相同但不修改输入矩阵的函数?类似的东西:
new_matrix = np.fill_diagonal(original_matrix, float('inf')
为什么我需要这个:
我的矩阵是点之间的距离矩阵,我想在每一步计算两个最近的点。当然,该矩阵的对角线为0(因为从点到自身的距离为0)。所以我的解决方案是确保我没有采取相同的观点是将对角线设置为Inf。
然而,一旦找到两个点,我需要计算这两个点与其余点之间的距离的平均值,所以我实际上需要对角线为0而不是Inf。
目前我正在做的是:
计算这两点与其余点之间的平均距离。
# fill diagonal with Inf to avoid taking the diagonals
np.fill_diagonal(data, float('inf'))
# find the minimum distance
idx = np.argmin(data)
# fill the diagonals back to 0
np.fill_diagonal(data, 0.0)
# get the coordinates of the minimum distance
row, col = np.unravel_index(idx,data.shape)
# compute the new node as the average distance between the two points
new_node = np.mean((data[:,row],data[:,col]),0)
# replace the first node (row) with the new node
data[:,row] = new_node
data[row,:] = new_node.T
# delete the second node (col) from the matrix
data = np.delete(data, col, 0) # delete row
data = np.delete(data, col, 1) # delete column
但是我不喜欢将对角线设置为Inf然后再回到0的想法,我宁愿只将一个函数传递给argmax
,它返回带有填充Inf的对角线的数据而不实际修改矩阵数据。
类似的东西:
idx = np.argmin(return_filled_diagonals(data, float('Inf'))
# here I can operate with data as usual since it has not been modified.
答案 0 :(得分:2)
方法#1
你正在寻找的魔法在NumPy strides
中,它让我们看到没有对角元素的数组,因此不再占用内存空间。这是实现这种观点的实现 -
def nodiag_view(a):
m = a.shape[0]
p,q = a.strides
return np.lib.stride_tricks.as_strided(a[:,1:], (m-1,m), (p+q,q))
让我们看看一个示例运行来验证它的视图 -
In [124]: a # Input array
Out[124]:
array([[ 0, 61, 43, 26, 21],
[20, 0, 78, 29, 64],
[34, 49, 0, 64, 60],
[36, 96, 67, 0, 75],
[36, 85, 40, 74, 0]])
# Get the no-diag view
In [125]: a_nodiag = nodiag_view(a)
# Lets's verify by changing elements in the view and that should change
# elements in the original array too
In [126]: a_nodiag[:] = 999
In [127]: a
Out[127]:
array([[ 0, 999, 999, 999, 999],
[999, 0, 999, 999, 999],
[999, 999, 0, 999, 999],
[999, 999, 999, 0, 999],
[999, 999, 999, 999, 0]])
最后,让我们看看我们如何设置它来解决您的整个问题 -
def argmin_without_diag(a):
a_nodiag = nodiag_view(a)
idx_nodiag = np.argmin(a_nodiag)
m = a.shape[0]
idx = idx_nodiag + np.unravel_index(idx_nodiag, (m-1,m))[0]+1
return np.unravel_index(idx, a.shape)
示例运行 -
In [142]: a
Out[142]:
array([[ 0, 60, 79, 55, 77],
[62, 0, 86, 84, 25],
[32, 96, 0, 74, 89],
[24, 33, 64, 0, 93],
[14, 74, 30, 44, 0]])
In [143]: argmin_without_diag(a)
Out[143]: (4, 0)
方法#2
如果您担心内存和性能,可以暂时将对角线设置为infnite
,然后获取argmin
索引,然后放回原始对角线值。因此,有效地输入数组不变。实现看起来像这样 -
def argmin_without_diag_replacement(a):
# Store diagonal values
vals = a.ravel()[::a.shape[1]+1].copy()
# Set diag ones as infinites
a.ravel()[::a.shape[1]+1] = np.inf
# Get argmin index
idx = np.argmin(a)
# Put back the original diag values
a.ravel()[::a.shape[1]+1] = vals
return np.unravel_index(idx, a.shape)
因此,对于(n x n)
形状的数组,临时数组只有n
个元素。
示例运行 -
In [237]: a
Out[237]:
array([[ 0., 95., 57., 75., 92.],
[ 37., 0., 69., 71., 62.],
[ 42., 72., 0., 30., 57.],
[ 41., 80., 94., 0., 26.],
[ 36., 45., 71., 76., 0.]])
In [238]: argmin_without_diag_replacement(a)
Out[238]: (3, 4)
运行时测试
In [271]: a = np.random.randint(11,99,(1000,1000)).astype(float)
In [272]: np.fill_diagonal(a,0)
In [273]: %timeit argmin_without_diag(a)
1000 loops, best of 3: 1.76 ms per loop
In [274]: %timeit argmin_without_diag_replacement(a)
1000 loops, best of 3: 688 µs per loop
答案 1 :(得分:1)
orig_mat = np.array([[1.2,2,3],[4,5,6],[7,8,9]])
#set diagonal to inf without making a copy of the array.
orig_mat + np.where(np.eye(orig_mat.shape[0])>0,np.inf,0)
array([[ inf, 2., 3.],
[ 4., inf, 6.],
[ 7., 8., inf]])
#the original array remains untorched.
print(orig_mat)
[[ 1.2 2. 3. ]
[ 4. 5. 6. ]
[ 7. 8. 9. ]]
答案 2 :(得分:0)
根据我的理解,你想找到矩阵的min的索引,但不包括对角元素:
orig_mat = np.array([[1.2,2,3],[4,5,6],[7,8,9]])
min_idx = np.argmin(orig_mat+np.multiply(np.eye(orig_mat.shape[0]),1e9))
答案 3 :(得分:0)
我的提名是明确的复制和填写,包含在一个函数
中def foo(mat):
a = mat.copy()
np.fill_diagonal(a,np.inf)
return a
这确实是一个副本,但只有一个。
orig_mat + np.where(np.eye(orig_mat.shape[0])>0,np.inf,0))
也会返回一个新数组,但在此过程中必须添加两个临时数组。所以最终会变慢:
代表orig_mat=np.ones((1000,1000))
In [67]: timeit np.argmin(orig_mat + np.where(np.eye(orig_mat.shape[0])>0, np.inf, 0))
100 loops, best of 3: 18.7 ms per loop
In [68]: timeit np.argmin(foo(orig_mat))
100 loops, best of 3: 6.94 ms per loop
令人惊讶的是@Divakar's
解决方案的速度要快得多:
In [69]: timeit argmin_without_diag(orig_mat)
100 loops, best of 3: 5.97 ms per loop
答案 4 :(得分:-2)
对于(n x n)形状的数组,首先创建一个长度等于n的np.inf列表。
inf_list = [np.inf] * n
然后,使用此inf列表作为numpy.diag()函数的输入。该条目列表将其放在对角线位置。其余条目全为零。
diag_entr = np.diag(inf_list)
如果n等于10,diag_entr看起来像:
[[inf 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[ 0. inf 0. 0. 0. 0. 0. 0. 0. 0.]
[ 0. 0. inf 0. 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. inf 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. inf 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. inf 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0. inf 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0. 0. inf 0. 0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. inf 0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. inf]]
将原始Matix表示为MyMatrix。通过以下操作,使其对角线入口变为无穷大:
MyMatrix = MyMatrix + diag_entr