【问题标题】:How can I compare two lists of numpy vectors?如何比较两个 numpy 向量列表?
【发布时间】:2017-04-02 17:38:28
【问题描述】:

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

我找到了诸如numpy.testing.assert_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

【问题讨论】:

  • 两个输入列表中的所有数组是否都具有相同的形状(包括相同的维数)?列表中的数组数量是否相同?
  • 为什么不将 first 和 second 转换为 unittest.TestCase.assertCountEqual 接受的格式?
  • @Divakar - 数组将具有相同的形状(至少,如果它们没有,那么我希望它们所属的单元测试失败!) - 不一定相同每个列表中的项目数,因为我是从作为调用传递给模拟对象的参数中获取它们的。但是,如果元素数量不同,测试应该会失败。
  • @FilipeAleixo 将它们转换为unittest.TestCase.assertCountEqual 接受的格式是什么意思?即使我确实将它们转换为元组,我也不知道如何将其更改为 assertCountApproxEqual-esque 方法。
  • 在 python 中 listset 意味着 2 个不同的东西。而numpy 数组是另一回事。

标签: python-3.x numpy python-unittest


【解决方案1】:

一个不错的列表迭代方法

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]))]

res 的长度是匹配的数量。

或者作为列表理解:

def match(i,j):
    diff = np.abs(i-j)
    return np.all(diff<2)

In [1051]: [(i,j) for i in first for j in second if match(i,j)]
Out[1051]: 
[(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)]

【讨论】:

  • 看不出这种方法如何满足要求:“希望确定它们是否代表大致相同的点(但可能以不同的顺序)。”
  • 我发现的是匹配的对。如果不能重复,我们可以将len(res)len(first)len(second) 进行比较。如果点可以以多种方式配对,则逻辑将更加复杂。
  • 基本上有2个问题-如何检查两个列表元素是否为close?以及如何判断这两个列表是否有完整的close点。一种可以表示为np.allclose 测试,另一种是某种列表或集合组合测试。
【解决方案2】:

给你:)

( 想法基于 Euclidean distance between points in two different Numpy arrays, not within)

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 )  

给出 distanceTolerance=0.5 的输出:

maxDistance: 0.509901951359 indicesOfOutOfTolerancePoints [1]

【讨论】:

    【解决方案3】:

    但可能顺序不同

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

    我在这里实现了一个。

    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
    

    【讨论】:

      猜你喜欢
      • 2014-02-24
      • 1970-01-01
      • 1970-01-01
      • 2010-12-25
      • 2020-03-25
      • 2022-06-28
      • 1970-01-01
      • 2011-06-19
      相关资源
      最近更新 更多