Finden von Unterschieden zwischen Elementen einer Liste


113

Wie findet man bei einer Liste von Zahlen Unterschiede zwischen jedem ( i) -ten Element und seinem ( i+1) -ten?

Ist es besser, einen lambdaAusdruck oder vielleicht ein Listenverständnis zu verwenden?

Beispielsweise:

In einer vorgegebenen Liste t=[1,3,6,...]ist das Ziel , eine Liste zu finden , v=[2,3,...]weil 3-1=2, 6-3=3usw.

Antworten:


154
>>> t
[1, 3, 6]
>>> [j-i for i, j in zip(t[:-1], t[1:])]  # or use itertools.izip in py2k
[2, 3]

14
Für den Fall, dass Sie absolute Unterschiede benötigen, [abs(j-i) for i,j in zip(t, t[1:])]
Anil

Für den Fall, dass Sie es effizienter machen möchten: list(itertools.starmap(operator.sub, zip(t[1:], t)))(nach dem Importieren itertoolsund operator).
Blhsing

3
Eigentlich einfach reicht list(map(operator.sub, t[1:], t[:-1])).
Blhsing

Brillant! Ich liebe diese Antwort sehr!
Chayim Friedman

104

Die anderen Antworten sind richtig, aber wenn Sie numerische Arbeiten ausführen, sollten Sie Numpy in Betracht ziehen. Mit numpy lautet die Antwort:

v = numpy.diff(t)

Sehr hilfreich! Vielen Dank! np.diff([2,4,9])wäre[2,5]
TravelTrader

Wäre dies effizienter als die zipVersion?
user760900

35

Wenn Sie möchten , verwenden Sie nicht numpynoch zip, können Sie die folgende Lösung verwenden:

>>> t = [1, 3, 6]
>>> v = [t[i+1]-t[i] for i in range(len(t)-1)]
>>> v
[2, 3]

12

Sie können das Ergebnis verwenden itertools.teeund zipeffizient erstellen:

from itertools import tee
# python2 only:
#from itertools import izip as zip

def differences(seq):
    iterable, copied = tee(seq)
    next(copied)
    for x, y in zip(iterable, copied):
        yield y - x

Oder verwenden Sie itertools.islicestattdessen:

from itertools import islice

def differences(seq):
    nexts = islice(seq, 1, None)
    for x, y in zip(seq, nexts):
        yield y - x

Sie können die Verwendung des itertoolsModuls auch vermeiden :

def differences(seq):
    iterable = iter(seq)
    prev = next(iterable)
    for element in iterable:
        yield element - prev
        prev = element

Alle diese Lösungen funktionieren auf konstantem Raum, wenn Sie nicht alle Ergebnisse speichern und unendlich viele Iterables unterstützen müssen.


Hier sind einige Mikro-Benchmarks der Lösungen:

In [12]: L = range(10**6)

In [13]: from collections import deque
In [15]: %timeit deque(differences_tee(L), maxlen=0)
10 loops, best of 3: 122 ms per loop

In [16]: %timeit deque(differences_islice(L), maxlen=0)
10 loops, best of 3: 127 ms per loop

In [17]: %timeit deque(differences_no_it(L), maxlen=0)
10 loops, best of 3: 89.9 ms per loop

Und die anderen vorgeschlagenen Lösungen:

In [18]: %timeit [x[1] - x[0] for x in zip(L[1:], L)]
10 loops, best of 3: 163 ms per loop

In [19]: %timeit [L[i+1]-L[i] for i in range(len(L)-1)]
1 loops, best of 3: 395 ms per loop

In [20]: import numpy as np

In [21]: %timeit np.diff(L)
1 loops, best of 3: 479 ms per loop

In [35]: %%timeit
    ...: res = []
    ...: for i in range(len(L) - 1):
    ...:     res.append(L[i+1] - L[i])
    ...: 
1 loops, best of 3: 234 ms per loop

