Python 查找二等分范围内的数字

Python 查找二等分范围内的数字,python,search,sorting,binary,bisect,Python,Search,Sorting,Binary,Bisect,我有一个整数列表,我想写一个函数,返回一个范围内的数字子集。类似NumberWithinRange(列表,间隔)函数名的内容 即 list = [4,2,1,7,9,4,3,6,8,97,7,65,3,2,2,78,23,1,3,4,5,67,8,100] interval = [4,20] results = NumbersWithinRange(list, interval) # [4,4,6,8,7,8] 也许我忘了在结果中再写一个数字,但这就是我的想法 该列表的长度可以达到10/20

我有一个整数列表,我想写一个函数,返回一个范围内的数字子集。类似NumberWithinRange(列表,间隔)函数名的内容

list = [4,2,1,7,9,4,3,6,8,97,7,65,3,2,2,78,23,1,3,4,5,67,8,100]
interval = [4,20]
results = NumbersWithinRange(list, interval)  # [4,4,6,8,7,8]
也许我忘了在结果中再写一个数字,但这就是我的想法

该列表的长度可以达到10/2000万,范围通常为几百

关于如何使用python高效地执行此操作的任何建议-我正在考虑使用对分


谢谢。

我会用numpy来做这件事,特别是如果列表那么长的话。例如:

In [101]: list = np.array([4,2,1,7,9,4,3,6,8,97,7,65,3,2,2,78,23,1,3,4,5,67,8,100])
In [102]: list
Out[102]: 
array([  4,   2,   1,   7,   9,   4,   3,   6,   8,  97,   7,  65,   3,
         2,   2,  78,  23,   1,   3,   4,   5,  67,   8, 100])
In [103]: good = np.where((list > 4) & (list < 20)) 
In [104]: list[good]
Out[104]: array([7, 9, 6, 8, 7, 5, 8])

# %timeit says that numpy is MUCH faster than any list comprehension: 
# create an array 10**6 random ints b/w 0 and 100
In [129]: arr = np.random.randint(0,100,1000000)
In [130]: interval = xrange(4,21)
In [126]: %timeit r = [x for x in arr if x in interval]
1 loops, best of 3: 14.2 s per loop

In [136]: %timeit good = np.where((list > 4) & (list < 20)) ; new_list = list[good]
100 loops, best of 3: 10.8 ms per loop

In [134]: %timeit r = [x for x in arr if 4 < x < 20]
1 loops, best of 3: 2.22 s per loop 

