Bir listenin öğeleri arasındaki farkları bulmak


113

Sayıların bir listesi verildiğinde, her ( i)-inci elemanlar ile ( ) -ci elemanlar arasındaki farklar nasıl bulunur i+1?

Bir lambdaifade veya belki bir liste anlayışı kullanmak daha mı iyidir ?

Örneğin:

Bir liste Verilen t=[1,3,6,...], amaç bir liste bulmaktır v=[2,3,...], çünkü 3-1=2, 6-3=3vb

Yanıtlar:


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
Mutlak farklılıklara ihtiyacınız olursa, [abs(j-i) for i,j in zip(t, t[1:])]
Anıl

Daha verimli hale getirmek istemeniz durumunda: list(itertools.starmap(operator.sub, zip(t[1:], t)))( itertoolsve içe aktarıldıktan sonra operator).
blhsing

3
Aslında list(map(operator.sub, t[1:], t[:-1]))yapacak.
blhsing

Parlak! Bu cevabı çok seviyorum!
Chayim Friedman

104

Diğer cevaplar doğrudur, ancak sayısal bir çalışma yapıyorsanız, uyuşmayı düşünebilirsiniz. Numpy kullanarak cevap:

v = numpy.diff(t)

Çok yararlı! Teşekkürler! np.diff([2,4,9])olacak[2,5]
TravelTrader

Bu zipversiyondan daha verimli olur mu?
user760900

35

Kullanmak istemiyorsanız numpyne de zip, aşağıdaki çözümü kullanabilirsiniz:

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

12

Sonucu verimli bir şekilde oluşturmak için itertools.teeve kullanabilirsiniz zip:

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

Veya itertools.islicebunun yerine şunu kullanın:

from itertools import islice

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

Ayrıca itertoolsmodülü kullanmaktan da kaçınabilirsiniz :

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

Tüm sonuçları saklamanız ve sonsuz yinelemeleri desteklemeniz gerekmiyorsa, tüm bu çözümler sabit alanda çalışır.


Çözümlerin bazı mikro kriterleri şunlardır:

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

Ve önerilen diğer çözümler:

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

Bunu not et:

  • zip(L[1:], L)eşdeğerdir zip(L[1:], L[:-1])çünkü zipzaten en kısa girişi sonlandırır, ancak bir bütün kopyasını önler L.
  • Tek tek öğelere dizine göre erişim çok yavaştır çünkü her dizin erişimi python'da bir yöntem çağrısıdır
  • numpy.diffolduğunu yavaş ilk dönüştürmek zorunda olduğu için listbir karşı ndarray. Açıkçası , bir ile başlarsanız çok daha hızlı ndarrayolacaktır :

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

ikinci çözümde sonsuz yinelemelerle çalışmasını sağlamak islice(seq, 1, None)yerineislice(seq, 1, len(seq))
Braham Snyder

5

:=Python 3.8+ sürümünde bulunan mors operatörünü kullanma :

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

5

Kullanmayı öneririm

v = np.diff(t)

bu basit ve okuması kolaydır.

Ama o zamanki vuzunluğa sahip olmak istiyorsant

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

veya

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

Bilginize: Bu yalnızca listeler için işe yarar.

uyuşuk diziler için

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

güzel cevap, aynı uzunluğu sağlamak için başa eklenen anahtar kelimeyi de kullanabilirsiniz, bence biraz daha düzgün olduğunu düşündüğüm aşağıdaki cevaba bakın
Adrian Tompkins

4

İşlevsel bir yaklaşım:

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

Jeneratörü kullanma:

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

Endeksleri kullanma:

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

Operatör yöntemi güzel ve zarif
bcattle

3

Tamam. Sanırım doğru çözümü buldum:

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

2
ya bu iyi, ama sıralı liste için zip (t [1:], t [: - 1])] için v = [x [0] -x [1] olması gerektiğini düşünüyorum!
Amit Karnik

0

Periyodik sınırlarla çözüm

Bazen sayısal entegrasyonla, periyodik sınır koşulları olan bir listeyi birbirinden ayırmak isteyebilirsiniz (bu nedenle, ilk öğe, sondan farkı hesaplar. Bu durumda numpy.roll işlevi yararlıdır:

v-np.roll(v,1)

Başına sıfır eklenmiş çözümler

Başka bir uyuşuk çözüm (tamlık için)

numpy.ediff1d(v)

Bu, numpy.diff olarak çalışır, ancak yalnızca bir vektörde çalışır (giriş dizisini düzleştirir). Numaraları ortaya çıkan vektöre ekleme veya ekleme yeteneği sunar. Bu, genellikle meteorolojik değişkenlerdeki (örn. Yağmur, gizli ısı vb.) Akılar olan birikmiş alanları işlerken kullanışlıdır, çünkü ilk giriş dokunulmadan girdi değişkeni ile aynı uzunlukta bir sonuç listesi istediğinizde.

Sonra yazardın

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

Elbette bunu np.diff komutuyla da yapabilirsiniz, bu durumda başa eklenen anahtar sözcüğün bulunduğu dizinin başına sıfır eklemeniz gerekse de:

np.diff(v,prepend=0.0) 

Yukarıdaki çözümlerin tümü, girdi ile aynı uzunlukta bir vektör verir.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.