it-swarm.com.de

Python - Wie kann ich die Liste der Monotonie überprüfen?

Was wäre ein effizienter und pythonischer Weg, um die Monotonie der Liste zu überprüfen?
d.h. dass es monoton steigende oder fallende Werte hat?

Beispiele:

[0, 1, 2, 3, 3, 4]   # This is a monotonically increasing list
[4.3, 4.2, 4.2, -2]  # This is a monotonically decreasing list
[2, 3, 1]            # This is neither
53
Jonathan
def strictly_increasing(L):
    return all(x<y for x, y in Zip(L, L[1:]))

def strictly_decreasing(L):
    return all(x>y for x, y in Zip(L, L[1:]))

def non_increasing(L):
    return all(x>=y for x, y in Zip(L, L[1:]))

def non_decreasing(L):
    return all(x<=y for x, y in Zip(L, L[1:]))

def monotonic(L):
    return non_increasing(L) or non_decreasing(L)
120
6502

Wenn Sie große Zahlenlisten haben, empfiehlt es sich, Numpy zu verwenden, und wenn Sie:

import numpy as np

def monotonic(x):
    dx = np.diff(x)
    return np.all(dx <= 0) or np.all(dx >= 0)

sollte den Trick tun.

31
Autoplectic
import itertools
import operator

def monotone_increasing(lst):
    pairs = Zip(lst, lst[1:])
    return all(itertools.starmap(operator.le, pairs))

def monotone_decreasing(lst):
    pairs = Zip(lst, lst[1:])
    return all(itertools.starmap(operator.ge, pairs))

def monotone(lst):
    return monotone_increasing(lst) or monotone_decreasing(lst)

Dieser Ansatz hat O(N) in der Länge der Liste. 

24

@ 6502 hat den perfekten Code für Listen. Ich möchte nur eine allgemeine Version hinzufügen, die für alle Sequenzen geeignet ist:

def pairwise(seq):
    items = iter(seq)
    last = next(items)
    for item in items:
        yield last, item
        last = item

def strictly_increasing(L):
    return all(x<y for x, y in pairwise(L))

def strictly_decreasing(L):
    return all(x>y for x, y in pairwise(L))

def non_increasing(L):
    return all(x>=y for x, y in pairwise(L))

def non_decreasing(L):
    return all(x<=y for x, y in pairwise(L))
14
Jochen Ritzel
import operator, itertools

def is_monotone(lst):
    op = operator.le            # pick 'op' based upon trend between
    if not op(lst[0], lst[-1]): # first and last element in the 'lst'
        op = operator.ge
    return all(op(x,y) for x, y in itertools.izip(lst, lst[1:]))
4
akira

Hier ist eine funktionale Lösung, die reduce der Komplexität O(n) verwendet:

is_increasing = lambda L: reduce(lambda a,b: b if a < b else 9999 , L)!=9999

is_decreasing = lambda L: reduce(lambda a,b: b if a > b else -9999 , L)!=-9999

Ersetzen Sie 9999 durch die obere Grenze Ihrer Werte und -9999 durch die untere Grenze. Wenn Sie beispielsweise eine Liste mit Ziffern testen, können Sie 10 und -1 verwenden.


Ich habe seine Leistung gegen @ 6502's Antwort getestet und es ist schneller.

Case true: [1,2,3,4,5,6,7,8,9]

# my solution .. 
$ python -m timeit "inc = lambda L: reduce(lambda a,b: b if a < b else 9999 , L)!=9999; inc([1,2,3,4,5,6,7,8,9])"
1000000 loops, best of 3: 1.9 usec per loop

# while the other solution:
$ python -m timeit "inc = lambda L: all(x<y for x, y in Zip(L, L[1:]));inc([1,2,3,4,5,6,7,8,9])"
100000 loops, best of 3: 2.77 usec per loop

Case False vom 2. Element: [4,2,3,4,5,6,7,8,7]:

# my solution .. 
$ python -m timeit "inc = lambda L: reduce(lambda a,b: b if a < b else 9999 , L)!=9999; inc([4,2,3,4,5,6,7,8,7])"
1000000 loops, best of 3: 1.87 usec per loop

