Menemukan perbedaan antara elemen daftar

113

Diberikan daftar angka, bagaimana cara menemukan perbedaan antara setiap ielemen ( ) -th dan ( i+1) -th?

Apakah lebih baik menggunakan lambdaekspresi atau mungkin pemahaman daftar?

Sebagai contoh:

Mengingat daftar t=[1,3,6,...], tujuannya adalah untuk menemukan daftar v=[2,3,...]karena 3-1=2, 6-3=3, dll

psihodelia.dll
sumber

Jawaban:

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
sumber
14
Jika Anda membutuhkan perbedaan mutlak, [abs(j-i) for i,j in zip(t, t[1:])]
Anil
Jika Anda ingin membuatnya lebih efisien: list(itertools.starmap(operator.sub, zip(t[1:], t)))(setelah mengimpor itertoolsdan operator).
blhsing
3
Sebenarnya cukup list(map(operator.sub, t[1:], t[:-1])).
blhsing
Cemerlang! Saya sangat menyukai jawaban ini!
Chayim Friedman
104

Jawaban lain benar tetapi jika Anda melakukan pekerjaan numerik, Anda mungkin ingin mempertimbangkan numpy. Menggunakan numpy, jawabannya adalah:

v = numpy.diff(t)
Christian Alis
sumber
Sangat membantu! Terima kasih! np.diff([2,4,9])akan[2,5]
TravelTrader
Apakah ini lebih efisien daripada zipversinya?
pengguna760900
35

Jika Anda tidak ingin menggunakan numpyatau zip, Anda dapat menggunakan solusi berikut:

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

Anda dapat menggunakan itertools.teedan zipmembuat hasilnya secara efisien:

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

Atau gunakan itertools.islicesebagai gantinya:

from itertools import islice

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

Anda juga dapat menghindari penggunaan itertoolsmodul:

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

Semua solusi ini bekerja dalam ruang konstan jika Anda tidak perlu menyimpan semua hasil dan mendukung iterable tak terbatas.


Berikut beberapa tolok ukur mikro dari solusi tersebut:

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

Dan solusi lain yang diusulkan:

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

Perhatikan bahwa:

  • zip(L[1:], L)setara dengan zip(L[1:], L[:-1])karena zipsudah berakhir pada input terpendek, namun ia menghindari seluruh salinan L.
  • Mengakses elemen tunggal dengan indeks sangat lambat karena setiap akses indeks adalah panggilan metode dalam python
  • numpy.diffadalah lambat karena harus pertama mengkonversi listke ndarray. Jelas jika Anda mulai dengan ndarrayitu akan jauh lebih cepat:

    In [22]: arr = np.array(L)
    
    In [23]: %timeit np.diff(arr)
    100 loops, best of 3: 3.02 ms per loop
Bakuriu
sumber
dalam solusi kedua, islice(seq, 1, None)alih-alih islice(seq, 1, len(seq))membuatnya berfungsi dengan iterabel tak terbatas
Braham Snyder
5

Menggunakan :=operator walrus yang tersedia di Python 3.8+:

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

Saya akan menyarankan menggunakan

v = np.diff(t)

ini sederhana dan mudah dibaca.

Tetapi jika Anda ingin vmemiliki panjang yang sama seperti titu

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

atau

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

FYI: ini hanya akan berfungsi untuk daftar.

untuk array numpy

v = np.diff(np.append(t[0], t))
Chaitanya Kesanapalli
sumber
jawaban yang bagus, Anda juga dapat menggunakan kata kunci prepend untuk memastikan panjang yang sama, lihat jawaban di bawah, yang menurut saya sedikit lebih rapi
Adrian Tompkins
4

Pendekatan fungsional:

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

Menggunakan generator:

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

Menggunakan indeks:

>>> [a[i+1]-a[i] for i in xrange(len(a)-1)]
[2, 2, 2, 4, 2, 4, 4]
Ola Fosheim Grøstad
sumber
Metode operatornya bagus dan elegan
bcattle
3

Baik. Saya rasa saya menemukan solusi yang tepat:

v = [x[1]-x[0] for x in zip(t[1:],t[:-1])]
psihodelia.dll
sumber
2
ya bagus, tapi saya pikir seharusnya v = [x [0] -x [1] untuk x dalam zip (t [1:], t [: - 1])] untuk daftar yang diurutkan!
Amit Karnik
0

Solusi dengan batasan berkala

Terkadang dengan integrasi numerik Anda ingin membedakan daftar dengan kondisi batas periodik (sehingga elemen pertama menghitung perbedaan dengan yang terakhir. Dalam hal ini fungsi numpy.roll berguna:

v-np.roll(v,1)

Solusi dengan nol di awal

Lain numpy solusi (hanya untuk kelengkapan) adalah untuk digunakan

numpy.ediff1d(v)

Ini berfungsi sebagai numpy.diff, tetapi hanya pada vektor (itu meratakan larik input). Ini menawarkan kemampuan untuk menambahkan atau menambahkan angka ke vektor yang dihasilkan. Ini berguna saat menangani bidang terakumulasi yang sering terjadi fluks dalam variabel meteorologi (misalnya hujan, panas laten, dll.), Karena Anda menginginkan daftar yang dihasilkan dengan panjang yang sama dengan variabel masukan, dengan entri pertama tidak tersentuh.

Kemudian Anda akan menulis

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

Tentu saja, Anda juga dapat melakukan ini dengan perintah np.diff, dalam hal ini meskipun Anda perlu menambahkan nol ke seri dengan kata kunci awal:

np.diff(v,prepend=0.0) 

Semua solusi di atas mengembalikan vektor dengan panjang yang sama dengan input.

Adrian Tompkins
sumber
-1

Jalanku

>>>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
sumber
1
Menggunakan enumerateitu boros karena Anda tidak menggunakannya value. Lihat stackoverflow.com/a/16714453/832230
Acumenus