Beachten Sie, dass:

  • zip(L[1:], L)ist gleichbedeutend mit, zip(L[1:], L[:-1])da zipbereits bei der kürzesten Eingabe beendet wird, jedoch wird eine ganze Kopie von vermieden L.
  • Der Zugriff auf die einzelnen Elemente über den Index ist sehr langsam, da jeder Indexzugriff ein Methodenaufruf in Python ist
  • numpy.diffist langsam, weil es zuerst das listin a konvertieren muss ndarray. Natürlich , wenn Sie beginnen mit einem ndarraywird es viel schneller:

    In [22]: arr = np.array(L)
    
    In [23]: %timeit np.diff(arr)
    100 loops, best of 3: 3.02 ms per loop

in der zweiten Lösung, islice(seq, 1, None)anstatt islice(seq, 1, len(seq))es mit unendlichen Iterablen arbeiten zu lassen
Braham Snyder

5

Verwenden des :=in Python 3.8+ verfügbaren Walross-Operators:

>>> t = [1, 3, 6]
>>> prev = t[0]; [-prev + (prev := x) for x in t[1:]]
[2, 3]

5

Ich würde vorschlagen, zu verwenden

v = np.diff(t)

Das ist einfach und leicht zu lesen.

Aber wenn Sie vdie gleiche Länge wie tdamals haben wollen

v = np.diff([t[0]] + t) # for python 3.x

oder

v = np.diff(t + [t[-1]])

Zu Ihrer Information: Dies funktioniert nur für Listen.

für numpy Arrays

v = np.diff(np.append(t[0], t))

Gute Antwort, Sie können aber auch das Schlüsselwort prepend verwenden, um die gleiche Länge zu gewährleisten. Siehe Antwort unten, die ich für etwas ordentlicher halte
Adrian Tompkins

4

Ein funktionaler Ansatz:

>>> import operator
>>> a = [1,3,5,7,11,13,17,21]
>>> map(operator.sub, a[1:], a[:-1])
[2, 2, 2, 4, 2, 4, 4]

Generator verwenden:

>>> import operator, itertools
>>> g1,g2 = itertools.tee((x*x for x in xrange(5)),2)
>>> list(itertools.imap(operator.sub, itertools.islice(g1,1,None), g2))
[1, 3, 5, 7]

Indizes verwenden:

>>> [a[i+1]-a[i] for i in xrange(len(a)-1)]
[2, 2, 2, 4, 2, 4, 4]

Die Bedienermethode ist nett und elegant
bcattle

3

OK. Ich denke, ich habe die richtige Lösung gefunden:

v = [x[1]-x[0] for x in zip(t[1:],t[:-1])]

2
ya es ist gut, aber ich denke, es hätte v = [x [0] -x [1] für x in zip (t [1:], t [: - 1])] für sortierte Liste sein sollen!
Amit Karnik

0

Lösung mit periodischen Grenzen

Manchmal möchten Sie bei der numerischen Integration eine Liste mit periodischen Randbedingungen differenzieren (daher berechnet das erste Element die Differenz zum letzten. In diesem Fall ist die Funktion numpy.roll hilfreich:

v-np.roll(v,1)

Lösungen ohne Null

Eine andere numpy Lösung (nur der Vollständigkeit halber ) ist zu verwenden

numpy.ediff1d(v)

Dies funktioniert als numpy.diff, jedoch nur für einen Vektor (es glättet das Eingabearray). Es bietet die Möglichkeit, dem resultierenden Vektor Zahlen voranzustellen oder anzuhängen. Dies ist nützlich, wenn akkumulierte Felder behandelt werden, wie dies häufig bei meteorologischen Variablen (z. B. Regen, latente Wärme usw.) der Fall ist, da Sie eine resultierende Liste mit der gleichen Länge wie die Eingabevariable erhalten möchten, wobei der erste Eintrag unberührt bleibt.

Dann würdest du schreiben

np.ediff1d(v,to_begin=v[0])

Natürlich können Sie dies auch mit dem Befehl np.diff tun. In diesem Fall müssen Sie der Reihe jedoch Null mit dem Schlüsselwort prepend voranstellen:

np.diff(v,prepend=0.0) 

Alle obigen Lösungen geben einen Vektor zurück, der dieselbe Länge wie die Eingabe hat.


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.