Chỉ để cho thấy cách bạn có thể kết hợp các itertoolscông thức nấu ăn , tôi sẽ mở rộng pairwisecông thức một cách trực tiếp nhất có thể trở lại windowcông thức bằng cách sử dụng consumecông thức:
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)
Công windowthức tương tự như đối với pairwise, nó chỉ thay thế phần tử duy nhất "tiêu thụ" trên teetrình lặp thứ hai với mức tăng dần tiêu thụ trên các n - 1trình vòng lặp. Việc sử dụng consumethay vì gói mỗi iterator vào islicenhanh hơn một chút (đối với các iterables đủ lớn) vì bạn chỉ trả islicechi phí gói trong suốt consumegiai đoạn, không phải trong quá trình trích xuất từng giá trị cửa sổ (vì vậy, nó bị giới hạn bởi n, không phải số lượng vật phẩm trong iterable).
Hiệu suất-khôn ngoan, so với một số giải pháp khác, điều này là khá tốt (và tốt hơn bất kỳ giải pháp nào khác mà tôi đã thử nghiệm khi nó mở rộng). Đã thử nghiệm trên Python 3.5.0, Linux x86-64, sử dụng ipython %timeitphép thuật.
kindall là dequegiải pháp , tinh chỉnh cho hiệu suất / đúng đắn bằng cách sử dụng islicethay vì một biểu thức máy phát điện gia đình cán và kiểm tra độ dài kết quả vì vậy nó không mang lại kết quả khi iterable là ngắn hơn so với các cửa sổ, cũng như vượt qua maxlennhững dequeđịa vị thay vì theo từ khóa (tạo sự khác biệt đáng ngạc nhiên cho các đầu vào nhỏ hơn):
>>> %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
Giống như giải pháp sắp xếp phù hợp trước đây, nhưng với mỗi yield winthay đổi để yield tuple(win)lưu trữ kết quả từ trình tạo hoạt động mà không có tất cả các kết quả được lưu trữ thực sự là một kết quả gần đây nhất (tất cả các giải pháp hợp lý khác đều an toàn trong kịch bản này) và thêm tuple=tuplevào định nghĩa hàm để chuyển việc sử dụng tupletừ Btrong LEGBsang 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
consumegiải pháp dựa trên hiển thị ở trên:
>>> %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
Tương tự như consume, nhưng elsetrường hợp nội tuyến consumeđể tránh chức năng gọi và n is Nonekiểm tra để giảm thời gian chạy, đặc biệt đối với các đầu vào nhỏ trong đó chi phí thiết lập là một phần có ý nghĩa của công việc:
>>> %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
(Lưu ý bên lề: Một biến thể pairwisesử dụng teevới đối số mặc định là 2 lần để tạo các teeđối tượng lồng nhau , do đó, bất kỳ trình vòng lặp đã cho nào chỉ được nâng cao một lần, không tiêu thụ độc lập số lần tăng, tương tự như câu trả lời của MrDrFenner tương tự như không được nội tuyến consumevà chậm hơn so với nội dung consumetrong tất cả các bài kiểm tra, vì vậy tôi đã bỏ qua những kết quả đó để cho ngắn gọn).
Như bạn có thể thấy, nếu bạn không quan tâm đến khả năng người gọi cần lưu trữ kết quả, phiên bản tối ưu hóa giải pháp của tôi sẽ thắng hầu hết thời gian, ngoại trừ trong "trường hợp kích thước cửa sổ nhỏ có thể lặp lại lớn" (trong đó consumechiến thắng được nội tuyến ); nó xuống cấp nhanh chóng khi kích thước lặp tăng lên, trong khi không suy giảm chút nào khi kích thước cửa sổ tăng (mọi giải pháp khác đều giảm chậm hơn khi tăng kích thước lặp, nhưng cũng giảm khi tăng kích thước cửa sổ). Nó thậm chí có thể được điều chỉnh cho trường hợp "cần bộ dữ liệu" bằng cách gói map(tuple, ...), nó chạy chậm hơn một chút so với đặt bộ dữ liệu vào chức năng, nhưng nó tầm thường (mất 1-5% lâu hơn) và cho phép bạn giữ được sự linh hoạt khi chạy nhanh hơn khi bạn có thể chịu đựng nhiều lần trả lại cùng một giá trị.
Nếu bạn cần sự an toàn chống lại lợi nhuận được lưu trữ, thì consumechiến thắng được đặt trên tất cả trừ các kích thước đầu vào nhỏ nhất (với không consumeđược đặt nội tuyến sẽ chậm hơn một chút nhưng tỷ lệ tương tự). Các dequevà tupling thắng giải pháp dựa trên chỉ dành cho các đầu vào nhỏ nhất, vì chi phí thiết lập nhỏ hơn, và đạt được là nhỏ; nó xuống cấp trầm trọng khi lặp đi lặp lại lâu hơn.
Đối với hồ sơ, phiên bản chuyển thể của giải pháp kindall rằng yields tuples tôi sử dụng là:
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)
Bỏ bộ nhớ đệm tupletrong dòng định nghĩa hàm và sử dụng tupletừng bộ yieldđể có phiên bản nhanh hơn nhưng kém an toàn hơn.
sum()hoặcmax()), điều đáng lưu ý là có các thuật toán hiệu quả để tính giá trị mới cho mỗi cửa sổ trong thời gian không đổi (không phân biệt kích thước cửa sổ). Tôi đã thu thập một số thuật toán này cùng nhau trong một thư viện Python: roll .