web-dev-qa-db-ja.com

リストがソートされているかどうかをチェックするPythonの方法

ASCまたはDESCでリストが既にソートされているかどうかをチェックするPython的な方法はありますか

listtimestamps = [1, 2, 3, 5, 6, 7]

TrueまたはFalseを返すisttimestamps.isSorted()のようなもの。

一部のメッセージのタイムスタンプのリストを入力し、トランザクションが正しい順序で表示されたかどうかを確認したい。

119
anijhaw

実際、私たちはanijhawが探している答えを与えていません。ライナーは次のとおりです。

all(l[i] <= l[i+1] for i in xrange(len(l)-1))

Python 3の場合:

all(l[i] <= l[i+1] for i in range(len(l)-1))
179
Wai Yip Tung

私はただ使うだろう

if sorted(lst) == lst:
    # code here

それが非常に大きなリストである場合を除き、カスタム関数を作成したい場合があります。

ソートされていない場合にソートする場合は、チェックを忘れてソートします。

lst.sort()

それについて考えすぎないでください。

カスタム関数が必要な場合は、次のようなことができます

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

これは、リストが既にソートされている場合はO(n)(およびforループ内のO(n))になります。ほとんどの場合、ソートされないように(そしてかなりランダムに)、リストをソートするだけです。

64
aaronasterling

このイテレータ形式は、整数インデックスを使用するよりも10〜15%高速です。

# 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:]))
36
PaulMcG

これを実装する美しい方法は、imapitertools関数を使用することです。

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))

この実装は高速で、あらゆるイテラブルで動作します。

18

ベンチマークを実行しました sorted(lst, reverse=True) == lstは長いリストで最速で、all(l[i] >= l[i+1] for i in xrange(len(l)-1))は短いリストで最速でした。これらのベンチマークは、MacBook Pro 2010 13 "(Core2 Duo 2.66GHz、4GB 1067MHz DDR3 RAM、Mac OS X 10.6.5)で実行されました。

PDATE:自分のシステムで直接実行できるようにスクリプトを修正しました。前のバージョンにはバグがありました。また、ソートされた入力とソートされていない入力の両方を追加しました。

  • 短いソートリストに最適: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))

そのため、ほとんどの場合、明確な勝者がいます。

PDATE: aaronsterlingの回答(#6および#7)は、実際にはすべての場合において最速です。 #7は、キーを検索するための間接層を持たないため、最速です。

#!/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
10

私はこれをします(ここの多くの回答から盗みます[アーロン・スターリング、ワイ・イップ・トゥン、ポール・マクガイアからのちょっとしたこと]そしてほとんど アーミン・ロナッチャー ):

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))

良いことの1つは、(リストスライスの場合とは異なり)シリーズの反復可能な2番目を実現する必要はありません。

9
hughdbrown

私はnumpy.diff()に基づいたこのワンライナーを使用します:

def issorted(x):
    """Check if x is sorted"""
    return (numpy.diff(x) >= 0).all() # is diff between all consecutive entries >= 0?

私は実際に他の方法とタイミングを合わせていませんが、numpy.diffのループが(おそらく)Cで直接実行されるため、特に大きなnの場合、純粋なPythonメソッドよりも高速であると思います(n -1減算とそれに続くn-1比較)。

ただし、xが符号なしintの場合は注意が必要です。これにより、numpy.diff()でサイレント整数アンダーフローが発生し、誤検知が発生する可能性があります。変更されたバージョンは次のとおりです。

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()
3
Martin Spacek

それほどPythonicではありませんが、少なくとも1つのreduce()答えが必要ですよね?

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')

アキュムレータ変数は単にその最後にチェックされた値を保存し、値が前の値よりも小さい場合、アキュムレータは無限に設定されます(したがって、「前の値」は常に現在のもの)。

3
orlade

sortedビルトインがi+1, iでcmp関数を呼び出すことを保証するものではないと思いますが、CPythonでそうするようです。

そのため、次のようなことができます。

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])

または、この方法(ifステートメントなし-> EAFPが間違っていますか?;-)):

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
3
Anthon

