如何比较python中2个排序的数字列表,其中每个对应的元素不必完全匹配?

时间:2018-10-27 14:06:39

标签: python numpy

给出2个排序数字列表,如下所示:

>>> list1 = list(map(int, '7 22 34 49 56 62 76 82 89 161 174'.split()))
>>> list2 = list(map(int, '7 14 49 57 66 76 135 142 161'.split()))

numpy.in1d将产生7、49、76和161的真实值。

但是我想接受一定的公差,最多3位,并得出7、49, 57 ,76和161的真实值(因为list1中的56和list2中的57仅相差1)

我编写了以下代码来产生所需的结果,其中FifoList()是fifo堆栈的实现:

    class FifoList:
        def __init__(self):
            self.data = []

        def append(self, data):
            self.data.append(data)

        def pop(self):
            return self.data.pop(0)

    def match_approximate(a, b, approx=3):
        c = []
        bEnd = False
        bfifo = FifoList()
        for i in b:
            bfifo.append(i)
        y = 0
        for x in a:
            if bEnd:
                continue
            while True:
                if y == 0 or x - y > approx:
                    try:
                        y = bfifo.pop()
                    except KeyError:
                        bEnd = True
                        break
                if abs(x - y) <= approx:
                    c.append(y)
                    break
                if y > x:
                    break
        return c

只是想知道是否还有另一种更好的方法来实现这一点?

3 个答案:

答案 0 :(得分:2)

我不确定您为什么在这里使用队列。似乎不适合该问题的数据结构。此外,Python具有内置的队列数据结构(collections.deque,只需使用popleft()代替pop(0))即可。

一种简单的方法(imo)是从开始就只维护每个数组的“指针”(或索引)。如果元素彼此在approx之内,则将它们相加,并增加两个指针。如果a的元素小于b的元素,则递增a的指针。否则,增加b的指针。继续操作直到两个指针都用完(即指向列表的末尾)。这以O(N)线性时间运行。这是上述算法的实现:

def match_approximate(a, b, approx=3):
    a_ind, b_ind = 0, 0
    result = []
    while a_ind < len(a) and b_ind < len(b):
        if abs(a[a_ind] - b[b_ind]) <= approx:
            result.append(b[b_ind])
        if a[a_ind] == b[b_ind]:
            b_ind += 1
            a_ind += 1
        elif a[a_ind] < b[b_ind]: a_ind += 1
        else: b_ind += 1

    def match_last_element(a, a_ind, last_elt_of_b, result):
        while a_ind != len(a):
            if abs(a[a_ind] - last_elt_of_b) <= approx:
                result.append(a[a_ind])
                a_ind += 1
            else:
                break

    if a_ind != len(a): match_last_element(a, a_ind, b[-1], result)
    else: match_last_element(b, b_ind, a[-1], result)

    return result

运行

a = [7, 22, 34, 49, 56, 62, 76, 82, 89, 161, 163, 174]
b = [5, 6, 7, 14, 49, 57, 66, 76, 135, 142, 161]
print(match_approximate(a, b, 3))

将输出[5, 6, 7, 49, 57, 76, 161, 163](这是我假定的预期值,但是请参见下文,了解一些不清楚的极端情况)。如果您在当前的展示次数上运行此案例,则会得到IndexError

尚不清楚在某些极端情况下该怎么做:

  1. 当您具有近似匹配项时,应将哪个元素添加到结果中?此impl仅引入b的元素(如示例)。

  2. 应如何处理重复项?在这个暗示中,如果两个列表都包含一个dup,它将被添加两次。如果只有一个列表包含重复项,则元素将仅添加一次。关于dups的一个更复杂的示例是了解我们是否将[4,5][4,5]作为输入,我们的输出应该是[4,5]还是[4,4,5,5](因为4和{ {1}}都在5之内,approx也与4相匹配。)

  3. 绑定的5是包含的还是排除的(即approx<= approx)?

可以随意调整上述含义以处理您认为在这些情况下需要做的任何事情。

HTH。

答案 1 :(得分:0)

从X中选择近似匹配项:

import numpy as np

X = np.array([7,22,34,49,56,62,76,82,89,161,174])  #len:11
Y = np.array([7,14,49,57,66,76,135,142,161])       #len:9

dist = np.abs(Y[:, np.newaxis] - X)
#print(dist)
for i in range(len(Y)):
    for j in dist[i]:
        if -3<=j<=3: #approximation of 3
            idx = dist[i].tolist().index(j)
            print(X[idx])

输出:

7
49
56
76
161

从Y处选择大致匹配项:

import numpy as np

X = np.array([7,22,34,49,56,62,76,82,89,161,174])  #len:11
Y = np.array([7,14,49,57,66,76,135,142,161])       #len:9

dist = np.abs(X[:, np.newaxis] - Y)
#print(dist)
for i in range(len(Y)+1):
    for j in dist[i]:
        if -3<=j<=3:
            #print(j)
            idx = dist[i].tolist().index(j)
            print(Y[idx])

输出:

7
49
57
76
161

答案 2 :(得分:0)

由于@MattMessersmith,我已经实现了我的最终解决方案,可满足2个要求:

  1. 过滤掉两个过于靠近的列表中的项目
  2. 匹配2个彼此接近的列表中的项目

    list1 = [7, 22, 34, 49, 56, 62, 76, 82, 89, 149, 161, 182]
    list2 = [7, 14, 49, 57, 66, 76, 135, 142, 161]
    >>> result = match_approximate(list1, list2, 3)
    >>> print result[0]
    >>> print result[1]
    [7, 49, 56, 76, 161]
    [7, 49, 57, 76, 161]
    >>> result = match_approximate(list1, list2, 1, True)
    >>> print result[0]
    >>> print result[1]
    [22, 34, 62, 82, 89, 149, 182]
    [14, 66, 135, 142]
    

代码如下:

    def match_approximate(a, b, approx, invert=False):
        a_ind, b_ind = 0, 0
        resulta, resultb = [], []
        while a_ind < len(a) and b_ind < len(b):
            aItem, bItem = a[a_ind], b[b_ind]
            if abs(aItem - bItem) <= approx:
                if not invert:
                    resulta.append(aItem)
                    resultb.append(bItem)
                a_ind += 1
                b_ind += 1
                continue
            if aItem < bItem:
                if invert:
                    resulta.append(aItem)
                a_ind += 1
            else:
                if invert:
                    resultb.append(bItem)
                b_ind += 1

        if invert:
            while a_ind != len(a):
                resulta.append(a[a_ind])
                a_ind += 1
            while b_ind != len(b):
                resulta.append(b[b_ind])
                b_ind += 1

        return [resulta, resultb]