给出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
只是想知道是否还有另一种更好的方法来实现这一点?
答案 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
。
尚不清楚在某些极端情况下该怎么做:
当您具有近似匹配项时,应将哪个元素添加到结果中?此impl仅引入b
的元素(如示例)。
应如何处理重复项?在这个暗示中,如果两个列表都包含一个dup,它将被添加两次。如果只有一个列表包含重复项,则元素将仅添加一次。关于dups的一个更复杂的示例是了解我们是否将[4,5]
和[4,5]
作为输入,我们的输出应该是[4,5]
还是[4,4,5,5]
(因为4
和{ {1}}都在5
之内,approx
也与4
相匹配。)
绑定的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个要求:
匹配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]