Cửa sổ cuộn hoặc trượt?


150

Tôi cần một cửa sổ cuộn (hay còn gọi là cửa sổ trượt) có thể lặp qua một chuỗi / trình lặp / trình tạo. Lặp lại Python mặc định có thể được coi là trường hợp đặc biệt, trong đó độ dài cửa sổ là 1. Tôi hiện đang sử dụng mã sau đây. Có ai có một phương pháp Pythonic nhiều hơn, ít dài dòng hơn hoặc hiệu quả hơn để làm điều này không?

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

3
Nếu bạn đang tìm cách thực hiện một số loại hoạt động trên mỗi cửa sổ khi bạn lặp lại (ví dụ sum()hoặc max()), đ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 .
Alex Riley

Câu trả lời:


123

Có một phiên bản cũ của tài liệu Python với các itertoolsví dụ :

from itertools import islice

def window(seq, n=2):
    "Returns a sliding window (of width n) over data from the iterable"
    "   s -> (s0,s1,...s[n-1]), (s1,s2,...,sn), ...                   "
    it = iter(seq)
    result = tuple(islice(it, n))
    if len(result) == n:
        yield result
    for elem in it:
        result = result[1:] + (elem,)
        yield result

Một trong những tài liệu ngắn gọn hơn một chút và sử dụng itertoolsđể tạo hiệu ứng lớn hơn tôi tưởng tượng.


2
Câu trả lời hay, nhưng (và tôi biết bạn chỉ đang sao chép công thức như được liên kết), tôi tự hỏi tại sao kích thước cửa sổ mặc định phải là 2? Nó có nên mặc định không?
Độc thân Khuyến khích

19
@TakenMacGuy: Tôi không biết tác giả của lý do công thức đó là gì, nhưng tôi cũng chọn 2. 2 là kích thước cửa sổ hữu ích nhỏ nhất (nếu không bạn chỉ lặp đi lặp lại và không cần cửa sổ), và nó cũng phổ biến để biết các mục trước (hoặc tiếp theo), được cho là nhiều hơn bất kỳ n cụ thể nào khác.
kindall

27
Có ai biết tại sao ví dụ này đã bị xóa khỏi các tài liệu không? Có điều gì đó sai với nó, hoặc có một sự thay thế dễ dàng hơn bây giờ?
wim


2
Khi nào một người sẽ vào for elem in itvòng lặp?
Glassjawed

47

Điều này dường như được thiết kế riêng cho một collections.dequevì về cơ bản bạn có một FIFO (thêm vào một đầu, loại bỏ từ đầu kia). Tuy nhiên, ngay cả khi bạn sử dụng, listbạn không nên cắt hai lần; thay vào đó, có lẽ bạn chỉ nên pop(0)từ danh sách và append()mục mới.

Dưới đây là một triển khai dựa trên deque được tối ưu hóa theo khuôn mẫu ban đầu của bạn:

from collections import deque

def window(seq, n=2):
    it = iter(seq)
    win = deque((next(it, None) for _ in xrange(n)), maxlen=n)
    yield win
    append = win.append
    for e in it:
        append(e)
        yield win

Trong các thử nghiệm của tôi, nó thường xuyên đánh bại mọi thứ khác được đăng ở đây, mặc dù teephiên bản của trụ cột đánh bại nó cho các vòng lặp lớn và các cửa sổ nhỏ. Trên các cửa sổ lớn hơn,deque kéo về phía trước một lần nữa ở tốc độ thô.

