[英]How to efficiently compare two sets of unordered vectors (`np.ndarray`)?
我試圖讓我的問題更清楚。 老題外話在這結尾還能找到。
問題:我有一個n
x 3 矩陣A
( np.ndarray
),在三個維度上有n
個點。 如果這些點作為無序集是靜止的,我們就說這些點關於變換R
(3×3 矩陣)是對稱的。
這意味着A
和A @ RT
相差 1. 至多一個排列和 2. 在對排列進行校正后,兩個矩陣可能因數值容差參數不同: np.allclose(A, permuted(A @ RT)) == True
(我事先不知道permuted()
,這肯定取決於R
)。
問題:如何創建以下函數:
def is_symmetric(A, R, atol=1e-5) -> bool:
# checks symmetry as defined above, considering both numerical noise
# and permutation of vectors.
(有關可能的方法以及我的疑慮和嘗試的一些討論將在下面找到。)
舊題外話:
我想檢查以向量表示的點集合中的對稱性。 這意味着檢查這些點是否在空間中應用矩陣變換時是不變的,例如旋轉或平面鏡像。
import numpy as np
R = np.array([[0, -1, 0], # 90° rotation around z as an example
[1, 0, 0],
[0, 0, 1]])
關鍵是我接受向量的排列:只要一些初始位置被轉換為其他一些預先存在的位置,我就可以了。 這意味着檢查從一個到另一個的轉換向量對。
最簡單的解決方案是遍歷A @ RT
的行(其中A
是一個矩陣,其行是點位置)並嘗試匹配A
每個初始行的變換向量,該向量似乎與列數呈二次方增長。
另一種可能性是對向量進行預排序(例如,通過它們的坐標值):
A = np.array([[1, 0, 0], # some points
[0, 1, 0],
[0, 0, 1],
[0.5, 0.5, 0]])
A = np.array(sorted(A, key=lambda v: (v[2], v[1], v[0]))) # sort by z, y, x values
# array([[1. , 0. , 0. ],
# [0.5, 0.5, 0. ],
# [0. , 1. , 0. ],
# [0. , 0. , 1. ]])
A_rotated = np.array(sorted(A @ R.T, key=lambda v: (v[2], v[1], v[0])))
# array([[-1. , 0. , 0. ], # no match
# [-0.5, 0.5, 0. ], # no match
# [ 0. , 1. , 0. ], # match
# [ 0. , 0. , 1. ]]) # match
(這種方法做了兩種排序,所以 O(n ln(n))?)第三個想法是比較從原始向量和旋轉向量創建的集合。 我有一種直覺,這與比較排序向量一樣好。
但還有一件事:如何處理近似比較? 如果例如np.allclose(v, w) == True
或等效(即abs(v - w) < eps
或類似),我想接受兩個向量v
和w
相等:
np.allclose([1, 0, 0], [1, 0, 0])
# True
np.allclose([1, 0, 0], [1 + 1e-5, 0, 0], atol=1e-5)
# True
np.allclose([1, 0, 0], [1 + 1e-4, 0, 0], atol=1e-5)
# False
所以這里的問題是:我如何(有效地)比較兩組(無序)向量的相等性,同時考慮數值近似(例如np.allclose
)?
很簡單,您將<atol
向量的所有值都轉換為 0。您可以使用numpy.vectorize
來做到這numpy.vectorize
,請在此處查找文檔。
import numpy as np
def transform(x, atol):
if x>0 and x-1<atol:
return 1
else:
return x
myarray = np.array([[1, 0, 0], [1 + 1e-4, 0, 0]])
vf = np.vectorize(transform)
new_array = vf(myarray, atol=1e-5)
現在您可以進行對稱驗證。
請告訴我是否回答了您的問題。
更新
我看到您已經在numpy
擁有函數isclose()
,您應該使用它,而不是從頭開始使用我的函數。
盡管如此,我不會刪除我的答案,以保留與之相關的評論。
這是一個使用np.lexsort
工作的函數:
def is_symmetric(A, R, *args, **kwargs):
A = np.asanyarray(A)
A = A[np.lexsort(A.T)]
A_t = A @ np.asanyarray(R).T
A_t = A_t[np.lexsort(A_t.T)]
return np.allclose(A, A_t, *args, **kwargs)
一些結果:
R = np.array([[0, -1, 0], # 90° rotation as an example
[1, 0, 0],
[0, 0, 1]])
is_symmetric([[0, 0, 0]], R)
# True
is_symmetric([[1, 0, 0],
[0, 1, 0],
[0, 0, 0]], R)
# False
is_symmetric([[1, 0, 0],
[0, 1, 0],
[0, 0, 0],
[-1, 0, 0]], R)
# False
is_symmetric([[1, 0, 0],
[0, 1, 0],
[0, 0, 0],
[-1, 0, 0],
[0, -1, 0]], R)
# True
對於 100000 個隨機向量,性能似乎很好:
A = np.random.rand(100000, 3)
%timeit is_symmetric(A, R)
# 82.2 ms ± 75.4 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.