2017-08-30 2 views
-2

Étant donné un tableau de 2x3, je veux calculer la moyenne sur axis=0, mais seulement compte tenu des valeurs qui sont supérieures à 0.moyenne conditionnelle avec numpy

Donc, étant donné le tableau

[ [1,0], 
    [0,0], 
    [1,0] ] 

Je veux la sortie être

# 1, 0, 1 filtered for > 0 gives 1, 1, average = (1+1)/2 = 1 
# 0, 0, 0 filtered for > 0 gives 0, 0, 0, average = 0  
[1 0] 

Mon code actuel est

import numpy as np 

frame = np.array([ [1,0], 
        [0,0], 
        [1,0] ]) 

weights=np.array(frame)>0 

print("weights:") 
print(weights) 

print("average without weights:") 
print((np.average(frame, axis=0))) 

print("average with weights:") 
print((np.average(frame, axis=0, weights=weights))) 

Cela me donne

weights: 
[[ True False] 
[False False] 
[ True False]] 
average without weights: 
[ 0.66666667 0.  ] 
average with weights: 
Traceback (most recent call last): 
File "C:\Users\myuser\project\test.py", line 123, in <module> 
print((np.average(frame, axis=0, weights=weights))) 
File "C:\Users\myuser\Miniconda3\envs\myenv\lib\site-packages\numpy\lib\function_base.py", line 1140, in average 
"Weights sum to zero, can't be normalized") 
ZeroDivisionError: Weights sum to zero, can't be normalized 

Je ne comprends pas cette erreur. Qu'est-ce que je fais de mal et comment puis-je obtenir la moyenne pour toutes les valeurs supérieures à zéro le long de axis=0? Merci!

+1

'0, 0, 0 filtré pour> 0 donne 0, 0, 0 '... Non, il ne fonctionne pas. Pouvez-vous donner une description plus précise de la façon dont vous voulez gérer le cas où aucun élément positif n'est trouvé? Est-ce que le résultat doit toujours être 0? Le résultat devrait-il être la moyenne de tous les éléments? Devrait-on imputer une autre valeur? – user2357112

+0

Une moyenne pondérée est calculée comme la somme des produits des nombres et poids moyens, divisée par la somme des poids. Puisque vos poids pour la deuxième colonne totalisent 0 (les trois sont «Faux»), la division n'est pas possible. – DyZ

+0

Et les commentaires sur la solution affichée? – Divakar

Répondre

0

Vous pouvez obtenir un masque supérieur à zéro et l'utiliser pour effectuer une multilication élémentaire et une réduction de somme le long du premier axe. Enfin, divisez par le nombre d'éléments masqués le long du premier axe pour obtenir les valeurs moyennes.

Ainsi, une solution serait -

mask = a > 0 # Input array : a 
out = np.einsum('i...,i...->...',a,mask)/mask.sum(0) 

run Exemple -

In [52]: a 
Out[52]: 
array([[ 3, -3, 3], 
     [ 2, 2, 0], 
     [ 0, -3, 1], 
     [ 0, 1, 1]]) 

In [53]: mask = a > 0 

In [56]: np.einsum('i...,i...->...',a,mask) # summations of > 0s 
Out[56]: array([5, 3, 5]) 

In [57]: np.einsum('i...,i...->...',a,mask)/mask.sum(0) # avg values of >0s 
Out[57]: array([ 2.5  , 1.5  , 1.66666667]) 

Pour tenir compte de toutes les colonnes zéro, il semble que nous nous attendons à 0 que le résultat. Ainsi, nous pouvons utiliser np.where pour faire le choix, comme si -

In [61]: a[:,-1] = 0 

In [62]: a 
Out[62]: 
array([[ 3, -3, 0], 
     [ 2, 2, 0], 
     [ 0, -3, 0], 
     [ 0, 1, 0]]) 

In [63]: mask = a > 0 

In [65]: np.where(mask.any(0), np.einsum('i...,i...->...',a,mask)/mask.sum(0), 0) 
__main__:1: RuntimeWarning: invalid value encountered in true_divide 
Out[65]: array([ 2.5, 1.5, 0. ]) 

simplement ignorer l'avertissement là.

Si vous vous sentez paranoïaque sur les avertissements, utilisez masking -

mask = a > 0 
vm = mask.any(0) # valid mask 
out = np.zeros(a.shape[1]) 
out[vm] = np.einsum('ij,ij->j',a[:,vm],mask[:,vm])/mask.sum(0)[vm] 
+0

Bien sûr, vous divisez toujours par zéro dans le cas des éléments non positifs, donc vous obtiendrez une moyenne de NaN si cela se produit. Le problème conceptuel sous-jacent d'essayer de prendre la moyenne d'aucun nombre est toujours là. – user2357112

+0

@ user2357112 Bon point! Edité pour couvrir un tel cas. – Divakar