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.

psihodelia
la source

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]
SilentGhost
la source
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)
Christian Alis
la source
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]
Omer Dagan
la source
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
    
Bakuriu
la source
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]
Eugène Yarmash
la source
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))
Chaitanya Kesanapalli
la source
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]
Ola Fosheim Grøstad
la source
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])]
psihodelia
la source
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.

Adrian Tompkins
la source
-1

Mon chemin

>>>v = [1,2,3,4,5]
>>>[v[i] - v[i-1] for i, value in enumerate(v[1:], 1)]
[1, 1, 1, 1]
Arindam Roychowdhury
la source
1
L'utilisation enumerateest un gaspillage parce que vous n'utilisez pas value. Voir stackoverflow.com/a/16714453/832230
Acumenus