Как выбрать скользящее окно элементов из списка списков? - PullRequest
4 голосов
/ 13 мая 2019

Скажите, у меня есть следующий список списков:

x = [[1,2,3],[4,5,6],[7,8,9,10]]

И я хочу выбрать все «окна» размера, например n=4, пораженный расстоянием, например, d=2

    [[1,2,3],[4]]                # Starts at position `0`
        [[3],[4,5,6]]            # Starts at position `d`
              [[5,6],[7,8]]      # Starts at position `2d`
                    [[7,8,9,10]] # Starts at position `3d`

т.е. Я хочу взять пересекающиеся «кусочки», где окна перекрываются с подсписками.

Как бы я поступил об этом?

Ответы [ 3 ]

3 голосов
/ 13 мая 2019

Если вы предварительно вычислили некоторые индексы, вы можете реконструировать любое окно с помощью виртуального однострочного:

import itertools
import operator

n=4; d=2
x = [[1,2,3],[4,5,6],[7,8,9,10]]

indices = [(i, j) for i, sublist in enumerate(x) for j in range(len(sublist))]

def window(x, start, stop, indices):
    return [
        [x[i][j] for i, j in g]
        for _, g in itertools.groupby(
            indices[start:stop],
            key=operator.itemgetter(0))
        ]

def flat_len(x):
    """Return length of flattened list."""
    return sum(len(sublist) for sublist in x)

for i in range(0,flat_len(x)-n+1,d):
    print(window(x,i,i+n,indices))

# Output:
# [[1, 2, 3], [4]]
# [[3], [4, 5, 6]]
# [[5, 6], [7, 8]]

Обновление:

Оптимизированная версия, если возможно копирование целых подсписков. start и stop должны находиться в допустимом диапазоне

def window(x, start, stop):
    first = indices[start][0]
    last = indices[stop-1][0]
    return [
        [x[i][j] for i, j in g] if k in (first, last) else x[k]
        for k, g in itertools.groupby(
            indices[start:stop],
            key=operator.itemgetter(0))
        ]
1 голос
/ 13 мая 2019

Другой подход может состоять в том, чтобы фактически использовать плоский список, получить правильное окно, но потом исправить его.Я сдался и использовал немного тупица в конце, облегчая фиксацию.

x = [[1,2,3],[4,5,6],[7,8,9,10]]
from itertools import chain
import numpy as np
n = 4
d = 2
def custom_slider(x, n, d):
    x_shape = [len(l) for l in x]
    x_cumsum_shape = np.cumsum(x_shape) #this will come in handy for fixing slices later
    x_flat = list(chain.from_iterable(x))
    result = []
    for i in range(0, len(x_flat) - n + 1, d):
        #essentially get slice points, using the current index i to start. ignore negative or zero slices
        split_pts = (x_cumsum_shape - i)[x_cumsum_shape - i > 0] 

        #[i: i + n] gives the correct slice. use split points to correctly mimic original arrays
        temp = [list(item) for item in np.split(x_flat[i: i + n], split_pts) if item.size]

        result.append(temp) #could also turn function into generator by yielding instead
    return result

custom_slider(x, n, d)

Вывод:

[[[1, 2, 3], [4]], [[3], [4, 5, 6]], [[5, 6], [7, 8]], [[7, 8, 9, 10]]]
1 голос
/ 13 мая 2019

Я бы пошел на вложенные циклы for, хотя это не красиво:

x = [[1,2,3],[4,5,6],[7,8,9,10]]

def window(x, n, offset):
    pos = 0
    res = []
    for l in x:
        # Skip `l` if offset is larger than its length 
        if len(l) + pos <= offset:
            pos += len(l)
            continue
        # Stop iterating when window is complete
        elif pos >= n + offset:
            break

        tmp = []
        for el in l:
            #if `el` is in window, append it to `tmp`
            if offset <= pos < n + offset:
                tmp.append(el)
            # Stop iterating when window is complete
            elif pos >= n + offset:
                break
            pos += 1
        res.append(tmp)
    return res

def flat_len(x):
    """Return length of flattened list."""
    return sum(len(sublist) for sublist in x)

n = 4
d = 2

for i in range(0, flat_len(x) - n + 1, d):
    print(window(x, n, i))
...