In [142]: %timeit filtered = [i for i in ifilter(lambda x: 4 < x < 20, arr)]
1 loops, best of 3: 2.56 s per loop
[101]中的
:list=np.数组([4,2,1,7,9,4,3,6,8,97,7,65,3,2,2,78,23,1,3,4,5,67,8100])
In[102]:列表
出[102]:
数组([4,2,1,7,9,4,3,6,8,97,7,65,3,
2,   2,  78,  23,   1,   3,   4,   5,  67,   8, 100])
在[103]中:good=np.其中((列表>4)和(列表<20))
在[104]中:列表[良好]
Out[104]:数组([7,9,6,8,7,5,8])
#%timeit说numpy比任何列表理解都快得多:
#创建一个数组10**6随机整数b/w 0和100
在[129]中:arr=np.random.randint(01000000)
In[130]:区间=xrange(4,21)
在[126]中:%timeit r=[x代表arr中的x,如果x在间隔中]
1圈,最好3圈:每个圈14.2秒
在[136]中:%timeit good=np.where((列表>4)和(列表<20));新建列表=列表[良好]
100个回路,最好为3:10.8 ms/回路
在[134]中:%timeit r=[x代表arr中的x,如果4
我认为这应该足够有效:

>>> nums = [4,2,1,7,9,4,3,6,8,97,7,65,3,2,2,78,23,1,3,4,5,67,8,100]
>>> r = [x for x in nums if 4 <= x <21]
>>> r
[4, 7, 9, 4, 6, 8, 7, 4, 5, 8]
>>nums=[4,2,1,7,9,4,3,6,8,97,7,65,3,2,2,78,23,1,3,4,5,67,8100]
>>>r=[x代表x,如果4>r,则以nums表示
[4, 7, 9, 4, 6, 8, 7, 4, 5, 8]
编辑:


在J.F.Sebastian的出色观察后,修改了代码。

如果列表未排序,则需要扫描整个列表:

lst = [ 4,2,1,...]
interval=[4,20]
results = [ x for x in lst if interval[0] <= x <= interval[1] ]
由于扫描列表是O(n),排序是O(n lg n),因此可能不值得仅使用对分对列表进行排序,除非您计划使用迭代器进行大量范围提取。

>>> from itertools import ifilter
>>> A = [4,2,1,7,9,4,3,6,8,97,7,65,3,2,2,78,23,1,3,4,5,67,8,100]
>>> [i for i in ifilter(lambda x: 4 < x < 20, A)]
[7, 9, 6, 8, 7, 5, 8]
>>从itertools导入ifilter
>>>A=[4,2,1,7,9,4,3,6,8,97,7,65,3,2,2,78,23,1,3,4,5,67,8100]
>>>[i代表i过滤器中的i(λx:4
让我们创建一个类似于您描述的列表:

import random  
l = [random.randint(-100000,100000) for i in xrange(1000000)]
现在测试一些可能的解决方案:

interval=range(400,800)

def v2():
    """ return a list """
    return [i for i in l if i in interval]

def v3():
    """ return a generator """
    return list((i for i in l if i in interval))

def v4():
    def te(x):
        return x in interval

    return filter(te,l)

def v5():
    return [i for i in ifilter(lambda x: x in interval, l)]    


print len(v2()),len(v3()), len(v4()), len(v5())
cmpthese.cmpthese([v2,v3,v4,v5],micro=True, c=2)
打印此文件:

   rate/sec   usec/pass   v5    v4    v2    v3
v5        0 6929225.922   -- -0.4% -1.0% -1.6%
v4        0 6903028.488 0.4%    -- -0.6% -1.2%
v2        0 6861472.487 1.0%  0.6%    -- -0.6%
v3        0 6817855.477 1.6%  1.2%  0.6%    --
但是,如果
interval
是一个集合而不是一个列表,请注意会发生什么:

interval=set(range(400,800))
cmpthese.cmpthese([v2,v3,v4,v5],micro=True, c=2)

  rate/sec  usec/pass     v5     v4     v3     v2
v5        5 201332.569     -- -20.6% -62.9% -64.6%
v4        6 159871.578  25.9%     -- -53.2% -55.4%
v3       13  74769.974 169.3% 113.8%     --  -4.7%
v2       14  71270.943 182.5% 124.3%   4.9%     --
现在与numpy相比:

na=np.array(l)

def v7():
    """ assume you have to convert from list => numpy array and return a list """
    arr=np.array(l)
    tgt = np.where((arr >= 400) & (arr < 800)) 
    return [arr[x] for x in tgt][0].tolist()


def v8():
    """ start with a numpy list but return a python list """
    tgt = np.where((na >= 400) & (na < 800)) 
    return na[tgt].tolist()


def v9():
    """ numpy all the way through """
    tgt = np.where((na >= 400) & (na < 800)) 
    return [na[x] for x in tgt][0]  
    # or return na[tgt] if you prefer that syntax...    

cmpthese.cmpthese([v2,v3,v4,v5, v7, v8,v9],micro=True, c=2)  

   rate/sec  usec/pass      v5      v4      v7     v3     v2     v8     v9
v5        5 185431.957      --  -17.4%  -24.7% -63.3% -63.4% -93.6% -93.6%
v4        7 153095.007   21.1%      --   -8.8% -55.6% -55.7% -92.3% -92.3%
v7        7 139570.475   32.9%    9.7%      -- -51.3% -51.4% -91.5% -91.5%
v3       15  67983.985  172.8%  125.2%  105.3%     --  -0.2% -82.6% -82.6%
v2       15  67861.438  173.3%  125.6%  105.7%   0.2%     -- -82.5% -82.5%
v8       84  11850.476 1464.8% 1191.9% 1077.8% 473.7% 472.6%     --  -0.0%
v9       84  11847.973 1465.1% 1192.2% 1078.0% 473.8% 472.8%   0.0%     --   
na=np.array(l)
def v7():
“”“假设您必须从list=>numpy数组转换并返回一个列表”“”
arr=np.数组(l)
tgt=np.其中((arr>=400)和(arr<800))
返回[arr[x]用于tgt中的x][0].tolist()
def v8():
“”“以numpy列表开始,但返回python列表”“”
tgt=np.其中((na>=400)和(na<800))
返回na[tgt].tolist()
def v9():
“一路走来”
tgt=np.其中((na>=400)和(na<800))
返回[na[x]表示tgt中的x][0]
#或者返回na[tgt],如果您喜欢该语法。。。
cmpthese.cmpthese([v2,v3,v4,v5,v7,v8,v9],micro=True,c=2)
速率/秒使用/通过v5 v4 v7 v3 v8 v9
v5 185431.957--17.4%-24.7%-63.3%-63.4%-93.6%-93.6%
v4 7 153095.007 21.1%--8.8%-55.6%-55.7%-92.3%-92.3%
v7 7 139570.475 32.9%9.7%--51.3%-51.4%-91.5%-91.5%
v3 15 67983.985 172.8%125.2%105.3%--0.2%-82.6%-82.6%
v2 15 67861.438 173.3%125.6%105.7%0.2%--82.5%-82.5%
v8 84 11850.476 1464.8%1191.9%1077.8%473.7%472.6%--0.0%
v9 84 11847.973 1465.1%1192.2%1078.0%473.8%472.8%0.0%——

很明显,只要您可以一直使用numpy,numpy就比纯python快。否则,请使用一组时间间隔来加快一点…

我想您正在寻找类似的东西

b=[i for i in a if 4<=i<90]
print sorted(set(b))
[4, 5, 6, 7, 8, 9, 23, 65, 67, 78]
b=[i for i in a if 4纯Python有一个类型可以帮助您。它以排序顺序自动维护列表,并已通过数千万个元素的测试。排序列表类型有一个对分函数,您可以使用

from sortedcontainers import SortedList
data = SortedList(...)

def NumbersWithinRange(items, lower, upper):
    start = items.bisect(lower)
    end = items.bisect_right(upper)
    return items[start:end]

subset = NumbersWithinRange(data, 4, 20)

通过这种方式,对分和索引将比扫描整个列表快得多。“排序容器”模块速度非常快,并且有一个页面,其中包含针对替代实现的基准测试。

如果您的数据集不是太稀疏,您可以使用来存储和检索数据。例如:

a = [4,2,1,7,9,4,3,6,8,97,7,65,3,2,2,78,23,1,3,4,5,67,8,100]

# Initalize a list of 0's [0, 0, ...]
# This is assuming that the minimum possible value is 0
bins = [0 for _ in range(max(a) + 1)]  

# Update the bins with the frequency of each number
for i in a:
    bins[i] += 1


def NumbersWithinRange(data, interval):
    result = []
    for i in range(interval[0], interval[1] + 1):
        freq = data[i]
        if freq > 0:
            result += [i] * freq
    return result
这适用于此测试用例:

print(NumbersWithinRange(bins, [4, 20]))
# [4, 4, 4, 5, 6, 7, 7, 8, 8, 9]
为了简单起见,我省略了函数中的一些边界检查


重申一下,这可能在空间和时间使用方面效果更好,但这在很大程度上取决于您的特定数据集。数据集越稀疏,效果越好。

这会起作用,但如果列表的顺序确实是10^6,则速度就越慢;如果列表是排序的,则速度就越快。
i in xrange
未优化(不同于Python 3上范围内的
i
)。它与iterable中的
i相同,也就是说,它一个接一个地枚举值。因此
4当你使用
4时,列表理解是怎么做的?是的,它的速度快了6倍,但仍然比numpyAh慢了大约4个数量级,是的,我现在明白了。我没有密切注意单位(s vs ms).numpy确实快多了。你的时间它不包括
arr[good]
。在numpy数组中逐个枚举项的速度较慢,Python列表应该更快。此外,OP表示生成的数组应该有~100个项;对randint使用更大的限制。不,由于在numpy数组中键入强,在numpy中,这总是比在Python列表中更快。添加了arr[好],没有性能问题您不应该使用
list
作为变量名。Python允许您(以静默方式)重新分配内置的列表构造函数
print(NumbersWithinRange(bins, [4, 20]))
# [4, 4, 4, 5, 6, 7, 7, 8, 8, 9]