Python 3.x 如何比较两个numpy向量列表?

Python 3.x 如何比较两个numpy向量列表?,python-3.x,numpy,python-unittest,Python 3.x,Numpy,Python Unittest,我有两个列表numpy向量,希望确定它们是否代表大致相同的点(但可能以不同的顺序) 我已经找到了一些方法,比如numpy.testing.assert\u allclose,但它不允许不同的顺序。我还发现了unittest.TestCase.assertCountEqual,但这不适用于numpy数组 我最好的方法是什么 import unittest import numpy as np first = [np.array([20, 40]), np.array([20, 60])] se

我有两个
列表
numpy
向量,希望确定它们是否代表大致相同的点(但可能以不同的顺序)

我已经找到了一些方法,比如
numpy.testing.assert\u allclose
,但它不允许不同的顺序。我还发现了
unittest.TestCase.assertCountEqual
,但这不适用于
numpy
数组

我最好的方法是什么

import unittest

import numpy as np

first = [np.array([20, 40]), np.array([20, 60])]
second = [np.array([19.8, 59.7]), np.array([20.1, 40.5])]

np.testing.assert_all_close(first, second, atol=2)  # Fails because the orders are different
unittest.TestCase.assertCountEqual(None, first, second)  # Fails because numpy comparisons evaluate element-wise; and because it doesn't allow a tolerance

一种很好的列表迭代方法

In [1047]: res = []
In [1048]: for i in first:
      ...:     for j in second:
      ...:         diff = np.abs(i-j)
      ...:         if np.all(diff<2):
      ...:             res.append((i,j))            
In [1049]: res
Out[1049]: 
[(array([20, 40]), array([ 20.1,  40.5])),
 (array([20, 60]), array([ 19.8,  59.7]))]
或使用现有阵列测试:

[(i,j) for i in first for j in second if np.allclose(i,j, atol=2)]
给你:)

(基于 )

但可能顺序不同

这是关键要求。这个问题可以看作是图论中的一个经典问题——在未加权区域中寻找完美匹配。是解决这个问题的经典算法

这里我实现了一个

import numpy as np

def is_matched(first, second):
    checked = np.empty((len(first),), dtype=bool)
    first_matching = [-1] * len(first)
    second_matching = [-1] * len(second)

    def find(i):
        for j, point in enumerate(second):
            if np.allclose(first[i], point, atol=2):
                if not checked[j]:
                    checked[j] = True
                    if second_matching[j] == -1 or find(second_matching[j]):
                        second_matching[j] = i
                        first_matching[i] = j
                        return True

    def get_max_matching():
        count = 0
        for i in range(len(first)):
            if first_matching[i] == -1:
                checked.fill(False)
                if find(i):
                    count += 1

        return count

    return len(first) == len(second) and get_max_matching() == len(first)

first = [np.array([20, 40]), np.array([20, 60])]
second = [np.array([19.8, 59.7]), np.array([20.1, 40.5])]
print(is_matched(first, second)) 
# True

first = [np.array([20, 40]), np.array([20, 60])]
second = [np.array([19.8, 59.7]), np.array([20.1, 43.5])]
print(is_matched(first, second))
# False

两个输入列表中的所有数组是否具有相同的形状(包括相同的维数)?列表中是否有相同数量的数组?为什么不将第一个和第二个数组转换为unittest.TestCase.assertCountEqual接受的格式?@Divakar-数组将具有相同的形状(至少,如果它们没有,那么我希望它们所属的unittests失败!)-每个列表中不一定有相同数量的项,因为我是从作为对模拟对象的调用传递的参数中获取它们的。但是,如果有不同数量的元素,测试应该失败。@FilipeAleixo将它们转换为unittest.TestCase.assertCountEqual所接受的格式是什么意思?即使我确实将它们转换为元组,比如说,我也不知道如何将其更改为
assertCountApproxEqual
-esque方法。在python
中,list
set
表示两种不同的含义。而
numpy
array是另一回事。我看不出这种方法如何满足要求:“希望确定它们是否代表大致相同的点(但可能以不同的顺序)。”我发现的是匹配的对。如果不能重复,我们可以将
len(res)
len(first)
len(second)
进行比较。如果点可以以多种方式配对,逻辑将更加复杂。基本上有两个问题-如何检查列表元素中的两个是否为
close
?您如何判断这两个列表是否有一整套
close
点。一个可以表示为
np.allclose
测试,另一个是某种列表或集合组合测试。
import numpy as np
import scipy.spatial
first  = [np.array([20  , 60  ]), np.array([  20,   40])]
second = [np.array([19.8, 59.7]), np.array([20.1, 40.5])]

def pointsProximityCheck(firstListOfPoints, secondListOfPoints, distanceTolerance): 
    pointIndex  = 0
    maxDistance = 0
    lstIndices  = []
    for item in scipy.spatial.distance.cdist( firstListOfPoints, secondListOfPoints ):
        currMinDist = min(item)
        if currMinDist > maxDistance: 
            maxDistance = currMinDist
        if currMinDist < distanceTolerance :
            pass
        else:
            lstIndices.append(pointIndex)
            # print("point with pointIndex [", pointIndex, "] in the first list outside of Tolerance")
        pointIndex+=1
    return (maxDistance, lstIndices)

maxDistance, lstIndicesOfPointsOutOfTolerance = pointsProximityCheck(first, second, distanceTolerance=0.5)
print("maxDistance:", maxDistance, "indicesOfOutOfTolerancePoints", lstIndicesOfPointsOutOfTolerance )  
maxDistance: 0.509901951359 indicesOfOutOfTolerancePoints [1]
import numpy as np

def is_matched(first, second):
    checked = np.empty((len(first),), dtype=bool)
    first_matching = [-1] * len(first)
    second_matching = [-1] * len(second)

    def find(i):
        for j, point in enumerate(second):
            if np.allclose(first[i], point, atol=2):
                if not checked[j]:
                    checked[j] = True
                    if second_matching[j] == -1 or find(second_matching[j]):
                        second_matching[j] = i
                        first_matching[i] = j
                        return True

    def get_max_matching():
        count = 0
        for i in range(len(first)):
            if first_matching[i] == -1:
                checked.fill(False)
                if find(i):
                    count += 1

        return count

    return len(first) == len(second) and get_max_matching() == len(first)

first = [np.array([20, 40]), np.array([20, 60])]
second = [np.array([19.8, 59.7]), np.array([20.1, 40.5])]
print(is_matched(first, second)) 
# True

first = [np.array([20, 40]), np.array([20, 60])]
second = [np.array([19.8, 59.7]), np.array([20.1, 43.5])]
print(is_matched(first, second))
# False