# while the other solution:
$ python -m timeit "inc = lambda L: all(x<y for x, y in Zip(L, L[1:]));inc([4,2,3,4,5,6,7,8,7])"
100000 loops, best of 3: 2.15 usec per loop
2
BigOther

Ich habe alle Antworten in dieser Frage unter verschiedenen Bedingungen zeitlich festgelegt und festgestellt, dass:

  • Die Sortierung war die schnellste, wenn die Liste bereits monoton anstieg
  • Die Sortierung war bei einer langen Einstellung am langsamsten, wenn die Liste zufällig gemischt wurde oder die Anzahl der Elemente außerhalb der Reihenfolge größer als ~ 1 war. Je mehr die Liste in der falschen Reihenfolge ist, entspricht natürlich einem langsameren Ergebnis.
  • Die Methode von Michael J. Barbers war die schnellste Methode, wenn die Liste meistens monoton anstieg oder völlig zufällig war.

Hier ist der Code, um es auszuprobieren:

import timeit

setup = '''
import random
from itertools import izip, starmap, islice
import operator

def is_increasing_normal(lst):
    for i in range(0, len(lst) - 1):
        if lst[i] >= lst[i + 1]:
            return False
    return True

def is_increasing_Zip(lst):
    return all(x < y for x, y in izip(lst, islice(lst, 1, None)))

def is_increasing_sorted(lst):
    return lst == sorted(lst)

def is_increasing_starmap(lst):
    pairs = izip(lst, islice(lst, 1, None))
    return all(starmap(operator.le, pairs))

if {list_method} in (1, 2):
    lst = list(range({n}))
if {list_method} == 2:
    for _ in range(int({n} * 0.0001)):
        lst.insert(random.randrange(0, len(lst)), -random.randrange(1,100))
if {list_method} == 3:
    lst = [int(1000*random.random()) for i in xrange({n})]
'''

n = 100000
iterations = 10000
list_method = 1

timeit.timeit('is_increasing_normal(lst)', setup=setup.format(n=n, list_method=list_method), number=iterations)

timeit.timeit('is_increasing_Zip(lst)', setup=setup.format(n=n, list_method=list_method), number=iterations)

timeit.timeit('is_increasing_sorted(lst)', setup=setup.format(n=n, list_method=list_method), number=iterations)

timeit.timeit('is_increasing_starmap(lst)', setup=setup.format(n=n, list_method=list_method), number=iterations)

Wenn die Liste bereits monoton anstieg (list_method == 1), lautete die schnellste bis langsamste:

  1. sortiert
  2. starmap
  3. normal
  4. Postleitzahl

Wenn die Liste meistens monoton anstieg (list_method == 2), lautete die schnellste bis langsamste:

  1. starmap
  2. Postleitzahl
  3. normal
  4. sortiert

(Ob die Starmap oder Zip am schnellsten war oder nicht, war von der Ausführung abhängig und ich konnte kein Muster identifizieren. Starmap schien normalerweise schneller zu sein.)

Wenn die Liste vollständig zufällig war (list_method == 3), war die schnellste bis langsamste:

  1. starmap
  2. Postleitzahl
  3. normal
  4. sortiert (extrem schlecht)
1
Matthew Moisen

Dies ist mit Pandas möglich, das Sie über pip install pandas installieren können.

import pandas as pd

Die folgenden Befehle funktionieren mit einer Liste von Ganzzahlen oder Floats.

Monoton steigend (≥):

pd.Series(mylist).is_monotonic_increasing

Streng monoton steigend (>):

myseries = pd.Series(mylist)
myseries.is_unique and myseries.is_monotonic_increasing

Alternative unter Verwendung einer nicht dokumentierten privaten Methode:

pd.Index(mylist)._is_strictly_monotonic_increasing

Monoton abnehmend (≤):

pd.Series(mylist).is_monotonic_decreasing

Streng monoton abnehmend (<):

myseries = pd.Series(mylist)
myseries.is_unique and myseries.is_monotonic_decreasing

Alternative unter Verwendung einer nicht dokumentierten privaten Methode:

pd.Index(mylist)._is_strictly_monotonic_decreasing
0
A-B-B
L = [1,2,3]
L == sorted(L)

L == sorted(L, reverse=True)
0
Asterisk