Calcul des moyennes locales dans un tableau 1D numpy

j'ai 1D tableau NumPy comme suit:

import numpy as np
d = np.array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20])

je veux calculer (1,2,6,7), (3,4,8,9), et ainsi de suite. Cela implique moyenne de 4 éléments: Deux éléments consécutifs et deux éléments consécutifs 5 positions après.

j'ai essayé le code suivant:

>> import scipy.ndimage.filters as filt
>> res = filt.uniform_filter(d,size=4)
>> print res
[ 1  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19]

cela ne me donne malheureusement pas les résultats souhaités. Comment puis-je le faire?

10
demandé sur Saullo G. P. Castro 2015-07-18 17:00:22

2 réponses

au lieu d'indexer, vous pouvez aborder cela avec une perspective de traitement de signal. Vous êtes essentiellement l'exécution d'une convolution discrète de votre signal d'entrée avec un noyau 7-tap où les trois coefficients centraux sont 0 alors que les extrémités sont 1, et puisque vous voulez calculer la moyenne, vous devez multiplier toutes les valeurs par (1/4). Cependant, vous ne calculez pas la convolution de tous les éléments, mais nous y reviendrons plus tard. Une façon est d'utiliser scipy.ndimage.filters.convolve1d pour:

import numpy as np
from scipy.ndimage import filters
d = np.arange(1, 21, dtype=np.float)
ker = (1.0/4.0)*np.array([1,1,0,0,0,1,1], dtype=np.float)
out = filters.convolve1d(d, ker)[3:-3:2]

parce que vous utilisez un noyau 7 tapez, convolution va étendre la sortie de 3 vers la gauche et de 3 vers la droite, donc vous devez vous assurer de recadrer le premier et les trois derniers éléments. Vous aussi vous voulez ignorer tous les autres éléments parce que convolution implique une fenêtre coulissante, mais vous voulez écarter tous les autres éléments de sorte que vous obtenez le résultat que vous voulez.

nous obtenons ceci pour out:

In [47]: out
Out[47]: array([  4.,   6.,   8.,  10.,  12.,  14.,  16.])

pour vérifier si nous avons le bon résultat, essayez quelques exemples de calculs pour chaque élément. Le premier élément est égal à (1+2+6+7)/4 = 4. Le deuxième élément est égal à (3+4+8+9)/4 = 6, et ainsi de suite.


Pour une solution avec moins de maux de tête, essayez numpy.convolvemode=valid drapeau. Cela évite la découpe du rembourrage supplémentaire à gauche et à droite, mais vous aurez encore besoin de sauter tous les autres éléments bien que:

import numpy as np
d = np.arange(1, 21, dtype=np.float)
ker = (1.0/4.0)*np.array([1,1,0,0,0,1,1], dtype=np.float)
out = np.convolve(d, ker, mode='valid')[::2]

Nous avons aussi:

In [59]: out
Out[59]: array([  4.,   6.,   8.,  10.,  12.,  14.,  16.])

enfin, si vous voulez indexer, quelque chose comme ceci peut suffire:

length = len(d[6::2])
out = np.array([(a+b+c+e)/4.0 for (a,b,c,e) in zip(d[::2][:length], d[1::2][:length], d[5::2][:length], d[6::2])])

On obtient:

In [69]: out
Out[69]: array([  4.,   6.,   8.,  10.,  12.,  14.,  16.])

C'est vraiment moche, mais ça marche. La longueur totale de votre signal est régie par le fait que la fin de chaque fenêtre est au 7ème index. La longueur de ce tableau qui contient ces indices dicte la longueur finale de votre signal. Notez également que, pour un élément dans un de la fenêtre, son prochain élément trouvé en ignorant tous les autres éléments jusqu'à la fin du tableau. Il y a 4 de ces séquences au total et nous avons simplement zip sur ces 4 séquences où chaque séquence saute tous les autres éléments, mais il y a un décalage que nous commençons par. La première séquence commence à l'offset 0, l'autre à 1, le suivant à 5 et l'autre à 6. Nous recueillons ces quatre éléments et de moyenne eux, puis sauter sur chacun dans le tableau jusqu'à ce que nous finissons.

BTW, j'ai encore comme la convolution de mieux.

11
répondu rayryeng 2017-02-01 06:43:18

Vous pouvez utiliser numpy.lib.stride_tricks.as_strided() pour obtenir un tableau de groupement applicable à un cas plus générique:

import numpy as np
from numpy.lib.stride_tricks import as_strided

d = np.arange(1, 21)

consec = 2
offset = 5
nsub = 2
pace = 2

s = d.strides[0]
ngroups= (d.shape[0] - (consec + (nsub-1)*offset - 1))//pace
a = as_strided(d, shape=(ngroups, nsub, consec),
               strides=(pace*s, offset*s, 1*s))

Où:

  • consec est le nombre de nombres consécutifs dans le sous-groupe
  • offset décalage entre la première entrée de chaque sous-groupe
  • nsub le nombre de sous-groupes (1, 2 est un sous-groupe, séparé de la deuxième sous-groupe 6, 7 par offset
  • pace indique la foulée entre l' la première entrée de deux groupes, qui dans votre cas est pace=consec, mais pourrait être différente dans un cas plus général

dans votre cas (en utilisant les valeurs données) a:

array([[[ 1,  2],
        [ 6,  7]],

       [[ 3,  4],
        [ 8,  9]],

       [[ 5,  6],
        [10, 11]],

       [[ 7,  8],
        [12, 13]],

       [[ 9, 10],
        [14, 15]],

       [[11, 12],
        [16, 17]],

       [[13, 14],
        [18, 19]]])

D'où il est tout à fait prêt pour obtenir la moyenne souhaitée en faisant:

a.mean(axis=-1).mean(axis=-1)

#array([  4.,   6.,   8.,  10.,  12.,  14.,  16.])
1
répondu Saullo G. P. Castro 2015-07-21 00:17:42