Truy cập vào các mục riêng lẻ trong dequecó thể nhanh hơn hoặc chậm hơn so với danh sách hoặc bộ dữ liệu. (Các mục gần đầu nhanh hơn hoặc các mục gần cuối nếu bạn sử dụng chỉ số âm.) Tôi đặt một phần sum(w)trong vòng lặp của mình; điều này phát huy sức mạnh của deque (lặp từ mục này sang mục tiếp theo rất nhanh, vì vậy vòng lặp này chạy nhanh hơn 20% so với phương pháp nhanh nhất tiếp theo, viên thuốc). Khi tôi thay đổi nó để tra cứu riêng lẻ và thêm các mục trong một cửa sổ mười, các bảng quay và teephương thức nhanh hơn 20%. Tôi đã có thể phục hồi một số tốc độ bằng cách sử dụng các chỉ số tiêu cực cho năm thuật ngữ cuối cùng trong phần bổ sung, nhưng teevẫn nhanh hơn một chút. Nhìn chung, tôi sẽ ước tính rằng một trong hai là nhanh chóng cho hầu hết các sử dụng và nếu bạn cần thêm một chút hiệu suất, hồ sơ và chọn một trong đó hoạt động tốt nhất.


11
yield winnên yield tuple(win)hoặc yield list(win)để ngăn trả về một iterator của các tham chiếu đến cùng một dequeđối tượng.
Joel Cornett

1
Tôi đã gửi nó cho PyPI . Cài đặt với pip install sliding_windowvà chạy với from sliding_window import window.
Thomas Levine

1
Bạn sẽ bị sốc nếu bạn nghĩ list(window(range(10)))nên tạo ra thứ gì đó như [[0,1], [1,2], [2,3], ...]
Paul

1
Nó rõ ràng sẽ không; bạn cần phải làm một cái gì đó giống như list(list(x) for x in window(range(10)))hoặc thêm nó vào iterator. Đối với một số ứng dụng, điều này sẽ có vấn đề, đối với những ứng dụng khác thì không, và vì tôi sẽ tăng tốc nên tôi đã không chọn và đặt onus cho người gọi để sao chép cửa sổ nếu cần.
loại

1
Nếu bạn thêm lại cần thiết tuple()trước khi mang lại, phương pháp này không có bất kỳ lợi thế nào so với các phương pháp khác.
kawing-chiu

35

Tôi thích tee():

from itertools import tee, izip

def window(iterable, size):
    iters = tee(iterable, size)
    for i in xrange(1, size):
        for each in iters[i:]:
            next(each, None)
    return izip(*iters)

for each in window(xrange(6), 3):
    print list(each)

cho:

[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]

Từ timeitcác thử nghiệm nhanh của tôi , tốc độ này chậm hơn nhiều so với Daniel DePaolo (theo tỷ lệ 2: 1) và không cảm thấy "đẹp" hơn nhiều.
David B.

@David B.: Trên hộp của tôi, nó chỉ chậm hơn khoảng 8% so với Daniel DePaolo.
thuốc

@pillmuncher: Python 2.7 hay 3.x? Tôi đã sử dụng 2.7. Tỷ lệ cũng khá nhạy cảm với giá trị của size. Nếu bạn tăng nó (ví dụ: nếu iterable dài 100000 phần tử, hãy tạo kích thước cửa sổ 1000), bạn có thể thấy mức tăng.
David B.

2
@David B.: Những gì bạn nói có ý nghĩa. Trong mã của tôi, thời gian thiết lập iterslà O (kích thước!) Và việc gọi next()nhiều lần (trong izip()) có lẽ tốn nhiều thời gian hơn so với sao chép một tuple hai lần. Tôi đã sử dụng Python 2.6.5, BTW.
thuốc

@pillmuncher: Ý bạn là, thời gian thiết lập iterslà O (kích thước ^ 2), phải không?
David B.

20

Dưới đây là một tổng quát có thêm hỗ trợ cho step, fillvaluecác thông số:

from collections import deque
from itertools import islice

def sliding_window(iterable, size=2, step=1, fillvalue=None):
    if size < 0 or step < 1:
        raise ValueError
    it = iter(iterable)
    q = deque(islice(it, size), maxlen=size)
    if not q:
        return  # empty iterable or size == 0
    q.extend(fillvalue for _ in range(size - len(q)))  # pad to size
    while True:
        yield iter(q)  # iter() to avoid accidental outside modifications
        try:
            q.append(next(it))
        except StopIteration: # Python 3.5 pep 479 support
            return
        q.extend(next(it, fillvalue) for _ in range(step - 1))