これはトップアンサーに似ていますが、明示的なインデックス付けを避けるため、私はそれがより好きです。リストの名前がlstであると仮定すると、生成できます
(item, next_item)リストのタプルとZip

all(x <= y for x,y in Zip(lst, lst[1:]))

Python 3では、Zipは既にジェネレーターを返します。Python 2では、itertools.izipを使用してメモリ効率を改善できます。

小さなデモ:

>>> 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

タプル(3, 2)が評価されると、最後のものは失敗します。

ボーナス:インデックスを作成できない有限(!)ジェネレーターをチェックする:

>>> 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

Python 2を使用している場合は、ここでitertools.izipを使用していることを確認してください。そうしないと、ジェネレーターからリストを作成する必要がないという目的に反します。

3
timgeb

SapphireSunはまったく正しい。 lst.sort()を使用できます。 Pythonのソート実装(TimSort)は、リストが既にソートされているかどうかを確認します。その場合、sort()は線形時間で完了します。リストを確実にソートするPython的な方法のように聞こえます;)

3
Wai Yip Tung

@aaronsterlingが述べているように、次の解決策は配列が並べ替えられており、小さすぎない場合に最も短く、最も速く見える:def is_sorted(lst):return(sorted(lst)== lst)

ほとんどの場合、配列が並べ替えられていない場合、並べ替えられていないプレフィックスが検出されるとすぐに配列全体をスキャンせずにFalseを返すソリューションを使用することが望ましいでしょう。以下は私が見つけることができる最速の解決策であり、特にエレガントではありません:

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

Nathan Farringtonのベンチマークを使用すると、大規模なソート済みリストで実行する場合を除き、すべての場合にsort(lst)を使用するよりも優れたランタイムを実現できます。

これが私のコンピューターでのベンチマーク結果です。

sorted(lst lst solution)==

  • L1:1.23838591576
  • L2:4.19063091278
  • L3:1.17996287346
  • L4:4.68399500847

2番目のソリューション:

  • L1:0.81095790863
  • L2:0.802397012711
  • L3:1.06135106087
  • L4:8.82761001587
2
Amit Moscovich

別の方法を追加するだけです(追加のモジュールが必要な場合でも): 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

DESCの注文を確認するには:

>>> all_monotone(listtimestamps, decreasing=True)
False

>>> all_monotone([3,2,1], decreasing=True)
True

厳密に(連続する要素が等しくない場合)単調シーケンスをチェックする必要がある場合は、strictパラメーターもあります。

あなたの場合は問題ありませんが、ifシーケンスにnanの値が含まれている場合、いくつかのメソッドは失敗します。たとえば、並べ替えの場合:

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

iteration_utilities.all_monotone は、特にソートされていない入力に関して、ここで説明した他のソリューションと比較して高速に実行されることに注意してください( benchmark を参照)。

2
MSeifert

Numpy配列の最速の方法が必要な場合は、 numba を使用します。condaを使用する場合は既にインストールされているはずです

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

その後:

>>> issorted(array([4,9,100]))
>>> True
2
tal

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

1
Chweng Mega

怠け者

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))
1
Sergey11g

最も簡単な方法:

def isSorted(arr):
  i = 1
  while i < len(arr):
    if(result[i] < result[i - 1]):
      return False
    i += 1
  return True
0
Aluis92
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]

導出された削減値は、(sortedSoFarFlagfirstTimeFlaglastElementValue)の3つの部分からなるタプルです。最初は(TrueTrueNone)で始まり、空のリストの結果としても使用されます(順不同の要素がないため、ソート済みと見なされます)。 。各要素を処理するときに、Tupleの新しい値を計算します(次のelementValueで以前のTuple値を使用):

[0] (sortedSoFarFlag) evaluates true if: prev_0 is true and (prev_1 is true or prev_2 <= elementValue)
[1] (firstTimeFlag): False
[2] (lastElementValue): elementValue

削減の最終結果は、次のタプルです。

[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

最初の値は関心のある値なので、[0]を使用して、reduce結果からそれを取得します。

0
Mr Weasel