เพียงเพื่อแสดงว่าคุณสามารถรวมitertools
สูตรอาหารได้อย่างไรฉันกำลังขยายpairwise
สูตรกลับไปสู่window
สูตรโดยตรงโดยใช้consume
สูตร:
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)
window
สูตรเช่นเดียวกับpairwise
ก็แค่แทนที่องค์ประกอบเดียว "กิน" ที่สองtee
iterator -ed ที่มีเพิ่มขึ้นเรื่อยกินบนn - 1
iterators การใช้consume
แทนที่จะห่อแต่ละตัววนซ้ำในislice
นั้นเร็วกว่าเล็กน้อย (สำหรับการวนซ้ำขนาดใหญ่พอ) เนื่องจากคุณจ่ายislice
ค่าโสหุ้ยการตัดในconsume
ช่วงเท่านั้นไม่ใช่ในระหว่างกระบวนการแยกแต่ละค่าหน้าต่าง -ed (ดังนั้นจะถูก จำกัด ด้วยn
จำนวนรายการในiterable
)
ประสิทธิภาพที่ชาญฉลาดเมื่อเทียบกับโซลูชันอื่น ๆ นี่ค่อนข้างดี (และดีกว่าโซลูชันอื่น ๆ ที่ฉันทดสอบเมื่อปรับขนาด) ทดสอบกับ Python 3.5.0, Linux x86-64 โดยใช้ipython
%timeit
เวทย์มนตร์
kindall เป็นdeque
ทางออก , เอ็นดูสำหรับประสิทธิภาพ / ความถูกต้องโดยใช้islice
แทนของบ้านรีดแสดงออกกำเนิดและการทดสอบความยาวที่เกิดขึ้นจึงไม่ได้ผลผลเมื่อ iterable สั้นกว่าหน้าต่างเช่นเดียวกับการส่งผ่านmaxlen
ของdeque
positionally แทน โดยคำหลัก (สร้างความแตกต่างที่น่าประหลาดใจสำหรับอินพุตที่เล็กกว่า):
>>> %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
เหมือนกับโซลูชัน kindall ดัดแปลงก่อนหน้านี้ แต่การyield win
เปลี่ยนแปลงแต่ละครั้งyield tuple(win)
เพื่อเก็บผลลัพธ์จากเครื่องกำเนิดไฟฟ้าโดยไม่มีผลลัพธ์ที่เก็บไว้ทั้งหมดเป็นมุมมองของผลลัพธ์ล่าสุด (โซลูชันที่สมเหตุสมผลอื่น ๆ ทั้งหมดปลอดภัยในสถานการณ์นี้) และเพิ่มtuple=tuple
คำจำกัดความของฟังก์ชัน เพื่อย้ายการใช้tuple
จากB
ในLEGB
เป็น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
โซลูชั่นพื้นฐานที่แสดงด้านบน:
>>> %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
เหมือนกันconsume
แต่เป็นelse
กรณีของconsume
เพื่อหลีกเลี่ยงการเรียกใช้ฟังก์ชันและn is None
การทดสอบเพื่อลด runtime, โดยเฉพาะอย่างยิ่งสำหรับอินพุตขนาดเล็กที่ค่าใช้จ่ายการตั้งค่าเป็นส่วนที่มีความหมายของงาน:
>>> %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
(ด้านข้างหมายเหตุ: ความแตกต่างในpairwise
การใช้งานที่tee
มีอาร์กิวเมนต์เริ่มต้นของ 2 ซ้ำ ๆ เพื่อให้ซ้อนกันtee
วัตถุดังนั้นใดก็ตาม iterator เป็นขั้นสูงเพียงครั้งเดียวไม่ได้บริโภคอิสระจำนวนที่เพิ่มขึ้นครั้งคล้ายกับคำตอบ MrDrFenner ของคล้ายกับที่ไม่ใช่ inlined consume
และช้ากว่า inlined consume
ในการทดสอบทั้งหมดดังนั้นฉันจึงไม่เห็นผลลัพธ์เหล่านั้นเพื่อความกระชับ)
อย่างที่เห็น, ถ้าคุณไม่สนใจเกี่ยวกับความเป็นไปได้ของผู้โทรที่ต้องการจัดเก็บผลลัพธ์โซลูชันของ kindall ที่ได้รับการปรับปรุงของฉันจะชนะเวลาส่วนใหญ่ยกเว้นในกรณี "ขนาดใหญ่ iterable ขนาดหน้าต่างเล็ก" (ที่อินไลน์consume
ชนะ ); มันลดขนาดลงอย่างรวดเร็วเมื่อขนาดเพิ่มขึ้นในขณะที่ไม่ลดขนาดเลยเมื่อขนาดของหน้าต่างเพิ่มขึ้น (โซลูชันอื่น ๆ จะลดลงช้ากว่าสำหรับขนาดที่เพิ่มได้ แต่ยังลดขนาดของหน้าต่างที่เพิ่มขึ้นด้วย) มันยังสามารถปรับให้เข้ากับเคส "need tuples" ได้โดยการหุ้มmap(tuple, ...)
ซึ่งจะช้ากว่าการวาง tupling ในฟังก์ชั่นเล็กน้อย แต่มันก็เล็กน้อย (ใช้เวลานานกว่า 1-5%) และช่วยให้คุณทำงานได้เร็วขึ้น เมื่อคุณสามารถทนการส่งคืนค่าเดียวกันซ้ำ ๆ
หากคุณต้องการความปลอดภัยจากผลตอบแทนที่ถูกเก็บไว้ inlined consume
ชนะกับทุกคนยกเว้นขนาดอินพุตที่เล็กที่สุด (โดยไม่อินไลน์consume
ช้ากว่าเล็กน้อย แต่มีขนาดใกล้เคียงกัน) deque
& tupling ตามชนะโซลูชั่นสำหรับปัจจัยการผลิตที่มีขนาดเล็กที่สุดเนื่องจากค่าใช้จ่ายในการติดตั้งที่มีขนาดเล็กและกำไรที่มีขนาดเล็ก; มันลดระดับลงอย่างรุนแรงเมื่อ iterable ยาวขึ้น
สำหรับบันทึกการปรับรุ่นของการแก้ปัญหาที่ kindall yield
s tuple
s ผมใช้คือ:
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)
วางแคชtuple
ในบรรทัดคำจำกัดความของฟังก์ชั่นและการใช้งานของtuple
แต่ละรายการyield
เพื่อให้ได้รุ่นที่เร็วขึ้น แต่ปลอดภัยน้อยลง
sum()
หรือmax()
) ควรคำนึงว่ามีอัลกอริทึมที่มีประสิทธิภาพในการคำนวณค่าใหม่สำหรับแต่ละหน้าต่างในเวลาคงที่ (โดยไม่คำนึงถึงขนาดของหน้าต่าง) ฉันได้เก็บบางส่วนของขั้นตอนวิธีการเหล่านี้ร่วมกันในห้องสมุดหลาม: กลิ้ง