检查一个值是否存在于一个非常大的列表的最快方法是什么?
7 in a
最清晰最快的方法。
您也可以考虑使用一个集合,但是从列表中构造该集合所花费的时间可能比快速成员测试所节省的时间要多。唯一确定的方法就是做好基准测试。(这也取决于你需要什么操作)
你可以把你的物品放在一个集合里。设置查找非常有效。
Try:
s = set(a)
if 7 in s:
# do stuff
在注释中,你说你想获取元素的索引。不幸的是,集合没有元素位置的概念。另一种方法是对列表进行预先排序,然后在每次需要查找元素时使用二分搜索。
def check_availability(element, collection: iter):
return element in collection
使用
check_availability('a', [1,2,3,4,'a','b','c'])
我相信这是知道所选值是否在数组中的最快方法。
a = [4,2,3,1,5,6]
index = dict((y,x) for x,y in enumerate(a))
try:
a_index = index[7]
except KeyError:
print "Not found"
else:
print "found"
只有在a没有改变的情况下,这才会是一个好主意,因此我们可以只执行一次dict()部分,然后重复使用它。如果a确实发生了变化,请提供更多关于您正在做什么的细节。
这不是代码,而是快速搜索的算法。
如果您的列表和您正在寻找的值都是数字,这是相当简单的。如果字符串:看底部:
-Let "n" be the length of your list -Optional step: if you need the index of the element: add a second column to the list with current index of elements (0 to n-1) - see later Order your list or a copy of it (.sort()) Loop through: Compare your number to the n/2th element of the list If larger, loop again between indexes n/2-n If smaller, loop again between indexes 0-n/2 If the same: you found it Keep narrowing the list until you have found it or only have 2 numbers (below and above the one you are looking for) This will find any element in at most 19 steps for a list of 1.000.000 (log(2)n to be precise)
如果您还需要您的数字的原始位置,请在第二索引列中查找。
如果您的列表不是由数字组成的,该方法仍然有效,并且将是最快的,但您可能需要定义一个可以比较/排序字符串的函数。
当然,这需要使用sorted()方法,但如果您不断重用相同的列表进行检查,那么这样做可能是值得的。
听起来您的应用程序可能会从使用Bloom Filter数据结构中获得优势。
简而言之,bloom过滤器查找可以非常快速地告诉你一个值是否绝对不存在于一个集合中。否则,您可以执行较慢的查找,以获得可能在列表中的值的索引。因此,如果您的应用程序倾向于获得“未找到”结果,而不是“找到”结果,您可能会通过添加Bloom Filter看到速度的提高。
关于细节,维基百科提供了Bloom过滤器如何工作的很好的概述,在网上搜索“python Bloom过滤器库”将提供至少两个有用的实现。
正如其他人所说,对于大型列表,in可能非常慢。这里比较了in, set和bisect的性能。注意时间(秒)是对数尺度。
测试代码:
import random
import bisect
import matplotlib.pyplot as plt
import math
import time
def method_in(a, b, c):
start_time = time.time()
for i, x in enumerate(a):
if x in b:
c[i] = 1
return time.time() - start_time
def method_set_in(a, b, c):
start_time = time.time()
s = set(b)
for i, x in enumerate(a):
if x in s:
c[i] = 1
return time.time() - start_time
def method_bisect(a, b, c):
start_time = time.time()
b.sort()
for i, x in enumerate(a):
index = bisect.bisect_left(b, x)
if index < len(a):
if x == b[index]:
c[i] = 1
return time.time() - start_time
def profile():
time_method_in = []
time_method_set_in = []
time_method_bisect = []
# adjust range down if runtime is too long or up if there are too many zero entries in any of the time_method lists
Nls = [x for x in range(10000, 30000, 1000)]
for N in Nls:
a = [x for x in range(0, N)]
random.shuffle(a)
b = [x for x in range(0, N)]
random.shuffle(b)
c = [0 for x in range(0, N)]
time_method_in.append(method_in(a, b, c))
time_method_set_in.append(method_set_in(a, b, c))
time_method_bisect.append(method_bisect(a, b, c))
plt.plot(Nls, time_method_in, marker='o', color='r', linestyle='-', label='in')
plt.plot(Nls, time_method_set_in, marker='o', color='b', linestyle='-', label='set')
plt.plot(Nls, time_method_bisect, marker='o', color='g', linestyle='-', label='bisect')
plt.xlabel('list size', fontsize=18)
plt.ylabel('log(time)', fontsize=18)
plt.legend(loc='upper left')
plt.yscale('log')
plt.show()
profile()
请注意,in操作符不仅测试相等性(==),还测试恒等式(is),列表的in逻辑大致相当于以下内容(实际上是用C而不是Python编写的,至少在CPython中是这样):
对于s中的元素: 如果元素是目标: #快速检查身份意味着相等 还真 如果element == target: #慢速检查实际相等 还真 返回假
在大多数情况下,这个细节是无关紧要的,但在某些情况下,它可能会让Python新手感到惊讶,例如numpy。NAN具有不等于自身的不寻常性质:
>>> import numpy
>>> numpy.NAN == numpy.NAN
False
>>> numpy.NAN is numpy.NAN
True
>>> numpy.NAN in [numpy.NAN]
True
为了区分这些不寻常的情况,你可以使用任何(),比如:
>>> lst = [numpy.NAN, 1 , 2]
>>> any(element == numpy.NAN for element in lst)
False
>>> any(element is numpy.NAN for element in lst)
True
注意,使用any()的列表的in逻辑将是:
any(element is target or element == target for element in lst)
然而,我应该强调这是一个边缘情况,对于绝大多数情况,in操作符是高度优化的,当然正是你想要的(无论是对列表还是对集合)。
最初的问题是:
知道一个值是否存在于一个列表(一个列表 它有数百万个值),它的索引是什么?
因此,有两件事需要发现:
是列表中的一项,和 索引是什么(如果在列表中)。
为此,我修改了@xslittlegrass代码来计算所有情况下的索引,并添加了一个额外的方法。
结果
方法是:
基本上,if x In b: return b。index(x) 在b.index(x)上尝试/捕获(跳过必须检查x是否在b中) Set——基本上如果x在Set (b):返回b.index(x) 平分——对b和它的下标排序,对x在排序(b)中进行二分搜索。 注意来自@ xsllittlegrass的mod,它返回排序后的b的下标, 而不是原来的b) 反向——为b形成一个反向查找字典d;然后 D [x]提供了x的索引。
结果表明,方法5速度最快。
有趣的是,try方法和set方法在时间上是等价的。
测试代码
import random
import bisect
import matplotlib.pyplot as plt
import math
import timeit
import itertools
def wrapper(func, *args, **kwargs):
" Use to produced 0 argument function for call it"
# Reference https://www.pythoncentral.io/time-a-python-function/
def wrapped():
return func(*args, **kwargs)
return wrapped
def method_in(a,b,c):
for i,x in enumerate(a):
if x in b:
c[i] = b.index(x)
else:
c[i] = -1
return c
def method_try(a,b,c):
for i, x in enumerate(a):
try:
c[i] = b.index(x)
except ValueError:
c[i] = -1
def method_set_in(a,b,c):
s = set(b)
for i,x in enumerate(a):
if x in s:
c[i] = b.index(x)
else:
c[i] = -1
return c
def method_bisect(a,b,c):
" Finds indexes using bisection "
# Create a sorted b with its index
bsorted = sorted([(x, i) for i, x in enumerate(b)], key = lambda t: t[0])
for i,x in enumerate(a):
index = bisect.bisect_left(bsorted,(x, ))
c[i] = -1
if index < len(a):
if x == bsorted[index][0]:
c[i] = bsorted[index][1] # index in the b array
return c
def method_reverse_lookup(a, b, c):
reverse_lookup = {x:i for i, x in enumerate(b)}
for i, x in enumerate(a):
c[i] = reverse_lookup.get(x, -1)
return c
def profile():
Nls = [x for x in range(1000,20000,1000)]
number_iterations = 10
methods = [method_in, method_try, method_set_in, method_bisect, method_reverse_lookup]
time_methods = [[] for _ in range(len(methods))]
for N in Nls:
a = [x for x in range(0,N)]
random.shuffle(a)
b = [x for x in range(0,N)]
random.shuffle(b)
c = [0 for x in range(0,N)]
for i, func in enumerate(methods):
wrapped = wrapper(func, a, b, c)
time_methods[i].append(math.log(timeit.timeit(wrapped, number=number_iterations)))
markers = itertools.cycle(('o', '+', '.', '>', '2'))
colors = itertools.cycle(('r', 'b', 'g', 'y', 'c'))
labels = itertools.cycle(('in', 'try', 'set', 'bisect', 'reverse'))
for i in range(len(time_methods)):
plt.plot(Nls,time_methods[i],marker = next(markers),color=next(colors),linestyle='-',label=next(labels))
plt.xlabel('list size', fontsize=18)
plt.ylabel('log(time)', fontsize=18)
plt.legend(loc = 'upper left')
plt.show()
profile()
如果您只想检查列表中是否存在一个元素,
7 in list_data
是最快的解决方案。请注意
7 in set_data
是一个近乎自由的操作,与集合的大小无关!从一个大列表中创建一个set要比在列表中慢300到400倍,所以如果您需要检查许多元素,首先创建一个set会更快。
用perfplot创建的Plot:
import perfplot
import numpy as np
def setup(n):
data = np.arange(n)
np.random.shuffle(data)
return data, set(data)
def list_in(data):
return 7 in data[0]
def create_set_from_list(data):
return set(data[0])
def set_in(data):
return 7 in data[1]
b = perfplot.bench(
setup=setup,
kernels=[list_in, set_in, create_set_from_list],
n_range=[2 ** k for k in range(24)],
xlabel="len(data)",
equality_check=None,
)
b.save("out.png")
b.show()
空间数据的边缘情况
可能有更快的算法来处理空间数据(例如重构以使用k-d树),但检查向量是否在数组中的特殊情况是有用的:
如果你有空间数据(即笛卡尔坐标) 如果你有整数掩码(即数组过滤)
在这种情况下,我想知道由两点定义的(无向)边是否在(无向)边的集合中,这样
(pair in unique_pairs) | (pair[::-1] in unique_pairs) for pair in pairs
其中pair构成两个任意长度的向量(即形状(2,N))。
如果这些向量之间的距离是有意义的,那么检验可以用一个浮点不等式来表示
test_result = Norm(v1 - v2) < Tol
和“值存在于列表”是简单的任何(test_result)。
下面是整数对和R3向量对的示例代码和虚拟测试集生成器。
# 3rd party
import numpy as np
import numpy.linalg as LA
import matplotlib.pyplot as plt
# optional
try:
from tqdm import tqdm
except ModuleNotFoundError:
def tqdm(X, *args, **kwargs):
return X
print('tqdm not found. tqdm is a handy progress bar module.')
def get_float_r3_pairs(size):
""" generate dummy vector pairs in R3 (i.e. case of spatial data) """
coordinates = np.random.random(size=(size, 3))
pairs = []
for b in coordinates:
for a in coordinates:
pairs.append((a,b))
pairs = np.asarray(pairs)
return pairs
def get_int_pairs(size):
""" generate dummy integer pairs (i.e. case of array masking) """
coordinates = np.random.randint(0, size, size)
pairs = []
for b in coordinates:
for a in coordinates:
pairs.append((a,b))
pairs = np.asarray(pairs)
return pairs
def float_tol_pair_in_pairs(pair:np.ndarray, pairs:np.ndarray) -> np.ndarray:
"""
True if abs(a0 - b0) <= tol & abs(a1 - b1) <= tol for (ai1, aj2), (bi1, bj2)
in [(a01, a02), ... (aik, ajl)]
NB this is expected to be called in iteration so no sanitization is performed.
Parameters
----------
pair : np.ndarray
pair of vectors with shape (2, M)
pairs : np.ndarray
collection of vector pairs with shape (N, 2, M)
Returns
-------
np.ndarray
(pair in pairs) | (pair[::-1] in pairs).
"""
m1 = np.sum( abs(LA.norm(pairs - pair, axis=2)) <= (1e-03, 1e-03), axis=1 ) == 2
m2 = np.sum( abs(LA.norm(pairs - pair[::-1], axis=2)) <= (1e-03, 1e-03), axis=1 ) == 2
return m1 | m2
def get_unique_pairs(pairs:np.ndarray) -> np.ndarray:
"""
apply float_tol_pair_in_pairs for pair in pairs
Parameters
----------
pairs : np.ndarray
collection of vector pairs with shape (N, 2, M)
Returns
-------
np.ndarray
pair if not ((pair in rv) | (pair[::-1] in rv)) for pair in pairs
"""
pairs = np.asarray(pairs).reshape((len(pairs), 2, -1))
rv = [pairs[0]]
for pair in tqdm(pairs[1:], desc='finding unique pairs...'):
if not any(float_tol_pair_in_pairs(pair, rv)):
rv.append(pair)
return np.array(rv)
推荐文章
- 如何更新SQLAlchemy行条目?
- name 'reduce'在Python中没有定义
- 如何计算一个NumPy bool数组中的真实元素的数量
- 在python中,在函数结束(例如检查失败)之前退出函数(没有返回值)的最佳方法是什么?
- 在Python中检查一个单词是否在字符串中
- Python glob多个文件类型
- 如何可靠地打开与当前运行脚本在同一目录下的文件
- Python csv字符串到数组
- 如何在Python中进行热编码?
- 如何嵌入HTML到IPython输出?
- 在Python生成器上使用“send”函数的目的是什么?
- 在list中获取不同值的列表
- 是否可以将已编译的.pyc文件反编译为.py文件?
- Django模型表单对象的自动创建日期
- 在Python中包装长行