4
votes

Comment sélectionner une fenêtre glissante d'éléments dans une liste de listes?

Disons que j'ai la liste de listes suivante:

    [[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`

Et je souhaite sélectionner toutes les 'fenêtres' de taille par exemple n = 4 , décalé d'une distance de par ex. d=2:

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

C'est-à-dire Je souhaite prendre des «tranches» qui se croisent là où les fenêtres se chevauchent avec les sous-listes.

Comment procéder?


0 commentaires

3 Réponses :


1
votes

J'opterais pour des boucles for imbriquées, même si ce n'est pas joli:

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))

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


0 commentaires

1
votes

Une autre approche peut être d'utiliser réellement la liste plate, d'obtenir la bonne fenêtre, mais de la réparer par la suite. J'ai cédé et j'ai utilisé un peu de numpy à la fin, ce qui facilite la correction.

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

Résultat:

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)


0 commentaires

3
votes

Si vous précalculez certains indices, vous pouvez reconstruire n'importe quelle fenêtre avec un one-liner virtuel:

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

Mise à jour:

Version optimisée copiant des sous-listes entières si possible. start et stop doivent être dans la plage valide

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


0 commentaires