fenêtre coulissante à numpy
j'ai un tableau numpy de la forme (6,2)
[[00,01],
[10,11],
[20,21],
[30,31],
[40,41],
[50,51]]
j'ai besoin d'une fenêtre coulissante avec step size 1 et la fenêtre size 3 aime ceci:
[[00,01,10,11,20,21],
[10,11,20,21,30,31],
[20,21,30,31,40,41],
[30,31,40,41,50,51]]
je cherche une solution toute nue. Si votre solution pouvait paramétrer la forme du tableau original ainsi que la taille de la fenêtre et la taille de pas, ce serait génial.
j'ai trouvé cette réponse à l'Aide de progrès pour l'efficacité de la moyenne mobile de filtre mais je ne vois pas comment spécifier le stepsize là et comment faire pour réduire la fenêtre de la 3d continue tableau 2d. Aussi ce Roulement ou de glissement de la fenêtre des itérateurs en Python mais C'est en Python et je ne sais pas si c'est efficace. En outre, il supporte les éléments mais ne les réunit pas à la fin si chaque élément a plusieurs fonctionnalités.
4 réponses
In [1]: import numpy as np
In [2]: a = np.array([[00,01], [10,11], [20,21], [30,31], [40,41], [50,51]])
In [3]: w = np.hstack((a[:-2],a[1:-1],a[2:]))
In [4]: w
Out[4]:
array([[ 0, 1, 10, 11, 20, 21],
[10, 11, 20, 21, 30, 31],
[20, 21, 30, 31, 40, 41],
[30, 31, 40, 41, 50, 51]])
Vous pourriez écrire comme une fonction de la manière suivante:
def window_stack(a, stepsize=1, width=3):
n = a.shape[0]
return np.hstack( a[i:1+n+i-width:stepsize] for i in range(0,width) )
cela ne dépend pas vraiment de la forme du tableau original, aussi longtemps que a.ndim = 2
. Notez que je n'utilise jamais ces longueurs dans la version interactive. La deuxième dimension de la forme n'est pas pertinente; chaque rangée peut être aussi longue que vous le voulez. Grâce à la suggestion de @Jaime, vous pouvez le faire sans vérifier la forme du tout:
def window_stack(a, stepsize=1, width=3):
return np.hstack( a[i:1+i-width or None:stepsize] for i in range(0,width) )
vous pouvez faire une fenêtre coulissante vectorisée dans numpy en utilisant l'indexation de fantaisie.
>>> import numpy as np
>>> a = np.array([[00,01], [10,11], [20,21], [30,31], [40,41], [50,51]])
>>> a
array([[ 0, 1],
[10, 11],
[20, 21], #define our 2d numpy array
[30, 31],
[40, 41],
[50, 51]])
>>> a = a.flatten()
>>> a
array([ 0, 1, 10, 11, 20, 21, 30, 31, 40, 41, 50, 51]) #flattened numpy array
>>> indexer = np.arange(6)[None, :] + 2*np.arange(4)[:, None]
>>> indexer
array([[ 0, 1, 2, 3, 4, 5],
[ 2, 3, 4, 5, 6, 7], #sliding window indices
[ 4, 5, 6, 7, 8, 9],
[ 6, 7, 8, 9, 10, 11]])
>>> a[indexer]
array([[ 0, 1, 10, 11, 20, 21],
[10, 11, 20, 21, 30, 31], #values of a over sliding window
[20, 21, 30, 31, 40, 41],
[30, 31, 40, 41, 50, 51]])
>>> np.sum(a[indexer], axis=1)
array([ 63, 123, 183, 243]) #sum of values in 'a' under the sliding window.
explication de ce que fait ce code.
np.arange(6)[None, :]
crée un vecteur de ligne 0 à 6, et np.arange(4)[:, None]
crée un vecteur de colonne 0 à 4. Il en résulte un 4x6 matrice où chaque ligne (six d'entre eux) représente une fenêtre, et le nombre de lignes (quatre) représente le nombre de fenêtres. Les multiples de 2 rend la fenêtre coulissante slide 2 unités à un moment qui est nécessaire pour glisser sur chaque tuple. En utilisant le découpage de tableau de numpy vous pouvez passer la fenêtre coulissante dans le tableau de numpy aplati et faire des agrégats sur eux comme la somme.
l'avantage de la méthode suivante est que sa complexité temporelle est O (1). Fonctionne pour toutes les tailles de données
utiliser les pas est devrait être intuitif lorsque vous commencez à penser en termes de pointeurs/adresses.
as_strided()
la méthode a 3 arguments.
- forme
- pas à pas
est le réseau sur lequel nous opérerions.
utiliser as_strided()
pour la mise en œuvre des fonctions de fenêtre coulissante, nous devons calculer la forme de la sortie avant main. Dans la question, (4,6) est la forme de sortie. Si les dimensions ne sont pas correctes, nous finissons par lire les valeurs des ordures. C'est parce que nous accédons aux données en déplaçant le pointeur de quelques octets (selon le type de données).
Détermination de la valeur correcte de pas à pas est essentiel pour obtenir les résultats attendus.
Avant de calculer les pas, découvrez la mémoire occupée par chaque élément. Nous pouvons obtenir ce à l'aide de arr.strides[-1]
. Dans cet exemple, la mémoire occupée par un élément est de 4 octets.
Les tableaux Numpy sont créés dans la ligne de la mode majeure. Donc, le premier élément de la ligne suivante juste à côté du dernier élément de la ligne actuelle.
Ex: 0, 1 | 10, 11/...
10 est juste à côté de 1.
imaginez le tableau 2D remodelé en 1D (ceci est acceptable car les données sont stockées dans un format ligne-majeur). Le premier élément de chaque ligne dans la sortie est le élément indexé Impair dans le tableau 1D. 0, 10, 20, 30, ..
donc, le nombre d'étapes dans la mémoire que nous devons prendre pour passer de 0 à 10, 10 à 20, ainsi de suite est 2 * mem taille de l'élément. Ainsi, chaque ligne a une foulée de 2 * 4octets = 8. Pour une ligne donnée dans la sortie, tous les éléments sont adjacents les uns aux autres dans notre imaginaire 1D tableau. Donc, pour obtenir l'élément suivant dans une ligne, il suffit de prendre un pas égal à la taille d'un élément. Donc, le pas de la colonne est de 4 octet.
par conséquent,strides=(8,4)
la solution est np.lib.stride_tricks.as_strided(a, shape=(4,6), strides=(8,4))
.
Une autre explication:
La sortie a une forme de (4,6). Colonne foulée 4
. Ainsi, les éléments de la première rangée commencent par index 0
et ont 6 éléments espacés chacun de 4 octets.
Après que la première ligne est collectée, la deuxième ligne commence à 8 octets du début de la ligne courante. La troisième rangée commence à 8 octets du point de départ de la deuxième rangée et ainsi sur.
la forme détermine le nombre de lignes et de colonnes dont nous avons besoin. foulées définir la mémoire pour le démarrage d'une ligne et de recueillir un élément de colonne
une compréhension de liste courte est possible avec more_itertools.windowed
1:
donnée
import numpy as np
import more_itertools as mit
a = [["00","01"],
["10","11"],
["20","21"],
["30","31"],
["40","41"],
["50","51"]]
Code
np.array([list(mit.flatten(w)) for w in mit.windowed(a, n=3)])
ou
np.array([[i for item in w for i in item] for w in mit.windowed(a, n=3)])
Sortie
array([['00', '01', '10', '11', '20', '21'],
['10', '11', '20', '21', '30', '31'],
['20', '21', '30', '31', '40', '41'],
['30', '31', '40', '41', '50', '51']],
dtype='<U2')
fenêtres Coulissantes de taille n=3
sont créés et aplatis. Remarque par défaut, le pas est more_itertools.windowed(..., step=1)
.
Performances
Cette approche est comparable rapide pour la accepté de répondre, sinon plus.
%timeit np.hstack((a[:-2], a[1:-1], a[2:]))
# 10000 loops, best of 3: 47.8 µs per loop
%timeit np.array([[i for item in w for i in item] for w in mit.windowed(a, n=3)])
# 10000 loops, best of 3: 31.7 µs per loop
Un tiers de la bibliothèque qui implémente recettes itertool et de nombreux outils utiles.