Hanya untuk menunjukkan bagaimana Anda dapat menggabungkan itertools
resep , saya memperluas pairwise
resep secara langsung kembali ke window
resep menggunakan consume
resep:
def consume(iterator, n):
"Advance the iterator n-steps ahead. If n is none, consume entirely."
# Use functions that consume iterators at C speed.
if n is None:
# feed the entire iterator into a zero-length deque
collections.deque(iterator, maxlen=0)
else:
# advance to the empty slice starting at position n
next(islice(iterator, n, n), None)
def window(iterable, n=2):
"s -> (s0, ...,s(n-1)), (s1, ...,sn), (s2, ..., s(n+1)), ..."
iters = tee(iterable, n)
# Could use enumerate(islice(iters, 1, None), 1) to avoid consume(it, 0), but that's
# slower for larger window sizes, while saving only small fixed "noop" cost
for i, it in enumerate(iters):
consume(it, i)
return zip(*iters)
The window
Resep adalah sama seperti untuk pairwise
, itu hanya menggantikan elemen tunggal "mengkonsumsi" pada kedua tee
iterator -ed dengan semakin meningkatnya mengkonsumsi pada n - 1
iterator. Menggunakan consume
alih-alih membungkus setiap iterator islice
sedikit lebih cepat (untuk iterables cukup besar) karena Anda hanya membayar islice
overhead pembungkus selama consume
fase, tidak selama proses mengekstraksi setiap nilai jendela-ed (jadi itu dibatasi oleh n
, bukan jumlah item dalam iterable
).
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 menggunakan islice
bukannya ekspresi pembangkit rumah linting dan pengujian panjang yang dihasilkan sehingga tidak menghasilkan hasil yang ketika iterable lebih pendek dari jendela, serta melewati maxlen
dari deque
secara posisi bukannya menurut kata kunci (membuat perbedaan mengejutkan untuk input yang lebih kecil):
>>> %timeit -r5 deque(windowkindall(range(10), 3), 0)
100000 loops, best of 5: 1.87 μs per loop
>>> %timeit -r5 deque(windowkindall(range(1000), 3), 0)
10000 loops, best of 5: 72.6 μs per loop
>>> %timeit -r5 deque(windowkindall(range(1000), 30), 0)
1000 loops, best of 5: 71.6 μs per loop
Sama seperti solusi kindall yang diadaptasi sebelumnya, tetapi dengan masing-masing yield win
diubah untuk yield 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 menambah tuple=tuple
definisi fungsi untuk memindahkan penggunaan tuple
dari B
dalam LEGB
ke L
:
>>> %timeit -r5 deque(windowkindalltupled(range(10), 3), 0)
100000 loops, best of 5: 3.05 μs per loop
>>> %timeit -r5 deque(windowkindalltupled(range(1000), 3), 0)
10000 loops, best of 5: 207 μs per loop
>>> %timeit -r5 deque(windowkindalltupled(range(1000), 30), 0)
1000 loops, best of 5: 348 μs per loop
consume
solusi berbasis-ditunjukkan di atas:
>>> %timeit -r5 deque(windowconsume(range(10), 3), 0)
100000 loops, best of 5: 3.92 μs per loop
>>> %timeit -r5 deque(windowconsume(range(1000), 3), 0)
10000 loops, best of 5: 42.8 μs per loop
>>> %timeit -r5 deque(windowconsume(range(1000), 30), 0)
1000 loops, best of 5: 232 μs per loop
Sama seperti consume
, tetapi sebaris else
kasus consume
untuk menghindari pemanggilan fungsi dan n is None
pengujian untuk mengurangi runtime, khususnya untuk input kecil di mana overhead pengaturan adalah bagian yang berarti dari pekerjaan:
>>> %timeit -r5 deque(windowinlineconsume(range(10), 3), 0)
100000 loops, best of 5: 3.57 μs per loop
>>> %timeit -r5 deque(windowinlineconsume(range(1000), 3), 0)
10000 loops, best of 5: 40.9 μs per loop
>>> %timeit -r5 deque(windowinlineconsume(range(1000), 30), 0)
1000 loops, best of 5: 211 μs per loop
(Catatan-samping: Varian pada pairwise
yang menggunakan tee
argumen default 2 berulang kali untuk membuat tee
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-inline consume
dan lebih lambat dari yang diuraikan consume
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 membungkusnya map(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-inline consume
menjadi sedikit lebih lambat tetapi scaling sama). Solusi deque
& 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
s tuple
s saya digunakan adalah:
def windowkindalltupled(iterable, n=2, tuple=tuple):
it = iter(iterable)
win = deque(islice(it, n), n)
if len(win) < n:
return
append = win.append
yield tuple(win)
for e in it:
append(e)
yield tuple(win)
Jatuhkan caching tuple
pada baris definisi fungsi dan gunakan tuple
masing yield
- masing untuk mendapatkan versi yang lebih cepat namun kurang aman.
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 .