À titre d'exemple, ma liste est la suivante:
[25.75443, 26.7803, 25.79099, 24.17642, 24.3526, 22.79056, 20.84866, 19.49222, 18.38086, 18.0358, 16.57819, 15.71255, 14.79059, 13.64154, 13.09409, 12.18347, 11.33447, 10.32184, 9.544922, 8.813385, 8.181152, 6.983734, 6.048035, 5.505096, 4.65799]
et je cherche l'indice de la valeur la plus proche de 11.5
. J'ai essayé d'autres méthodes telles que la recherche binaire et bisect_left
mais elles ne fonctionnent pas.
Je ne peux pas trier ce tableau, car l'index de la valeur sera utilisé sur un tableau similaire pour récupérer la valeur à cet index.
Essayez ce qui suit:
min(range(len(a)), key=lambda i: abs(a[i]-11.5))
Par exemple:
>>> a = [25.75443, 26.7803, 25.79099, 24.17642, 24.3526, 22.79056, 20.84866, 19.49222, 18.38086, 18.0358, 16.57819, 15.71255, 14.79059, 13.64154, 13.09409, 12.18347, 11.33447, 10.32184, 9.544922, 8.813385, 8.181152, 6.983734, 6.048035, 5.505096, 4.65799]
>>> min(range(len(a)), key=lambda i: abs(a[i]-11.5))
16
Ou pour obtenir l'index et la valeur:
>>> min(enumerate(a), key=lambda x: abs(x[1]-11.5))
(16, 11.33447)
Que diriez-vous: vous zippez les deux listes, puis vous triez le résultat?
Si vous ne pouvez pas trier le tableau, il n'y a pas de moyen rapide de trouver l'élément le plus proche - vous devez effectuer une itération sur toutes les entrées.
Il existe une solution de contournement mais elle demande beaucoup de travail: écrivez un algorithme de tri qui trie le tableau et (en même temps) met à jour un second tableau qui vous indique où cette entrée était avant le tableau a été trié.
De cette façon, vous pouvez utiliser la recherche binaire pour rechercher l'index de l'entrée la plus proche, puis utiliser cet index pour rechercher l'index d'origine à l'aide du "tableau d'index".
[EDIT] Avec Zip()
, c'est assez simple à réaliser:
array_to_sort = Zip( original_array, range(len(original_array)) )
array_to_sort.sort( key=i:i[0] )
Vous pouvez maintenant effectuer une recherche binaire de la valeur (en utilisant item[0]
). item[1]
vous donnera l'index original.
Traverser tous les articles n’est que linéaire. Si vous voulez trier le tableau, ce serait pire.
Je ne vois pas d'inconvénient à garder une deltax
supplémentaire (la différence minimale jusqu'à présent) et une idx
(l'index de cet élément) et à ne parcourir qu'une fois la liste.
import numpy as np
a = [25.75443, 26.7803, 25.79099, 24.17642, 24.3526, 22.79056, 20.84866, 19.49222, 18.38086, 18.0358, 16.57819, 15.71255, 14.79059, 13.64154, 13.09409, 12.18347, 11.33447, 10.32184, 9.544922, 8.813385, 8.181152, 6.983734, 6.048035, 5.505096, 4.65799]
index = np.argmin(np.abs(np.array(a)-11.5))
a[index] # here is your result
Dans le cas où a est déjà un tableau, la transformation correspondante peut être omise.
Si vous recherchez souvent une longue liste, alors min
échelles très mauvaises (O (n) ou même O (n ^ 2) si vous ajoutez certaines de vos recherches à la liste, je pense) . Bisect est ton ami. Voici ma solution. Il met à l'échelle O(log(n)) dans le pire des cas:
class Closest:
"""Assumes *no* redundant entries - all inputs must be unique"""
def __init__(self, numlist=[], firstdistance=0):
self.numindexes = dict((val, n) for n, val in enumerate(numlist))
self.nums = sorted(self.numindexes)
self.firstdistance = firstdistance
def append(self, num):
if num in self.numindexes:
raise ValueError("Cannot append '%i' it is already used" % num)
self.numindexes[num] = len(self.nums)
bisect.insort(self.nums, num)
def rank(self, target):
rank = bisect.bisect(self.nums, target)
if rank == 0:
pass
Elif len(self.nums) == rank:
rank -= 1
else:
dist1 = target - self.nums[rank - 1]
dist2 = self.nums[rank] - target
if dist1 < dist2:
rank -= 1
return rank
def closest(self, target):
try:
return self.numindexes[self.nums[self.rank(target)]]
except IndexError:
return 0
def distance(self, target):
rank = self.rank(target)
try:
dist = abs(self.nums[rank] - target)
except IndexError:
dist = self.firstdistance
return dist
Utilisez-le comme ceci:
a = [25.75443, 26.7803, 25.79099, 24.17642, 24.3526, 22.79056, 20.84866, 19.49222, 18.38086, 18.0358, 16.57819, 15.71255, 14.79059, 13.64154, 13.09409, 12.18347, 11.33447, 10.32184, 9.544922, 8.813385, 8.181152, 6.983734, 6.048035, 5.505096, 4.65799]
cl = Closest(a)
for x in targets:
rank = cl.rank(x)
print("Closest number:", cl.nums[rank])
print("Closest index:", cl.numindexes[cl.nums[rank]])
Gardez à l'esprit que si l'espace n'est pas important, vous pouvez trier n'importe quelle liste sans déplacer le contenu en créant une liste secondaire des index triés.
N'oubliez pas non plus que si vous le faites, il vous suffit de parcourir tous les éléments de la liste O (n). (Si plusieurs fois, vous voudrez probablement trier pour augmenter l'efficacité ultérieurement)