Façon pythonique de vérifier si une liste est triée ou non
Existe-t-il un moyen pythonique de vérifier si une liste est déjà triée dans ASC
ou DESC
listtimestamps = [1, 2, 3, 5, 6, 7]
Quelque Chose comme isttimestamps.isSorted()
qui retourne True
ou False
.
Je veux entrer une liste d'horodatages pour certains messages et vérifier si les transactions sont apparues dans le bon ordre.
19 réponses
En fait, nous ne donnons pas la réponse qu'anijhaw cherche. Voici le One liner:
all(l[i] <= l[i+1] for i in xrange(len(l)-1))
Pour Python 3:
all(l[i] <= l[i+1] for i in range(len(l)-1))
, je voudrais juste utiliser
if sorted(lst) == lst:
# code here
Sauf si c'est une très grande liste, auquel cas vous voudrez peut-être créer une fonction personnalisée.
Si vous allez simplement le trier s'il n'est pas trié, oubliez la vérification et triez-la.
lst.sort()
Et n'y pense pas trop.
Si vous voulez une fonction personnalisée, vous pouvez faire quelque chose comme
def is_sorted(lst, key=lambda x: x):
for i, el in enumerate(lst[1:]):
if key(el) < key(lst[i]): # i is the index of the previous element
return False
return True
Ce sera O (n) si la liste est déjà triée (et O(N) dans une boucle for
à cela! donc, à moins que vous l'attendez pas triés (et assez aléatoire) la plupart du temps, je voudrais, encore une fois, juste trier la liste.
Cette forme d'itérateur est 10-15% plus rapide que l'utilisation de l'indexation entière:
# from itertools import izip as zip # python 2 only!
def is_sorted(l):
return all(a <= b for a, b in zip(l, l[1:]))
Une belle façon de l'implémenter est d'utiliser la fonction imap
de itertools
:
from itertools import imap, tee
import operator
def is_sorted(iterable, compare=operator.le):
a, b = tee(iterable)
next(b, None)
return all(imap(compare, a, b))
Cette implémentation est rapide et fonctionne sur tous les itérables.
Je le ferais (voler de nombreuses réponses ici [Aaron Sterling, Wai Yip Tung, sorta de Paul McGuire] et surtout Armin Ronacher):
from itertools import tee, izip
def pairwise(iterable):
a, b = tee(iterable)
next(b, None)
return izip(a, b)
def is_sorted(iterable, key=lambda a, b: a <= b):
return all(key(a, b) for a, b in pairwise(iterable))
Une bonne chose: vous n'avez pas besoin de réaliser la deuxième itérable pour la série (contrairement à une tranche de liste).
J'ai couru un benchmark et . Ces benchmarks ont été exécutés sur un MacBook Pro 2010 13" (Core2 Duo 2.66 GHz, 4GB 1067MHz DDR3 RAM, Mac OS X 10.6.5).sorted(lst, reverse=True) == lst
était le plus rapide pour les listes longues, et all(l[i] >= l[i+1] for i in xrange(len(l)-1))
était le plus rapide pour les listes courtes
UPDATE: j'ai révisé le script afin que vous puissiez l'exécuter directement sur votre propre système. La version précédente avait des insectes. En outre, j'ai ajouté des entrées triées et non triées.
- meilleur pour les listes triées courtes:
all(l[i] >= l[i+1] for i in xrange(len(l)-1))
- meilleur pour les longues listes triées:
sorted(l, reverse=True) == l
- idéal pour les listes courtes non triées:
all(l[i] >= l[i+1] for i in xrange(len(l)-1))
- idéal pour les listes longues non triées:
all(l[i] >= l[i+1] for i in xrange(len(l)-1))
Donc, dans la plupart des cas, il y a un gagnant clair.
Mise à jour: les réponses d'aaronsterling (#6 et #7) sont en fait les plus rapides dans tous les cas. #7 est le plus rapide car il n'a pas de couche d'indirection pour rechercher la clé.
#!/usr/bin/env python
import itertools
import time
def benchmark(f, *args):
t1 = time.time()
for i in xrange(1000000):
f(*args)
t2 = time.time()
return t2-t1
L1 = range(4, 0, -1)
L2 = range(100, 0, -1)
L3 = range(0, 4)
L4 = range(0, 100)
# 1.
def isNonIncreasing(l, key=lambda x,y: x >= y):
return all(key(l[i],l[i+1]) for i in xrange(len(l)-1))
print benchmark(isNonIncreasing, L1) # 2.47253704071
print benchmark(isNonIncreasing, L2) # 34.5398209095
print benchmark(isNonIncreasing, L3) # 2.1916718483
print benchmark(isNonIncreasing, L4) # 2.19576501846
# 2.
def isNonIncreasing(l):
return all(l[i] >= l[i+1] for i in xrange(len(l)-1))
print benchmark(isNonIncreasing, L1) # 1.86919999123
print benchmark(isNonIncreasing, L2) # 21.8603689671
print benchmark(isNonIncreasing, L3) # 1.95684289932
print benchmark(isNonIncreasing, L4) # 1.95272517204
# 3.
def isNonIncreasing(l, key=lambda x,y: x >= y):
return all(key(a,b) for (a,b) in itertools.izip(l[:-1],l[1:]))
print benchmark(isNonIncreasing, L1) # 2.65468883514
print benchmark(isNonIncreasing, L2) # 29.7504849434
print benchmark(isNonIncreasing, L3) # 2.78062295914
print benchmark(isNonIncreasing, L4) # 3.73436689377
# 4.
def isNonIncreasing(l):
return all(a >= b for (a,b) in itertools.izip(l[:-1],l[1:]))
print benchmark(isNonIncreasing, L1) # 2.06947803497
print benchmark(isNonIncreasing, L2) # 15.6351969242
print benchmark(isNonIncreasing, L3) # 2.45671010017
print benchmark(isNonIncreasing, L4) # 3.48461818695
# 5.
def isNonIncreasing(l):
return sorted(l, reverse=True) == l
print benchmark(isNonIncreasing, L1) # 2.01579380035
print benchmark(isNonIncreasing, L2) # 5.44593787193
print benchmark(isNonIncreasing, L3) # 2.01813793182
print benchmark(isNonIncreasing, L4) # 4.97615599632
# 6.
def isNonIncreasing(l, key=lambda x, y: x >= y):
for i, el in enumerate(l[1:]):
if key(el, l[i-1]):
return False
return True
print benchmark(isNonIncreasing, L1) # 1.06842684746
print benchmark(isNonIncreasing, L2) # 1.67291283607
print benchmark(isNonIncreasing, L3) # 1.39491200447
print benchmark(isNonIncreasing, L4) # 1.80557894707
# 7.
def isNonIncreasing(l):
for i, el in enumerate(l[1:]):
if el >= l[i-1]:
return False
return True
print benchmark(isNonIncreasing, L1) # 0.883186101913
print benchmark(isNonIncreasing, L2) # 1.42852401733
print benchmark(isNonIncreasing, L3) # 1.09229516983
print benchmark(isNonIncreasing, L4) # 1.59502696991
Bien que je ne pense pas qu'il y ait une garantie pour que le sorted
intégré appelle sa fonction cmp avec i+1, i
, il semble le faire pour CPython.
Donc vous pouvez faire quelque chose comme:
def my_cmp(x, y):
cmpval = cmp(x, y)
if cmpval < 0:
raise ValueError
return cmpval
def is_sorted(lst):
try:
sorted(lst, cmp=my_cmp)
return True
except ValueError:
return False
print is_sorted([1,2,3,5,6,7])
print is_sorted([1,2,5,3,6,7])
Ou de cette façon (sans instructions if - > EAFP qui a mal tourné? ;-) ):
def my_cmp(x, y):
assert(x >= y)
return -1
def is_sorted(lst):
try:
sorted(lst, cmp=my_cmp)
return True
except AssertionError:
return False
Pas très pythonique du tout, mais nous avons besoin d'au moins une réponse reduce()
, non?
def is_sorted(iterable):
prev_or_inf = lambda prev, i: i if prev <= i else float('inf')
return reduce(prev_or_inf, iterable, float('-inf')) < float('inf')
La variable accumulator stocke simplement cette dernière valeur vérifiée, et si une valeur est plus petite que la valeur précédente, l'accumulateur est défini sur infinity (et sera donc toujours infini à la fin, puisque la 'valeur précédente' sera toujours plus grande que la valeur actuelle).
J'utilise ce one-liner basé sur numpy.diff ():
def issorted(x):
"""Check if x is sorted"""
return (numpy.diff(x) >= 0).all() # is diff between all consecutive entries >= 0?
Je ne l'ai pas vraiment chronométré contre une autre méthode, mais je suppose que c'est plus rapide que toute méthode Python pure, en particulier pour les gros n, Depuis la boucle dans numpy.diff (probablement) s'exécute directement en C (soustractions n-1 suivies de comparaisons n-1).
Cependant, vous devez faire attention si x est un int non signé, ce qui peut provoquer un sous-flux entier silencieux dans numpy.diff (), résultant en un faux positif. Voici une modification version:
def issorted(x):
"""Check if x is sorted"""
try:
if x.dtype.kind == 'u':
# x is unsigned int array, risk of int underflow in np.diff
x = numpy.int64(x)
except AttributeError:
pass # no dtype, not an array
return (numpy.diff(x) >= 0).all()
Ceci est similaire à la réponse supérieure, mais je l'aime mieux car cela évite l'indexation explicite. En supposant que votre liste a le nom lst
, vous pouvez générer(item, next_item)
tuples de votre liste avec zip
:
all(x <= y for x,y in zip(lst, lst[1:]))
En Python 3, zip
renvoie déjà un générateur, en Python 2 Vous pouvez utiliser itertools.izip
pour une meilleure efficacité de la mémoire.
Petite démo:
>>> lst = [1, 2, 3, 4]
>>> zip(lst, lst[1:])
[(1, 2), (2, 3), (3, 4)]
>>> all(x <= y for x,y in zip(lst, lst[1:]))
True
>>>
>>> lst = [1, 2, 3, 2]
>>> zip(lst, lst[1:])
[(1, 2), (2, 3), (3, 2)]
>>> all(x <= y for x,y in zip(lst, lst[1:]))
False
Le dernier échoue lorsque le tuple (3, 2)
est évaluée.
Bonus: vérification finie (!) générateurs qui ne peut pas être indexé:
>>> def gen1():
... yield 1
... yield 2
... yield 3
... yield 4
...
>>> def gen2():
... yield 1
... yield 2
... yield 4
... yield 3
...
>>> g1_1 = gen1()
>>> g1_2 = gen1()
>>> next(g1_2)
1
>>> all(x <= y for x,y in zip(g1_1, g1_2))
True
>>>
>>> g2_1 = gen2()
>>> g2_2 = gen2()
>>> next(g2_2)
1
>>> all(x <= y for x,y in zip(g2_1, g2_2))
False
Assurez-vous d'utiliser itertools.izip
ici si vous utilisez Python 2, sinon vous vaincriez le but de ne pas avoir à créer des listes à partir des générateurs.
Saphiresun a tout à fait raison. Vous pouvez simplement utiliser lst.sort()
. L'implémentation de tri de Python (TimSort) vérifie si la liste est déjà triée. Si c'est le cas, sort () sera terminé en temps linéaire. Cela ressemble à un moyen pythonique de s'assurer qu'une liste est triée ;)
Comme noté par @ aaronsterling, la solution suivante est la plus courte et semble la plus rapide lorsque le tableau est trié et pas trop petit: def is_sorted(lst): retour (trié (lst) = = lst)
Si la plupart du temps le tableau n'est pas trié, il serait souhaitable d'utiliser une solution qui n'analyse pas tout le tableau et renvoie False dès qu'un préfixe non trié est découvert. Voici la solution la plus rapide que j'ai pu trouver, ce n'est pas particulièrement élégant:
def is_sorted(lst):
it = iter(lst)
try:
prev = it.next()
except StopIteration:
return True
for x in it:
if prev > x:
return False
prev = x
return True
Utilisation Le benchmark de Nathan Farrington, cela permet d'obtenir un meilleur temps d'exécution que d'utiliser tried(LST) dans tous les cas, sauf lors de l'exécution sur la grande liste triée.
Voici les résultats de référence sur mon ordinateur.
Trié (lst)==LST solution
- L1: 1.23838591576
- L2: 4.19063091278
- L3: 1.17996287346
- L4: 4.68399500847
Deuxième solution:
- L1: 0.81095790863
- L2: 0.802397012711
- L3: 1.06135106087
- L4: 8.82761001587
Si vous voulez le moyen le plus rapide pour les tableaux numpy, utiliser numba, qui, si vous utilisez conda doit être déjà installé
Le code sera rapide, car il sera compilé par numba
import numba
@numba.jit
def issorted(vec, ascending=True):
if len(vec) < 2:
return True
if ascending:
for i in range(1, len(vec)):
if vec[i-1] > vec[i]:
return False
return True
else:
for i in range(1, len(vec)):
if vec[i-1] < vec[i]:
return False
return True
Puis:
>>> issorted(array([4,9,100]))
>>> True
Juste pour ajouter une autre façon (même si elle nécessite un module supplémentaire): iteration_utilities.all_monotone
:
>>> from iteration_utilities import all_monotone
>>> listtimestamps = [1, 2, 3, 5, 6, 7]
>>> all_monotone(listtimestamps)
True
>>> all_monotone([1,2,1])
False
Pour vérifier l'ordre DESC:
>>> all_monotone(listtimestamps, decreasing=True)
False
>>> all_monotone([3,2,1], decreasing=True)
True
Il y a aussi un paramètre strict
Si vous devez vérifier les séquences strictement monotones (si les éléments successifs ne doivent pas être égaux).
Ce n'est pas un problème dans votre cas mais si vos séquences contiennent des valeurs nan
alors certaines méthodes échoueront, par exemple avec tried:
def is_sorted_using_sorted(iterable):
return sorted(iterable) == iterable
>>> is_sorted_using_sorted([3, float('nan'), 1]) # definetly False, right?
True
>>> all_monotone([3, float('nan'), 1])
False
Notez que iteration_utilities.all_monotone
effectue plus rapide par rapport aux autres solutions mentionnées ici en particulier pour les entrées non triées (voir benchmark).
Paresseux
from itertools import tee
def is_sorted(l):
l1, l2 = tee(l)
next(l2, None)
return all(a <= b for a, b in zip(l1, l2))
C'est en fait le moyen le plus rapide de le faire en utilisant la récursivité:
S'il est trié imprimera True sinon imprimera False
def is_Sorted(lst):
if len(lst) == 1:
return True
return lst[0] <= lst[1] and is_Sorted(lst[1:])
any_list = [1,2,3,4]
print is_Sorted(any_list)
Moyen le plus simple:
def isSorted(arr):
i = 1
while i < len(arr):
if(result[i] < result[i - 1]):
return False
i += 1
return True
Fonctionne définitivement en Python 3 et au-dessus pour les entiers ou les chaînes:
def tail(t):
return t[:]
letters = ['a', 'b', 'c', 'd', 'e']
rest = tail(letters)
rest.sort()
if letters == rest:
print ('Given list is SORTED.')
else:
print ('List NOT Sorted.')
=====================================================================
Une autre façon de trouver si la liste donnée est triée ou non
trees1 = list ([1, 4, 5, 3, 2])
trees2 = list (trees1)
trees2.sort()
if trees1 == trees2:
print ('trees1 is SORTED')
else:
print ('Not sorted')
Et celui-ci ? Simple et directe.
def is_list_sorted(al):
llength =len(al)
for i in range (llength):
if (al[i-1] > al[i]):
print(al[i])
print(al[i+1])
print('Not sorted')
return -1
else :
print('sorted')
return true