Nó mang lại các sizevật phẩm chunk tại một thời điểm lăn steptrên mỗi lần lặp đệm mỗi đoạn với fillvaluenếu cần thiết. Ví dụ cho size=4, step=3, fillvalue='*':

 [a b c d]e f g h i j k l m n o p q r s t u v w x y z
  a b c[d e f g]h i j k l m n o p q r s t u v w x y z
  a b c d e f[g h i j]k l m n o p q r s t u v w x y z
  a b c d e f g h i[j k l m]n o p q r s t u v w x y z
  a b c d e f g h i j k l[m n o p]q r s t u v w x y z
  a b c d e f g h i j k l m n o[p q r s]t u v w x y z
  a b c d e f g h i j k l m n o p q r[s t u v]w x y z
  a b c d e f g h i j k l m n o p q r s t u[v w x y]z
  a b c d e f g h i j k l m n o p q r s t u v w x[y z * *]

Để biết ví dụ về trường hợp sử dụng cho steptham số, hãy xem Xử lý tệp .txt lớn trong python một cách hiệu quả .


17

Có một thư viện thực hiện chính xác những gì bạn cần:

import more_itertools
list(more_itertools.windowed([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],n=3, step=3))

Out: [(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12), (13, 14, 15)]

step=3thực sự nên được gỡ bỏ để phù hợp với yêu cầu của OP:list(more_itertools.windowed(range(6), 3))
user3780389

10

Chỉ cần đóng góp nhanh chóng.

