Trouver des différences entre les éléments d'une liste


113

Étant donné une liste de nombres, comment trouver des différences entre chaque ( i) -ième élément et son ( i+1) -th?

Vaut-il mieux utiliser une lambdaexpression ou peut-être une compréhension de liste?

Par exemple:

Compte tenu de la liste t=[1,3,6,...], l'objectif est de trouver une liste v=[2,3,...]parce que 3-1=2, 6-3=3etc.

Réponses:


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
Au cas où vous auriez besoin de différences absolues, [abs(j-i) for i,j in zip(t, t[1:])]
Anil

Au cas où vous voudriez le rendre plus efficace: list(itertools.starmap(operator.sub, zip(t[1:], t)))(après l'importation itertoolset operator).
blhsing le

3
En fait list(map(operator.sub, t[1:], t[:-1])), cela fera tout simplement l'affaire.
blhsing

Brillant! J'adore cette réponse!
Chayim Friedman

104

Les autres réponses sont correctes, mais si vous faites du travail numérique, vous voudrez peut-être considérer numpy. En utilisant numpy, la réponse est:

v = numpy.diff(t)

Très utile! Merci! np.diff([2,4,9])serait[2,5]
TravelTrader

Serait-ce plus efficace que la zipversion?
user760900

35

Si vous ne souhaitez pas utiliser numpyni zip, vous pouvez utiliser la solution suivante:

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

12

Vous pouvez utiliser itertools.teeet zipcréer efficacement le résultat:

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

Ou en utilisant à la itertools.isliceplace:

from itertools import islice

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

Vous pouvez également éviter d'utiliser le itertoolsmodule:

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

Toutes ces solutions fonctionnent dans un espace constant si vous n'avez pas besoin de stocker tous les résultats et de prendre en charge des itérables infinis.


Voici quelques micro-benchmarks des solutions:

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

Et les autres solutions proposées:

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

Notez que:

  • zip(L[1:], L)est équivalent à zip(L[1:], L[:-1])puisque se ziptermine déjà sur l'entrée la plus courte, mais il évite une copie complète de L.
  • L'accès aux éléments uniques par index est très lent car chaque accès à l'index est un appel de méthode en python
  • numpy.diffest lent car il doit d'abord convertir le listen a ndarray. Évidemment, si vous commencez par un, ndarrayce sera beaucoup plus rapide:

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

dans la deuxième solution, islice(seq, 1, None)au lieu de le islice(seq, 1, len(seq))faire fonctionner avec des itérables infinis
Braham Snyder

5

Utilisation de l' :=opérateur morse disponible dans Python 3.8+:

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

5

Je suggérerais d'utiliser

v = np.diff(t)

c'est simple et facile à lire.

Mais si vous voulez vavoir la même longueur tqu'alors

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

ou

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

FYI: cela ne fonctionnera que pour les listes.

pour les tableaux numpy

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

belle réponse, vous pouvez également utiliser le mot-clé prefend pour assurer la même longueur, voir la réponse ci-dessous, ce qui, à mon avis, est un peu plus soigné
Adrian Tompkins

4

Une approche fonctionnelle:

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

Utilisation du générateur:

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

Utilisation d'indices:

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

La méthode de l'opérateur est agréable et élégante
bcattle

3

D'accord. Je pense avoir trouvé la bonne solution:

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

2
oui c'est bien, mais je pense que ça aurait dû être v = [x [0] -x [1] pour x dans zip (t [1:], t [: - 1])] pour une liste triée!
Amit Karnik le

0

Solution avec des limites périodiques

Parfois, avec l'intégration numérique, vous voudrez différencier une liste avec des conditions aux limites périodiques (le premier élément calcule la différence par rapport au dernier. Dans ce cas, la fonction numpy.roll est utile:

v-np.roll(v,1)

Solutions avec zéro ajouté

Une autre solution numpy (juste pour l'exhaustivité) consiste à utiliser

numpy.ediff1d(v)

Cela fonctionne comme numpy.diff, mais uniquement sur un vecteur (cela aplatit le tableau d'entrée). Il offre la possibilité d'ajouter ou d'ajouter des nombres au vecteur résultant. Ceci est utile lors de la gestion des champs accumulés qui sont souvent les flux de cas dans les variables météorologiques (par exemple la pluie, la chaleur latente, etc.), car vous voulez une liste résultante de la même longueur que la variable d'entrée, avec la première entrée intacte.

Alors tu écrirais

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

Bien sûr, vous pouvez également le faire avec la commande np.diff, dans ce cas, vous devez ajouter zéro à la série avec le mot clé prepend:

np.diff(v,prepend=0.0) 

Toutes les solutions ci-dessus renvoient un vecteur de même longueur que l'entrée.


En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.