Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/excel/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 通过允许公差匹配两个包含略有不同浮点值的列表_Python_Python 3.x_List - Fatal编程技术网

Python 通过允许公差匹配两个包含略有不同浮点值的列表

Python 通过允许公差匹配两个包含略有不同浮点值的列表,python,python-3.x,list,Python,Python 3.x,List,我有两个包含浮点值的排序列表。第一个列表包含我感兴趣的值(l1),第二个列表包含我要搜索的值(l2)。然而,我并不是在寻找精确的匹配,而是在容忍基于函数的差异。因为我经常做这个搜索(>>100000),而且列表可能相当大(~5000和~200000个元素),所以我对运行时非常感兴趣。起初,我认为我可以使用numpy.isclose(),但我的容忍度不是固定的,而是取决于感兴趣的值。几个嵌套for循环可以工作,但速度非常慢。我相信有一些有效的方法可以做到这一点 #check if two floa

我有两个包含浮点值的排序列表。第一个列表包含我感兴趣的值(
l1
),第二个列表包含我要搜索的值(
l2
)。然而,我并不是在寻找精确的匹配,而是在容忍基于函数的差异。因为我经常做这个搜索(>>100000),而且列表可能相当大(~5000和~200000个元素),所以我对运行时非常感兴趣。起初,我认为我可以使用
numpy.isclose()
,但我的容忍度不是固定的,而是取决于感兴趣的值。几个嵌套for循环可以工作,但速度非常慢。我相信有一些有效的方法可以做到这一点

#check if two floats are close enough to match
def matching(mz1, mz2):
    if abs( (1-mz1/mz2) * 1000000) <= 2:
        return True
    return False

#imagine another huge for loop around everything
l1  = [132.0317, 132.8677, 132.8862, 133.5852, 133.7507]
l2  = [132.0317, 132.0318, 132.8678, 132.8861, 132.8862, 133.5851999, 133.7500]

d = {i:[] for i in l1}
for i in l1:
    for j in l2:
        if matching(i, j):
            d[i].append(j)
结果可能是这样的词典:
d={132.0317:[132.0317,132.0318],132.8677:[132.8678],132.8862:[132.8862,132.8861],133.5852:[133.5851999],133.7507:[]}
但当有比赛时,我实际上做得更多


感谢您的帮助

您的列表已经排序,因此您可以使用类似于MergeSort的“Merge”部分的范例:跟踪
idx1
idx2
的当前元素,当其中一个元素可以接受时,处理它并只推进该索引

d = {i:[] for i in l1}
idx1, idx2 = 0, 0
while idx1 < len(l1):
    while matching(l1[idx1], l2[idx2]) and idx2 < len(l2):
        d[l1[idx1]].append(l2[idx2])
        idx2 += 1
    idx1 += 1

print(d)
# {132.0317: [132.0317, 132.0318], 132.8677: [132.8678], 132.8862: [132.8862, 132.8861], 133.5852: [133.5851999], 133.7507: []}
d={i:[]表示l1中的i}
idx1,idx2=0,0
当idx1
这是
O(len(l1)+len(l2))
,因为它对两个列表的每个元素执行一次


这里需要注意的是,这永远不会“后退”——如果
l1
的当前元素与
l2
的当前元素匹配,但是
l1
的下一个元素也会与
l2
的当前元素匹配,则后者不会被列出。修复这一问题可能需要添加某种“回溯”功能(在最坏的情况下,这会将复杂度级别提高
n
,但仍然比重复遍历两个列表更快)。但是,它确实适用于给定的数据集。

我使用
itertools
重新实现了for循环。为了使it工作,必须对输入进行排序。对于基准测试,我从
l1
生成了1000个项目,从
l2
生成了100000个项目:

from timeit import timeit
from itertools import tee
from random import uniform

#check if two floats are close enough to match
def matching(mz1, mz2):
    if abs( (1-mz1/mz2) * 1000000) <= 2:
        return True
    return False

