Saya perlu jendela bergulir (alias jendela geser) iterable atas urutan / iterator / generator. Iterasi Python default dapat dianggap sebagai kasus khusus, dengan panjang jendela 1. Saat ini saya menggunakan kode berikut. Adakah yang punya metode yang lebih Pythonic, kurang verbose, atau lebih efisien untuk melakukan ini?
def rolling_window(seq, window_size):
it = iter(seq)
win = [it.next() for cnt in xrange(window_size)] # First window
yield win
for e in it: # Subsequent windows
win[:-1] = win[1:]
win[-1] = e
yield win
if __name__=="__main__":
for w in rolling_window(xrange(6), 3):
print w
"""Example output:
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
"""
sum()
ataumax()
) perlu diingat bahwa ada algoritma yang efisien untuk menghitung nilai baru untuk setiap jendela dalam waktu yang konstan (terlepas dari ukuran jendela). Saya telah mengumpulkan beberapa algoritma ini bersama-sama di perpustakaan Python: bergulir .Jawaban:
Ada satu dalam versi lama dokumen Python dengan
itertools
contoh :Yang dari dokumen sedikit lebih ringkas dan digunakan
itertools
untuk efek yang lebih besar saya bayangkan.sumber
for elem in it
loop?Ini tampaknya dibuat khusus untuk
collections.deque
karena Anda pada dasarnya memiliki FIFO (tambahkan ke satu ujung, hapus dari yang lain). Namun, bahkan jika Anda menggunakan,list
Anda tidak harus mengiris dua kali; sebagai gantinya, Anda mungkin harus hanyapop(0)
dari daftar danappend()
item baru.Berikut ini adalah implementasi berbasis deque yang dioptimalkan sesuai dengan aslinya:
Dalam tes saya itu dengan mudah mengalahkan segala sesuatu yang diposting di sini sebagian besar waktu, meskipun
tee
versi pembuat pil mengalahkan itu untuk iterables besar dan jendela kecil. Pada jendela yang lebih besar,deque
tarikan maju lagi dalam kecepatan mentah.Akses ke masing-masing item dalam
deque
mungkin lebih cepat atau lebih lambat daripada dengan daftar atau tupel. (Item di dekat awal lebih cepat, atau item di dekat akhir jika Anda menggunakan indeks negatif.) Saya menaruh asum(w)
di badan loop saya; ini memainkan kekuatan deque (pergantian dari satu item ke item berikutnya cepat, jadi loop ini berjalan 20% lebih cepat dari metode tercepat berikutnya, pembuat pil). Ketika saya mengubahnya secara individual mencari dan menambahkan item dalam jendela sepuluh, tabel berbalik dantee
metode itu 20% lebih cepat. Saya dapat memulihkan beberapa kecepatan dengan menggunakan indeks negatif untuk lima istilah terakhir di samping, tetapitee
masih sedikit lebih cepat. Secara keseluruhan saya akan memperkirakan bahwa salah satu dari mereka banyak cepat untuk sebagian besar kegunaan dan jika Anda memerlukan sedikit lebih banyak kinerja, profil dan pilih salah satu yang terbaik.sumber
yield win
harusyield tuple(win)
atauyield list(win)
untuk mencegah pengembalian iterator referensi kedeque
objek yang sama .pip install sliding_window
, dan jalankan denganfrom sliding_window import window
.list(window(range(10)))
harus menghasilkan sesuatu seperti [[0,1], [1,2], [2,3], ...]list(list(x) for x in window(range(10)))
atau menambahkannya ke iterator. Untuk beberapa aplikasi ini tidak masalah, untuk yang lain tidak, dan karena saya akan mempercepat, saya memilih tidak dan meletakkan tanggung jawab pada penelepon untuk menyalin jendela jika diperlukan.tuple()
sebelum hasil, metode ini tidak memiliki keunggulan dibandingkan yang lain.Saya suka
tee()
:memberi:
sumber
timeit
tes cepat saya , ini jauh lebih lambat daripada Daniel DePaolo (sekitar 2: 1 rasio) dan tidak merasa jauh lebih "baik".size
. Jika Anda meningkatkannya (misalnya, jika iterable adalah 100000 elemen, buat ukuran jendela 1000), Anda mungkin melihat peningkatan.iters
adalah O (size!), Dan meneleponnext()
berkali-kali (inizip()
) mungkin jauh lebih memakan waktu daripada menyalin tuple dua kali. Saya menggunakan Python 2.6.5, BTW.iters
adalah O (ukuran ^ 2), kan?Inilah generalisasi yang menambahkan dukungan untuk
step
,fillvalue
parameter:Ini menghasilkan potongan
size
item padastep
posisi bergulir waktu per iterasi padding setiap potongan denganfillvalue
jika perlu. Contoh untuksize=4, step=3, fillvalue='*'
:Untuk contoh use case untuk
step
parameter, lihat Memproses file .txt besar dengan python secara efisien .sumber
Ada perpustakaan yang melakukan persis apa yang Anda butuhkan:
sumber
step=3
harus benar-benar dihapus agar sesuai dengan permintaan OP:list(more_itertools.windowed(range(6), 3))
Kontribusi cepat.
Karena python docs saat ini tidak memiliki "jendela" di contoh itertool (yaitu, di bagian bawah http://docs.python.org/library/itertools.html ), inilah cuplikan berdasarkan kode untuk kerapu yang adalah salah satu contoh yang diberikan:
Pada dasarnya, kami membuat serangkaian irisan irisan, masing-masing dengan titik awal satu titik lebih jauh ke depan. Lalu, kita kumpulkan bersama-sama. Catatan, fungsi ini mengembalikan generator (bukan langsung generator itu sendiri).
Sama seperti versi append-element dan advance-iterator di atas, kinerja (yang mana yang terbaik) bervariasi dengan ukuran daftar dan ukuran jendela. Saya suka yang ini karena ini adalah dua-liner (bisa jadi satu-liner, tapi saya lebih suka konsep penamaan).
Ternyata kode di atas salah . Ini berfungsi jika parameter yang diteruskan ke iterable adalah urutan tetapi tidak jika itu adalah iterator. Jika itu adalah iterator, iterator yang sama dibagikan (tetapi tidak tee'd) di antara panggilan islice dan ini merusak segalanya.
Berikut ini beberapa kode tetap:
Juga, satu versi lagi untuk buku. Alih-alih menyalin iterator dan kemudian memajukan salinan berkali-kali, versi ini membuat salinan berpasangan dari masing-masing iterator saat kami memindahkan posisi awal ke depan. Jadi, iterator t memberikan iterator "lengkap" dengan titik awal di t dan juga dasar untuk membuat iterator t + 1:
sumber
Hanya untuk menunjukkan bagaimana Anda dapat menggabungkan
itertools
resep , saya memperluaspairwise
resep secara langsung kembali kewindow
resep menggunakanconsume
resep:The
window
Resep adalah sama seperti untukpairwise
, itu hanya menggantikan elemen tunggal "mengkonsumsi" pada keduatee
iterator -ed dengan semakin meningkatnya mengkonsumsi padan - 1
iterator. Menggunakanconsume
alih-alih membungkus setiap iteratorislice
sedikit lebih cepat (untuk iterables cukup besar) karena Anda hanya membayarislice
overhead pembungkus selamaconsume
fase, tidak selama proses mengekstraksi setiap nilai jendela-ed (jadi itu dibatasi olehn
, bukan jumlah item dalamiterable
).Kinerja-bijaksana, dibandingkan dengan beberapa solusi lain, ini cukup bagus (dan lebih baik daripada solusi lain yang saya uji karena skala). Diuji pada Python 3.5.0, Linux x86-64, menggunakan
ipython
%timeit
sihir.kindall adalah
deque
solusi , tweak untuk kinerja / kebenaran dengan menggunakanislice
bukannya ekspresi pembangkit rumah linting dan pengujian panjang yang dihasilkan sehingga tidak menghasilkan hasil yang ketika iterable lebih pendek dari jendela, serta melewatimaxlen
darideque
secara posisi bukannya menurut kata kunci (membuat perbedaan mengejutkan untuk input yang lebih kecil):Sama seperti solusi kindall yang diadaptasi sebelumnya, tetapi dengan masing-masing
yield win
diubah untukyield tuple(win)
menyimpan hasil dari generator bekerja tanpa semua hasil yang disimpan benar-benar menjadi pandangan dari hasil terbaru (semua solusi masuk akal lainnya aman dalam skenario ini), dan menambahtuple=tuple
definisi fungsi untuk memindahkan penggunaantuple
dariB
dalamLEGB
keL
:consume
solusi berbasis-ditunjukkan di atas:Sama seperti
consume
, tetapi sebariselse
kasusconsume
untuk menghindari pemanggilan fungsi dann is None
pengujian untuk mengurangi runtime, khususnya untuk input kecil di mana overhead pengaturan adalah bagian yang berarti dari pekerjaan:(Catatan-samping: Varian pada
pairwise
yang menggunakantee
argumen default 2 berulang kali untuk membuattee
objek bersarang , sehingga setiap iterator yang diberikan hanya maju sekali, tidak dikonsumsi secara mandiri dalam peningkatan jumlah kali, mirip dengan jawaban MrDrFenner mirip dengan non-inlineconsume
dan lebih lambat dari yang diuraikanconsume
pada semua tes, jadi saya telah menghilangkan hasil tersebut untuk singkatnya).Seperti yang dapat Anda lihat, jika Anda tidak peduli tentang kemungkinan penelepon perlu menyimpan hasil, versi saya yang dioptimalkan dari solusi kindall memenangkan sebagian besar waktu, kecuali dalam "case iterable besar, case ukuran jendela kecil" (di mana yang disebutkan
consume
menang ); itu terdegradasi dengan cepat seiring dengan meningkatnya ukuran iterable, sementara tidak menurunkan sama sekali dengan meningkatnya ukuran window (setiap solusi lain terdegradasi lebih lambat untuk peningkatan ukuran iterable, tetapi juga menurunkan untuk ukuran window meningkat). Ia bahkan dapat diadaptasi untuk case "need tuple" dengan membungkusnyamap(tuple, ...)
, yang bekerja sedikit lebih lambat daripada menempatkan tupling pada fungsi, tetapi ini sepele (membutuhkan 1-5% lebih lama) dan memungkinkan Anda menjaga fleksibilitas berjalan lebih cepat ketika Anda bisa mentolerir berulang kali mengembalikan nilai yang sama.Jika Anda membutuhkan keamanan terhadap pengembalian yang disimpan, inline
consume
menang pada semua tetapi ukuran input terkecil (dengan non-inlineconsume
menjadi sedikit lebih lambat tetapi scaling sama). Solusideque
& tupling berbasis menang hanya untuk input terkecil, karena biaya setup lebih kecil, dan keuntungannya kecil; itu rusak parah karena iterable semakin lama.Sebagai catatan, versi disesuaikan dari solusi kindall yang
yield
stuple
s saya digunakan adalah:Jatuhkan caching
tuple
pada baris definisi fungsi dan gunakantuple
masingyield
- masing untuk mendapatkan versi yang lebih cepat namun kurang aman.sumber
consume
adalah tujuan umum (termasuk kemampuan untuk melakukan secara lengkapconsume
) dan dengan demikian memerlukan impor tambahan dan uji per penggunaan untukn is None
. Dalam kode nyata, jika dan hanya jika saya menentukan kinerja adalah masalah, atau saya benar-benar membutuhkan kode yang lebih ringkas, saya akan mempertimbangkan untuk memasukkanelse
kasusconsume
menjadiwindow
, dengan asumsi saya tidak menggunakanconsume
untuk hal lain. Tetapi jika kinerja tidak terbukti menjadi masalah, saya akan mempertahankan definisi yang terpisah;consume
fungsi bernama membuat operasi kurang ajaib / mendokumentasikan diri.Saya menggunakan kode berikut sebagai jendela geser sederhana yang menggunakan generator untuk secara drastis meningkatkan keterbacaan. Kecepatannya sejauh ini sudah cukup untuk digunakan dalam analisis urutan bioinformatika dalam pengalaman saya.
Saya memasukkannya di sini karena saya belum melihat metode ini digunakan. Sekali lagi, saya tidak membuat klaim tentang kinerja yang dibandingkan.
sumber
len(sequence)
panggilan. Ini tidak akan berfungsi jikasequence
iterator atau generator. Ketika input cocok dengan memori, ini memang menawarkan solusi yang lebih mudah dibaca daripada dengan iterator.sumber
range(len
adalah pola buruk di python?versi yang sedikit dimodifikasi dari jendela deque, untuk menjadikannya jendela bergulir yang sebenarnya. Sehingga mulai diisi hanya dengan satu elemen, lalu tumbuh hingga ukuran jendela maksimumnya, dan kemudian menyusut saat tepi kiri mendekati akhir:
ini memberi
sumber
Dibuat ini untuk fungsi rata-rata bergulir
sumber
kenapa tidak
Itu didokumentasikan dalam Python doc . Anda dapat dengan mudah memperluasnya ke jendela yang lebih luas.
sumber
Beberapa iterator!
next(it)
memunculkanStopIteration
ketika urutan selesai, dan untuk beberapa alasan keren itu di luar saya, pernyataan hasil di sini kecuali itu dan fungsi kembali, mengabaikan nilai sisa yang tidak membentuk jendela penuh.Pokoknya, ini adalah solusi paling tidak namun yang satu-satunya persyaratan adalah bahwa
seq
menerapkan salah satu__iter__
atau__getitem__
tidak dan tidak bergantung padaitertools
ataucollections
selain solusi @ dansalmo :)sumber
Mari kita membuatnya malas!
sumber
"" "
sumber
Saya menguji beberapa solusi dan satu yang saya buat dan menemukan yang saya buat menjadi yang tercepat, jadi saya pikir saya akan membagikannya.
sumber
sumber
Bagaimana menggunakan yang berikut ini:
Keluaran:
sumber
Ini adalah pertanyaan lama tetapi bagi mereka yang masih tertarik ada implementasi yang bagus dari slider jendela menggunakan generator dalam hal ini halaman (oleh Adrian Rosebrock).
Ini adalah implementasi untuk OpenCV namun Anda dapat dengan mudah menggunakannya untuk tujuan lain. Untuk yang bersemangat saya akan menempelkan kode di sini tetapi untuk memahaminya lebih baik saya sarankan mengunjungi halaman asli.
Tip: Anda dapat memeriksa
.shape
jendela saat iterasi generator untuk membuang yang tidak memenuhi persyaratan AndaBersulang
sumber
Jawaban DiPaolo yang dimodifikasi untuk memungkinkan pengisian acak dan ukuran langkah variabel
sumber
di sini adalah satu liner. Saya menghitung waktunya dan itu kompatibel dengan kinerja jawaban atas dan semakin progresif dengan seq lebih besar dari 20% lebih lambat dengan len (seq) = 20 dan 7% lebih lambat dengan len (seq) = 10000
sumber
Mencoba bagian saya, sederhana, satu liner, cara pythonic menggunakan islice. Tapi, mungkin tidak efisien secara optimal.
Penjelasan: Buat jendela dengan menggunakan islice dari window_size dan ulangi operasi ini menggunakan peta di atas semua larik.
sumber
Fungsi yang Dioptimalkan untuk menggeser data jendela dalam pembelajaran yang mendalam
sumber