Quel est le moyen le plus rapide de savoir si une valeur existe dans une liste (une liste contenant des millions de valeurs) et quel est son index?
Je sais que toutes les valeurs de la liste sont uniques, comme dans cet exemple.
La première méthode que j'essaie est (3,8 secondes dans mon code réel):
a = [4,2,3,1,5,6]
if a.count(7) == 1:
b=a.index(7)
"Do something with variable b"
La deuxième méthode que j'essaie est (2x plus rapide: 1.9 sec pour mon code réel):
a = [4,2,3,1,5,6]
try:
b=a.index(7)
except ValueError:
"Do nothing"
else:
"Do something with variable b"
Méthodes proposées par l'utilisateur Stack Overflow (2,74 s pour mon vrai code)):
a = [4,2,3,1,5,6]
if 7 in a:
a.index(7)
Dans mon code réel, la première méthode prend 3,81 secondes et la seconde, 1,88 seconde. C'est une bonne amélioration, mais:
Je suis un débutant avec Python/script, et existe-t-il un moyen plus rapide de faire la même chose et d’économiser plus de temps de traitement?
Explication plus spécifique pour mon application:
Dans l’API de Blender, je peux accéder à une liste de particules:
particles = [1, 2, 3, 4, etc.]
De là, je peux accéder à l'emplacement d'une particule:
particles[x].location = [x,y,z]
Et pour chaque particule, je teste si un voisin existe en recherchant chaque emplacement de particule comme suit:
if [x+1,y,z] in particles.location
"Find the identity of this neighbour particle in x:the particle's index
in the array"
particles.index([x+1,y,z])
7 in a
Le moyen le plus clair et le plus rapide de le faire.
Vous pouvez également envisager d'utiliser un set
, mais la construction de cet ensemble à partir de votre liste risque de prendre plus de temps que les tests d'adhésion plus rapides ne sauveront. Le seul moyen d'être certain est de bien mesurer les performances. (cela dépend aussi de quelles opérations vous avez besoin)
Comme indiqué par d'autres, in
peut être très lent pour les grandes listes. Voici quelques comparaisons des performances pour in
, set
et bisect
. Notez que le temps (en secondes) est en échelle log.
Code pour tester:
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 = []
Nls = [x for x in range(1000,20000,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(math.log(method_in(a,b,c)))
time_method_set_in.append(math.log(method_set_in(a,b,c)))
time_method_bisect.append(math.log(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.show()
def check_availability(element, collection: iter):
return element in collection
tilisation
check_availability('a', [1,2,3,4,'a','b','c'])
Je crois que c'est le moyen le plus rapide de savoir si une valeur choisie est dans un tableau.
Vous pouvez placer vos objets dans un set
. Les recherches de set sont très efficaces.
Essayer:
s = set(a)
if 7 in s:
# do stuff
edit Dans un commentaire, vous indiquez que vous souhaitez obtenir l'index de l'élément. Malheureusement, les ensembles n'ont aucune notion de position d'élément. Une alternative consiste à trier votre liste, puis à utiliser recherche binaire à chaque fois que vous devez rechercher un élément.
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"
Ce ne sera une bonne idée que si ne change pas et que nous pouvons donc faire la partie dict () une fois, puis l’utiliser à plusieurs reprises. Si cela change, veuillez fournir plus de détails sur ce que vous faites.
Il semble que votre application pourrait tirer parti de l’utilisation d’une structure de données Bloom Filter.
En bref, une recherche de filtre de bloom peut vous dire très rapidement si une valeur n'est définitivement PAS présente dans un ensemble. Sinon, vous pouvez effectuer une recherche plus lente pour obtenir l'index d'une valeur qui POURRAIT ÊTRE dans la liste. Ainsi, si votre application tend à obtenir le résultat "non trouvé" beaucoup plus souvent que le résultat "trouvé", vous pouvez voir une accélération en ajoutant un filtre de Bloom.
Pour plus de détails, Wikipedia fournit un bon aperçu du fonctionnement de Bloom Filters, et une recherche sur le Web pour "bibliothèque de filtres Python Bloom" fournira au moins quelques implémentations utiles.
Sachez que l'opérateur in
teste non seulement l'égalité (==
) mais également l'identité (is
), la logique in
pour list
s est à peu près équivalent à ce qui suit (il est écrit en C et non pas Python bien que, du moins en CPython):
for element in s: if element is target: # fast check for identity implies equality return True if element == target: # slower check for actual equality return True return False
Dans la plupart des cas, ces détails ne sont pas pertinents, mais ils peuvent parfois laisser un Python novice surpris, par exemple, numpy.NAN
a la propriété inhabituelle d'être ne pas être égal à lui-même :
>>> import numpy
>>> numpy.NAN == numpy.NAN
False
>>> numpy.NAN is numpy.NAN
True
>>> numpy.NAN in [numpy.NAN]
True
Pour distinguer ces cas inhabituels, vous pouvez utiliser any()
comme:
>>> lst = [numpy.NAN, 1 , 2]
>>> any(element == numpy.NAN for element in lst)
False
>>> any(element is numpy.NAN for element in lst)
True
Notez que la logique in
pour list
s avec any()
serait:
any(element is target or element == target for element in lst)
Cependant, je devrais souligner qu’il s’agit d’un cas Edge, et dans la grande majorité des cas, l’opérateur in
est hautement optimisé et correspond exactement à ce que vous voulez bien entendu (soit avec un list
, soit avec un set
).
Ou utilisez __contains__
:
sequence.__contains__(value)
démo:
>>> l=[1,2,3]
>>> l.__contains__(3)
True
>>>
Ce n'est pas le code, mais l'algorithme pour une recherche très rapide.
Si votre liste et la valeur que vous recherchez sont tous des nombres, c'est assez simple. Si les chaînes: regardez en bas:
Si vous avez également besoin de la position d'origine de votre numéro, recherchez-la dans la deuxième colonne d'index.
Si votre liste n'est pas composée de nombres, la méthode fonctionne toujours et sera la plus rapide, mais vous devrez peut-être définir une fonction permettant de comparer/ordonner des chaînes.
Bien sûr, cela nécessite l’investissement de la méthode triée (), mais si vous continuez à réutiliser la même liste pour vérification, cela peut en valoir la peine.
present = False
searchItem = 'd'
myList = ['a', 'b', 'c', 'd', 'e']
if searchItem in myList:
present = True
print('present = ', present)
else:
print('present = ', present)
La solution de @Winston Ewert accélère considérablement les très grandes listes, mais cette réponse stackoverflow indique que la construction try:/except:/else: sera ralentie si la branche except est souvent atteinte . Une alternative consiste à tirer parti de la méthode .get()
pour le dict:
a = [4,2,3,1,5,6]
index = dict((y, x) for x, y in enumerate(a))
b = index.get(7, None)
if b is not None:
"Do something with variable b"
La méthode .get(key, default)
est juste pour le cas où vous ne pouvez pas garantir qu'une clé sera dans le dict. Si la clé est présente , elle renvoie la valeur (comme le ferait dict[key]
), mais si ce n'est pas le cas, .get()
renvoie votre valeur par défaut (ici None
). Dans ce cas, vous devez vous assurer que la valeur par défaut choisie ne sera pas dans a
.
Pour moi, il était de 0.030 sec (réel), 0.026 sec (utilisateur) et 0.004 sec (sys).
try:
print("Started")
x = ["a", "b", "c", "d", "e", "f"]
i = 0
while i < len(x):
i += 1
if x[i] == "e":
print("Found")
except IndexError:
pass
Code pour vérifier s'il existe dans le tableau deux éléments dont le produit est égal à k:
n = len(arr1)
for i in arr1:
if k%i==0:
print(i)