#imagine another huge for loop around everything
l1 = sorted([uniform(130.00, 135.00) for _ in range(1000)])
l2 = sorted([uniform(130.00, 135.00) for _ in range(100_000)])

def method1():
    d = {i:[] for i in l1}
    for i in l1:
        for j in l2:
            if matching(i, j):
                d[i].append(j)
    return d

def method2():
    iter_2, last_match = tee(iter(l2))
    d = {}
    for i in l1:
        d.setdefault(i, [])
        found = False
        while True:
            j = next(iter_2, None)
            if j is None:
                break
            if matching(i, j):
                d[i].append(j)
                if not found:
                    iter_2, last_match = tee(iter_2)
                    found = True
            else:
                if found:
                    break
        iter_2, last_match = tee(last_match)
    return d

print(timeit(lambda: method1(), number=1))
print(timeit(lambda: method2(), number=1))

如果您转换公式为给定的mz1生成一个mz2值范围,您可以使用二进制搜索在已排序的l2列表中找到第一个匹配项,然后按顺序向上搜索,直到到达该范围的末尾

def getRange(mz1):
    minimum = mz1/(1+2/1000000) 
    maximum = mz1/(1-2/1000000)
    return minimum,maximum

l1  = [132.0317, 132.8677, 132.8862, 133.5852, 133.7507]
l2  = [132.0317, 132.0318, 132.8678, 132.8862, 132.8861, 133.5851999, 133.7500]

l2  = sorted(l2)
from bisect import bisect_left
d = { mz1:[] for mz1 in l1 }
for mz1 in l1:
    lo,hi = getRange(mz1)
    i = bisect_left(l2,lo)
    while i < len(l2) and l2[i]<= hi:
        d[mz1].append(l2[i])
        i+=1
def getRange(mz1):
最小值=mz1/(1+2/1000000)
最大值=mz1/(1-2/1000000)
返回最小值,最大值
l1=[132.0317132.8677132.8862133.5852133.7507]
l2=[132.0317、132.0318、132.8678、132.8862、132.8861、133.5851999、133.7500]
l2=已排序(l2)
从对分导入左对分
d={mz1:[]表示l1}中的mz1
对于l1中的mz1:
lo,hi=getRange(mz1)
i=左二等分(l2,lo)

当il2
的一个元素是否可能匹配
l1
的多个元素?最小值和最大值的阈值是多少?通过去掉阈值创建一个l1_最小数组,通过添加阈值创建一个l1_最大数组。然后可以搜索l1_minl1中的每个元素使用两个二进制搜索来查找
l2
中包含所有可能匹配项的子列表的起始索引和结束索引。对于每个列表,排序是
O(nlogn)
。然后,二进制搜索是
O(nlogm)
,对于大小
n
l1
和大小
m
l2
在范围内迭代是
O(nm)
。总的来说,我认为排序
l2
(使用
O(mlogm)
)将是限制因素。@StardustGogeta Genius!幸运的是,两个列表都已排序。提醒一下:我先尝试了Alain T.的答案,它比我以前的答案快得多(而且很短!)。我也会尝试安德烈·凯斯利的解决方案,然后等待几个小时,等待更多的评论或投票,然后接受答案。非常感谢你!你太棒了。
16.900722101010615
0.030588202003855258
def getRange(mz1):
    minimum = mz1/(1+2/1000000) 
    maximum = mz1/(1-2/1000000)
    return minimum,maximum

l1  = [132.0317, 132.8677, 132.8862, 133.5852, 133.7507]
l2  = [132.0317, 132.0318, 132.8678, 132.8862, 132.8861, 133.5851999, 133.7500]

l2  = sorted(l2)
from bisect import bisect_left
d = { mz1:[] for mz1 in l1 }
for mz1 in l1:
    lo,hi = getRange(mz1)
    i = bisect_left(l2,lo)
    while i < len(l2) and l2[i]<= hi:
        d[mz1].append(l2[i])
        i+=1