Encontrar diferencias entre elementos de una lista

113

Dada una lista de números, ¿cómo se encuentran las diferencias entre cada ( i) -ésimo elemento y su ( i+1) -ésimo?

¿Es mejor usar una lambdaexpresión o quizás una lista de comprensión?

Por ejemplo:

Dada una lista t=[1,3,6,...], el objetivo es encontrar una lista v=[2,3,...]porque 3-1=2, 6-3=3, etc.

psihodelia
fuente

Respuestas:

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
fuente
14
En caso de que necesite diferencias absolutas, [abs(j-i) for i,j in zip(t, t[1:])]
Anil
En caso de que desee hacerlo más eficiente: list(itertools.starmap(operator.sub, zip(t[1:], t)))(después de importar itertoolsy operator).
blhsing
3
En realidad, simplemente list(map(operator.sub, t[1:], t[:-1]))servirá.
blhsing
¡Brillante! ¡Me encanta esta respuesta!
Chayim Friedman
104

Las otras respuestas son correctas, pero si está haciendo un trabajo numérico, es posible que desee considerar numpy. Usando numpy, la respuesta es:

v = numpy.diff(t)
Christian Alis
fuente
¡Muy útil! ¡Gracias! np.diff([2,4,9])sería[2,5]
TravelTrader
¿Sería esto más eficiente que la zipversión?
user760900
35

Si no desea utilizar numpyni zip, puede utilizar la siguiente solución:

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

Puede usar itertools.teey zippara generar el resultado de manera eficiente:

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

O usando en su itertools.islicelugar:

from itertools import islice

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

También puede evitar usar el itertoolsmódulo:

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

Todas estas soluciones funcionan en un espacio constante si no necesita almacenar todos los resultados y admitir iterables infinitos.


A continuación, se muestran algunos microevaluaciones de las soluciones:

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

Y las otras soluciones propuestas:

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

Tenga en cuenta que:

  • zip(L[1:], L)es equivalente a zip(L[1:], L[:-1])ya que zipya termina en la entrada más corta, sin embargo, evita una copia completa de L.
  • Acceder a los elementos individuales por índice es muy lento porque cada acceso al índice es una llamada a un método en Python
  • numpy.diffes lento porque primero tiene que convertir el archivo lista ndarray. Obviamente, si comienzas con un ndarrayserá mucho más rápido:

    In [22]: arr = np.array(L)
    
    In [23]: %timeit np.diff(arr)
    100 loops, best of 3: 3.02 ms per loop
Bakuriu
fuente
en la segunda solución, en islice(seq, 1, None)lugar de islice(seq, 1, len(seq))hacerlo funcionar con iterables infinitos
Braham Snyder
5

Usando el :=operador de morsa disponible en Python 3.8+:

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

Sugeriría usar

v = np.diff(t)

esto es simple y fácil de leer.

Pero si quieres vtener la misma longitud que tentonces

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

o

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

FYI: esto solo funcionará para listas.

para matrices numpy

v = np.diff(np.append(t[0], t))
Chaitanya Kesanapalli
fuente
buena respuesta, también puede usar la palabra clave anteponer para garantizar la misma longitud, consulte la respuesta a continuación, que creo que es un poco más ordenada
Adrian Tompkins
4

Un enfoque funcional:

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

Usando generador:

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

Usando índices:

>>> [a[i+1]-a[i] for i in xrange(len(a)-1)]
[2, 2, 2, 4, 2, 4, 4]
Ola Fosheim Grøstad
fuente
El método del operador es agradable y elegante
bcattle
3

Okay. Creo que encontré la solución adecuada:

v = [x[1]-x[0] for x in zip(t[1:],t[:-1])]
psihodelia
fuente
2
ya es bueno, pero creo que debería haber sido v = [x [0] -x [1] para x en zip (t [1:], t [: - 1])] para lista ordenada!
Amit Karnik
0

Solución con límites periódicos

A veces, con la integración numérica, querrá diferenciar una lista con condiciones de contorno periódicas (por lo que el primer elemento calcula la diferencia con el último. En este caso, la función numpy.roll es útil:

v-np.roll(v,1)

Soluciones con cero antepuesto

Otra solución numpy (solo para completar) es usar

numpy.ediff1d(v)

Esto funciona como numpy.diff, pero solo en un vector (aplana la matriz de entrada). Ofrece la posibilidad de anteponer o añadir números al vector resultante. Esto es útil cuando se manejan campos acumulados que a menudo es el caso de los flujos en las variables meteorológicas (por ejemplo, lluvia, calor latente, etc.), ya que desea una lista resultante de la misma longitud que la variable de entrada, con la primera entrada intacta.

Entonces escribirías

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

Por supuesto, también puede hacer esto con el comando np.diff, en este caso, aunque debe anteponer cero a la serie con la palabra clave anteponer:

np.diff(v,prepend=0.0) 

Todas las soluciones anteriores devuelven un vector que tiene la misma longitud que la entrada.

Adrian Tompkins
fuente
-1

Mi manera

>>>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
fuente
1
Usar enumeratees un desperdicio porque no estás usando value. Ver stackoverflow.com/a/16714453/832230
Acumenus