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?
3 Réponses :
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
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)
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]]