Vì các tài liệu python hiện tại không có "cửa sổ" trong các ví dụ itertool (ví dụ: ở dưới cùng của http://docs.python.org/l Library / itemertools.html ), đây là đoạn trích dựa trên mã cho nhóm cá mú là một trong những ví dụ được đưa ra:

import itertools as it
def window(iterable, size):
    shiftedStarts = [it.islice(iterable, s, None) for s in xrange(size)]
    return it.izip(*shiftedStarts)

Về cơ bản, chúng tôi tạo ra một loạt các trình vòng lặp được cắt lát, mỗi điểm có một điểm bắt đầu một điểm tiến xa hơn. Sau đó, chúng tôi nén chúng lại với nhau. Lưu ý, hàm này trả về một trình tạo (nó không trực tiếp là một trình tạo).

Giống như các phiên bản phần tử nối thêm và phần mở rộng ở trên, hiệu suất (tức là tốt nhất) thay đổi theo kích thước danh sách và kích thước cửa sổ. Tôi thích cái này bởi vì nó là một lớp lót (nó có thể là một lớp lót, nhưng tôi thích các khái niệm đặt tên).

Hóa ra mã trên là sai . Nó hoạt động nếu tham số được truyền cho iterable là một chuỗi nhưng không phải nếu nó là một iterator. Nếu đó là một trình vòng lặp, cùng một trình vòng lặp được chia sẻ (nhưng không phải là tee'd) trong số các lệnh gọi islice và điều này phá vỡ mọi thứ một cách tồi tệ.

Đây là một số mã cố định:

import itertools as it
def window(iterable, size):
    itrs = it.tee(iterable, size)
    shiftedStarts = [it.islice(anItr, s, None) for s, anItr in enumerate(itrs)]
    return it.izip(*shiftedStarts)

Ngoài ra, một phiên bản nữa cho các cuốn sách. Thay vì sao chép một trình vòng lặp và sau đó tiến lên các bản sao nhiều lần, phiên bản này tạo các bản sao theo cặp của mỗi trình vòng lặp khi chúng ta di chuyển vị trí bắt đầu về phía trước. Do đó, iterator t cung cấp cả iterator "hoàn chỉnh" với điểm bắt đầu tại t và cũng là cơ sở để tạo iterator t + 1:

import itertools as it
def window4(iterable, size):
    complete_itr, incomplete_itr = it.tee(iterable, 2)
    iters = [complete_itr]
    for i in xrange(1, size):
        incomplete_itr.next()
        complete_itr, incomplete_itr = it.tee(incomplete_itr, 2)
        iters.append(complete_itr)
    return it.izip(*iters)

9

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.


Rõ ràng, điều này là ít hiệu quả hơn nó có thể được; consumelà mục đích chung (bao gồm khả năng thực hiện hoàn thành consume) và do đó cần nhập thêm và kiểm tra mỗi lần sử dụng n is None. Trong mã thực tế, khi và chỉ khi tôi muốn thực hiện quyết tâm là một vấn đề, hoặc tôi thực sự cần mã ngắn gọn hơn, tôi muốn xem xét nội tuyến các elsetrường hợp consumevào window, giả sử tôi đã không sử dụng consumecho bất cứ điều gì khác. Nhưng nếu hiệu suất không được coi là một vấn đề, tôi sẽ giữ các định nghĩa riêng biệt; consumechức năng được đặt tên làm cho hoạt động ít ma thuật / tự ghi lại.
ShadowRanger

7

Tôi sử dụng đoạn mã sau như một cửa sổ trượt đơn giản sử dụng máy phát điện để tăng đáng kể khả năng đọc. Tốc độ của nó cho đến nay là đủ để sử dụng trong phân tích trình tự tin sinh học theo kinh nghiệm của tôi.

Tôi bao gồm nó ở đây vì tôi chưa thấy phương pháp này được sử dụng. Một lần nữa, tôi không tuyên bố về hiệu suất so sánh của nó.

def slidingWindow(sequence,winSize,step=1):
"""Returns a generator that will iterate through
the defined chunks of input sequence. Input sequence
must be sliceable."""

    # Verify the inputs
    if not ((type(winSize) == type(0)) and (type(step) == type(0))):
        raise Exception("**ERROR** type(winSize) and type(step) must be int.")
    if step > winSize:
        raise Exception("**ERROR** step must not be larger than winSize.")
    if winSize > len(sequence):
        raise Exception("**ERROR** winSize must not be larger than sequence length.")

    # Pre-compute number of chunks to emit
    numOfChunks = ((len(sequence)-winSize)/step)+1

    # Do the work
    for i in range(0,numOfChunks*step,step):
        yield sequence[i:i+winSize]

3
Hạn chế chính ở đây là len(sequence)cuộc gọi. Điều này sẽ không hoạt động nếu sequencelà một trình vòng lặp hoặc trình tạo. Khi đầu vào không vừa trong bộ nhớ, điều này sẽ cung cấp một giải pháp dễ đọc hơn so với các trình vòng lặp.
David B.

Vâng bạn đã đúng. Trường hợp cụ thể này ban đầu được dùng để quét các chuỗi DNA thường được biểu diễn dưới dạng chuỗi. Nó chắc chắn có giới hạn mà bạn đề cập. Nếu bạn muốn, bạn có thể chỉ cần kiểm tra từng lát cắt để đảm bảo nó vẫn có độ dài phù hợp và sau đó quên đi việc phải biết độ dài của toàn bộ chuỗi. Nhưng nó sẽ thêm một chút chi phí (kiểm tra len () mỗi lần lặp).
Gus

6
def GetShiftingWindows(thelist, size):
    return [ thelist[x:x+size] for x in range( len(thelist) - size + 1 ) ]

>> a = [1, 2, 3, 4, 5]
>> GetShiftingWindows(a, 3)
[ [1, 2, 3], [2, 3, 4], [3, 4, 5] ]

Ngay lập tức bạn thấy "phạm vi (len" trong Python là mùi mã.
Mark Lawrence

@MarkLawrence Điều gì khiến bạn nghĩ range(lenlà một mô hình xấu trong python?
duhaime

5

một phiên bản sửa đổi một chút của cửa sổ deque, để làm cho nó trở thành một cửa sổ cuộn thực sự. Vì vậy, nó bắt đầu được phổ biến chỉ với một yếu tố, sau đó tăng lên kích thước cửa sổ tối đa và sau đó co lại khi cạnh trái của nó đến gần cuối:

from collections import deque
def window(seq, n=2):
    it = iter(seq)
    win = deque((next(it, None) for _ in xrange(1)), maxlen=n)
    yield win
    append = win.append
    for e in it:
        append(e)
        yield win
    for _ in xrange(len(win)-1):
        win.popleft()
        yield win

for wnd in window(range(5), n=3):
    print(list(wnd))

cái này cho

[0]
[0, 1]
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4]
[4]

3
def rolling_window(list, degree):
    for i in range(len(list)-degree+1):
        yield [list[i+o] for o in range(degree)]

Làm điều này cho một chức năng trung bình cán


3

tại sao không

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return zip(a, b)

Đây là tài liệu bằng Python doc . Bạn có thể dễ dàng mở rộng nó ra cửa sổ rộng hơn.


2

Nhiều vòng lặp!

def window(seq, size, step=1):
    # initialize iterators
    iters = [iter(seq) for i in range(size)]
    # stagger iterators (without yielding)
    [next(iters[i]) for j in range(size) for i in range(-1, -j-1, -1)]
    while(True):
        yield [next(i) for i in iters]
        # next line does nothing for step = 1 (skips iterations for step > 1)
        [next(i) for i in iters for j in range(step-1)]

next(it)tăng lên StopIterationkhi chuỗi kết thúc và vì một lý do tuyệt vời nào đó ngoài tôi, câu lệnh lợi nhuận ở đây chấp nhận nó và hàm trả về, bỏ qua các giá trị còn lại không tạo thành một cửa sổ đầy đủ.

Dù sao, đây là giải pháp ít dòng nhất mà yêu cầu duy nhất là seqthực hiện __iter__hoặc __getitem__không dựa vào itertoolshoặc collectionsbên cạnh giải pháp của @ dansalmo :)


lưu ý: bước đi loạng choạng là O (n ^ 2) trong đó n là kích thước của cửa sổ và chỉ xảy ra trong cuộc gọi đầu tiên. Nó có thể được tối ưu hóa xuống O (n), nhưng nó sẽ làm cho mã trở nên rắc rối hơn một chút: P
jameh

2

Hãy làm cho nó lười biếng!

from itertools import islice, tee

def window(iterable, size): 
    iterators = tee(iterable, size) 
    iterators = [islice(iterator, i, None) for i, iterator in enumerate(iterators)]  
    yield from zip(*iterators)

list(window(range(5), 3))
# [(0, 1, 2), (1, 2, 3), (2, 3, 4)]

1
#Importing the numpy library
import numpy as np
arr = np.arange(6) #Sequence
window_size = 3
np.lib.stride_tricks.as_strided(arr, shape= (len(arr) - window_size +1, window_size), 
strides = arr.strides*2)

"""Example output:

  [0, 1, 2]
  [1, 2, 3]
  [2, 3, 4]
  [3, 4, 5]

"" "


3
Hãy viết một số văn bản về câu trả lời của bạn.
jrswgtr

1

Tôi đã thử nghiệm một vài giải pháp và một giải pháp tôi đã đưa ra và tìm ra giải pháp tôi đưa ra là nhanh nhất nên tôi nghĩ tôi sẽ chia sẻ nó.

import itertools
import sys

def windowed(l, stride):
    return zip(*[itertools.islice(l, i, sys.maxsize) for i in range(stride)])

1
Trông tương tự như giải pháp đầu tiên từ câu trả lời này: stackoverflow.com/a/11249883/7851470
Georgy

@georgy Tôi nghĩ rằng tôi đã bỏ qua câu trả lời đó vì nó được viết bằng Python2 nhưng tôi đồng ý, về cơ bản là giống nhau!
Ryan Codrai

0
>>> n, m = 6, 3
>>> k = n - m+1
>>> print ('{}\n'*(k)).format(*[range(i, i+m) for i in xrange(k)])
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]

0

Làm thế nào về việc sử dụng như sau:

mylist = [1, 2, 3, 4, 5, 6, 7]

def sliding_window(l, window_size=2):
    if window_size > len(l):
        raise ValueError("Window size must be smaller or equal to the number of elements in the list.")

    t = []
    for i in xrange(0, window_size):
        t.append(l[i:])

    return zip(*t)

print sliding_window(mylist, 3)

Đầu ra:

[(1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6), (5, 6, 7)]

@ keocra zip (* t) nghĩa là gì? Tôi có thể tìm thấy một số tài liệu về loại tuyên bố đó ở đâu?
Shejo284

1
Python 2.7: docs.python.org/2/library/functions.html#zip , ngôi sao giải nén danh sách và cung cấp các yếu tố cá nhân như là đầu vào để zip ( giải nén đối số )
keocra

0

Đây là một câu hỏi cũ nhưng đối với những người vẫn quan tâm thì có một triển khai tuyệt vời của thanh trượt cửa sổ bằng cách sử dụng máy phát điện trong này trang (bởi Adrian Rosebrock).

Đây là một triển khai cho OpenCV tuy nhiên bạn có thể dễ dàng sử dụng nó cho bất kỳ mục đích nào khác. Đối với những người háo hức tôi sẽ dán mã ở đây nhưng để hiểu rõ hơn tôi khuyên bạn nên truy cập trang gốc.

def sliding_window(image, stepSize, windowSize):
    # slide a window across the image
    for y in xrange(0, image.shape[0], stepSize):
        for x in xrange(0, image.shape[1], stepSize):
            # yield the current window
            yield (x, y, image[y:y + windowSize[1], x:x + windowSize[0]])

Mẹo: Bạn có thể kiểm tra .shapecửa sổ khi lặp lại trình tạo để loại bỏ những cửa sổ không đáp ứng yêu cầu của bạn

Chúc mừng


0

Câu trả lời của DiPaolo đã sửa đổi để cho phép điền tùy ý và kích thước bước thay đổi

import itertools
def window(seq, n=2,step=1,fill=None,keep=0):
    "Returns a sliding window (of width n) over data from the iterable"
    "   s -> (s0,s1,...s[n-1]), (s1,s2,...,sn), ...                   "
    it = iter(seq)
    result = tuple(itertools.islice(it, n))    
    if len(result) == n:
        yield result
    while True:        
#         for elem in it:        
        elem = tuple( next(it, fill) for _ in range(step))
        result = result[step:] + elem        
        if elem[-1] is fill:
            if keep:
                yield result
            break
        yield result

0

đây là một lớp lót Tôi đã tính thời gian và nó có thể phù hợp với hiệu suất của câu trả lời hàng đầu và ngày càng tốt hơn với seq lớn hơn từ chậm hơn 20% với len (seq) = 20 và chậm hơn 7% với len (seq) = 10000

zip(*[seq[i:(len(seq) - n - 1 + i)] for i in range(n)])

Vui lòng thêm một số văn bản giải thích với câu trả lời của bạn. Không phải ai cũng vấp phải chủ đề này là Ninja Ninja.
Abhijit Sarkar

đó là tắt bởi 2, điều này hoạt động: zip (* [seq [i: (len (seq) - n + 1 + i)] cho i trong phạm vi (n)])
Gösta Forsum

0

Thử phần của tôi, đơn giản, một lớp lót, cách pythonic bằng cách sử dụng islice. Nhưng, có thể không hiệu quả tối ưu.

from itertools import islice
array = range(0, 10)
window_size = 4
map(lambda i: list(islice(array, i, i + window_size)), range(0, len(array) - window_size + 1))
# output = [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7], [5, 6, 7, 8], [6, 7, 8, 9]]

Giải thích: Tạo cửa sổ bằng cách sử dụng islice của window_size và lặp lại thao tác này bằng cách sử dụng bản đồ trên tất cả các mảng.


0

Chức năng tối ưu hóa cho dữ liệu cửa sổ trượt trong Deep learning

def SlidingWindow(X, window_length, stride):
    indexer = np.arange(window_length)[None, :] + stride*np.arange(int(len(X)/stride)-window_length+4)[:, None]
    return X.take(indexer)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.