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.
En fait, nous ne donnons pas la réponse recherchée par anijhaw. Voici la doublure:
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
à moins que ce ne soit une très grande liste, auquel cas vous pouvez créer une fonction personnalisée.
si vous voulez simplement le trier s'il n'est pas trié, oubliez la vérification et triez-le.
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 bien que (et O(n)) dans une boucle for
à ce moment-là!! ) donc, à moins que vous ne vous attendiez à ce que ce ne soit pas trié (et assez aléatoire) la plupart du temps, je trierais à nouveau la liste.
Cette forme d'itérateur est 10-15% plus rapide que l'indexation d'entiers:
# python2 only
if str is bytes:
from itertools import izip as Zip
def is_sorted(l):
return all(a <= b for a, b in Zip(l, l[1:]))
Une belle façon d'implémenter ceci 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 iterables.
J'ai couru un repère et . Ces tests ont été exécutés sur un MacBook Pro 2010 13 "(Core2 Duo à 2,66 GHz, 4 Go de RAM DDR3 à 1067 MHz, Mac OS X 10.6.5).sorted(lst, reverse=True) == lst
était le plus rapide pour les longues listes et all(l[i] >= l[i+1] for i in xrange(len(l)-1))
était le plus rapide pour les petites listes
PDATE: 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 bugs. De plus, j'ai ajouté des entrées triées et non triées.
all(l[i] >= l[i+1] for i in xrange(len(l)-1))
sorted(l, reverse=True) == l
all(l[i] >= l[i+1] for i in xrange(len(l)-1))
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.
PDATE: Les réponses de aaronsterling (n ° 6 et n ° 7) sont en réalité les plus rapides dans tous les cas. # 7 est le plus rapide car il ne dispose pas d'une 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
Je ferais ceci (en volant beaucoup de réponses ici [Aaron Sterling, Wai Yip Tung, en quelque sorte 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 belle chose: vous n'avez pas à réaliser le deuxième itérable de la série (contrairement à une tranche de liste).
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 comparé à une autre méthode, mais je suppose qu'elle est plus rapide que toute méthode pure Python, en particulier pour les grands n, car la boucle dans numpy.diff (probablement) s'exécute directement dans C (n-1 soustractions suivies de n-1 comparaisons).
Cependant, vous devez faire attention si x est un entier non signé, ce qui pourrait provoquer un dépassement inférieur entier en silence dans numpy.diff (), entraînant ainsi un faux positif. Voici une version modifiée:
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()
Pas très pythonique du tout, mais il nous faut au moins une réponse reduce()
, n'est-ce pas?
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 accumulateur stocke simplement la dernière valeur vérifiée, et si une valeur est inférieure à la valeur précédente, l'accumulateur est défini sur l'infini (et sera donc toujours l'infini à la fin, car la "valeur précédente" sera toujours supérieure à l'actuel).
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 que ce soit le cas pour CPython.
Pour que vous puissiez 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 si déclarations -> 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
Ceci est similaire à la première réponse, mais je l’aime mieux parce qu’il évite l’indexation explicite. En supposant que votre liste porte 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:]))
Dans Python 3, Zip
renvoie déjà un générateur, dans 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é.
Bonus: vérification des générateurs finis (!) Non indexables:
>>> 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 iriez à l'encontre de l'objectif de ne pas avoir à créer des listes à partir des générateurs.
SapphireSun 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 tel est le cas, sort () sera terminé en temps linéaire. Cela ressemble à une façon pythonique de s'assurer qu'une liste est triée;)
Comme l'a noté @aaronsterling, la solution suivante est la plus courte et semble la plus rapide lorsque le tableau est trié et pas trop petite: def is_sorted (lst): return (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, elle n'est pas particulièrement élégante:
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
En utilisant le point de repère de Nathan Farrington, cela permet une meilleure durée d'exécution que d'utiliser trié (lst) dans tous les cas, sauf lorsqu'il est exécuté sur la grande liste triée.
Voici les résultats de référence sur mon ordinateur.
trié (lst lst solution) ==
Deuxième solution:
Juste pour ajouter une autre manière (même si cela 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 de DESC:
>>> all_monotone(listtimestamps, decreasing=True)
False
>>> all_monotone([3,2,1], decreasing=True)
True
Il existe également un paramètre strict
si vous devez vérifier les séquences monotones strictement (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
, certaines méthodes échoueront, par exemple avec les éléments triés:
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
est plus rapide que les autres solutions mentionnées ici, en particulier pour les entrées non triées (voir repère ).
Si vous voulez le moyen le plus rapide pour les tableaux numpy, utilisez numba , qui, si vous utilisez conda, devrait déjà être 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
et alors:
>>> issorted(array([4,9,100]))
>>> True
Python 3.6.8
from more_itertools import pairwise
class AssertionHelper:
@classmethod
def is_ascending(cls, data: iter) -> bool:
for a, b in pairwise(data):
if a > b:
return False
return True
@classmethod
def is_descending(cls, data: iter) -> bool:
for a, b in pairwise(data):
if a < b:
return False
return True
@classmethod
def is_sorted(cls, data: iter) -> bool:
return cls.is_ascending(data) or cls.is_descending(data)
>>> AssertionHelper.is_descending((1, 2, 3, 4))
False
>>> AssertionHelper.is_ascending((1, 2, 3, 4))
True
>>> AssertionHelper.is_sorted((1, 2, 3, 4))
True
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))
Manière la plus simple:
def isSorted(arr):
i = 1
while i < len(arr):
if(result[i] < result[i - 1]):
return False
i += 1
return True
from functools import reduce
# myiterable can be of any iterable type (including list)
isSorted = reduce(lambda r, e: (r[0] and (r[1] or r[2] <= e), False, e), myiterable, (True, True, None))[0]
La valeur de réduction dérivée est un nuplet en 3 parties de (triéSoFarFlag, firstTimeFlag, lastElementValue). Cela commence par (True
, True
, None
), qui est également utilisé comme résultat pour une liste vide (considérée comme triée car il n'y a pas éléments de commande). Lors du traitement de chaque élément, il calcule de nouvelles valeurs pour le Tuple (en utilisant les valeurs précédentes de Tuple avec la valeur suivante elementValue):
[0] (sortedSoFarFlag) evaluates true if: prev_0 is true and (prev_1 is true or prev_2 <= elementValue)
[1] (firstTimeFlag): False
[2] (lastElementValue): elementValue
Le résultat final de la réduction est un tuple de:
[0]: True/False depending on whether the entire list was in sorted order
[1]: True/False depending on whether the list was empty
[2]: the last element value
La première valeur est celle qui nous intéresse, nous utilisons donc [0]
pour saisir cela du résultat réduit.