簡體   English   中英

如何有效地比較兩組無序向量(`np.ndarray`)?

[英]How to efficiently compare two sets of unordered vectors (`np.ndarray`)?

我試圖讓我的問題更清楚。 老題外話在這結尾還能找到。

問題:我有一個n x 3 矩陣A ( np.ndarray ),在三個維度上有n個點。 如果這些點作為無序集是靜止的,我們就說這些點關於變換R (3×3 矩陣)是對稱的。

這意味着AA @ 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或類似),我想接受兩個向量vw相等:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM