2012-04-22 3 views
2

Pour une série de valeurs d'angle dans la gamme (-pi, pi), je fais un histogramme. Existe-t-il un moyen efficace de calculer une valeur moyenne et modale (post-probable)? Considérons les exemples suivants:statistiques pour l'histogramme des données périodiques

import numpy as N, cmath 
deg = N.pi/180. 
d = N.array([-175., 170, 175, 179, -179])*deg 
i = N.sum(N.exp(1j*d)) 
ave = cmath.phase(i) 
i /= float(d.size) 
stdev = -2. * N.log(N.sqrt(i.real**2 + i.imag**2)) 

print ave/deg, stdev/deg 

Maintenant, nous allons avoir un histogramme:

counts, bins = N.histogram(data, N.linspace(-N.pi, N.pi, 360)) 

Est-il possible de calculer la moyenne, le mode ayant compte et bacs? Pour les données non périodiques, le calcul d'une moyenne est simple:

ave = sum(counts*bins[:-1]) 

Le calcul d'une valeur modale nécessite plus d'efforts. En fait, je ne suis pas sûr que mon code ci-dessous est correcte: tout d'abord, j'identifie les bacs qui se produisent le plus souvent, puis-je calculer une moyenne arithmétique:

cmax = bins[N.argmax(counts)] 
mode = N.mean(N.take(bins, N.nonzero(counts == cmax)[0])) 

Je ne sais pas, comment calculer l'écart-type à partir de ces données , bien que. Une solution évidente à tous mes problèmes (au moins ceux décrits ci-dessus) est de convertir les données d'histogramme en une série de données et ensuite l'utiliser dans les calculs. Ce n'est pas élégant, cependant, et inefficace.

Tous les conseils seront très appréciés.


C'est la solution partielle que j'ai écrite.

import numpy as N, cmath 
import scipy.stats as ST 

d = [-175, 170.2, 175.57, 179, -179, 170.2, 175.57, 170.2] 
deg = N.pi/180. 
data = N.array(d)*deg 

i = N.sum(N.exp(1j*data)) 
ave = cmath.phase(i) # correct and exact mean for periodic data 
wrong_ave = N.mean(d) 

i /= float(data.size) 
stdev = -2. * N.log(N.sqrt(i.real**2 + i.imag**2)) 
wrong_stdev = N.std(d) 

bins = N.linspace(-N.pi, N.pi, 360) 
counts, bins = N.histogram(data, bins, normed=False) 
# consider it weighted vector addition 
nz = N.nonzero(counts)[0] 
weight = counts[nz] 
i = N.sum(weight * N.exp(1j*bins[nz])/len(nz)) 
pave = cmath.phase(i) # correct and approximated mean for periodic data 
i /= sum(weight)/float(len(nz)) 
pstdev = -2. * N.log(N.sqrt(i.real**2 + i.imag**2)) 
print 
print 'scipy: %12.3f (mean) %12.3f (stdev)' % (ST.circmean(data)/deg, \ 
               ST.circstd(data)/deg) 

Lorsqu'il est exécuté, il donne des résultats suivants:

mean:  175.840  85.843  175.360 
stdev:  0.472  151.785  0.430 

scipy:  175.840 (mean)  3.673 (stdev) 

Quelques commentaires maintenant: la première colonne moyenne/stdev calculé. Comme on peut le voir, la moyenne concorde bien avec scipy.stats.circmean (merci JoeKington pour l'avoir signalé). Malheureusement, stdev diffère. Je le regarderai plus tard. La deuxième colonne donne des résultats complètement faux (moyenne non-périodique/std de numpy évidemment ne fonctionne pas ici). La 3ème colonne donne sth je voulais obtenir à partir des données de l'histogramme (@JoeKington: mes données brutes ne correspondent pas à la mémoire de mon ordinateur .., @dmytro: merci pour votre contribution: bien sûr, la taille de la poubelle influencera le résultat mais dans mon application je n'ai pas beaucoup de choix, c'est à dire que je dois réduire les données en quelque sorte). Comme on peut le voir, la moyenne (3e colonne) est calculée correctement, stdev nécessite plus d'attention :)

+0

Si j'ai bien compris, vous voulez calculer la moyenne des données, le mode, std, etc. à partir des données de l'histogramme? Si c'est le cas, cela ne me semble pas possible, car vous perdez beaucoup d'informations en prenant l'histogramme des données. Tout ce que vous pouvez obtenir est une approximation qui s'aggrave avec des bacs plus larges. Ou est-ce ce que vous cherchez? – dmytro

+0

Jetez un oeil à la distribution de Von Mises: http://en.wikipedia.org/wiki/Von_Mises_distribution. Si vous voulez un livre, l'analyse statistique des données circulaires de Fisher est le manuel standard, et est généralement assez raisonnable. –

Répondre

1

Voici comment obtenir une approximation.

Depuis Var(x) = <x^2> - <x>^2, nous avons:

meanX = N.sum(counts * bins[:-1])/N.sum(counts) 
meanX2 = N.sum(counts * bins[:-1]**2)/N.sum(counts) 
std = N.sqrt(meanX2 - meanX**2) 
+0

Ceux-ci ne s'appliquent pas aux données circulaires, pour tout ce que ça vaut. La moyenne n'est pas simplement la moyenne :) (par exemple, 359 degrés et 0 degrés ne sont distants que de 1 degré) –

+0

@ JoeKington, assez juste. L'auteur, cependant, a mentionné des données non périodiques et semble être parfaitement bien avec sa 'somme (counts * bins [: - 1])', donc j'ai supposé que la question est plus sur l'estimation des moments de l'histogramme. – dmytro

+0

@dmytro: Ce que j'ai foiré dans ma question initiale, c'est la façon dont la moyenne des données non périodiques a été calculée (mon histogramme original est normalisé et c'est pourquoi j'ai négligé de diviser par la somme des comptes). En fait, dans mon code j'ai besoin des deux cas: c'est-à-dire que je dois gérer des données périodiques et non périodiques, ainsi votre solution pour le calcul de stdev est très appréciée. – krzym

5

Jetez un oeil à scipy.stats.circmean et scipy.stats.circstd. Ou avez-vous seulement l'histogramme compte, et pas les données "brutes"? Si tel est le cas, vous pouvez ajouter un Von Mises distribution au nombre d'histogrammes et approcher la moyenne et stddev de cette manière.

+0

Et si les données sont loin d'être distribuées normalement? – dmytro

+0

@JoeKington: merci d'avoir signalé scipy.stats. {Circmean, circstd}. La moyenne que je calcule est exactement la même que celle de circmean. Je vais regarder dans le code de circstd pour découvrir pourquoi mes résultats sont différents. Je suis également reconnaissant d'avoir porté mon attention sur la distribution de Von Mises. L'embout sur le raccord est également génial. En fait, avant que je ne trouve une solution partielle (voir edit) je frappe indépendamment sur une idée similaire et ça marche bien .. – krzym

+0

@dmytro: tu as raison, la distribution normale n'est pas une solution générale, dans mon cas j'ai ajusté p [ 0] * sin (a) exp (-0,5 (a/p [0]) ** 2) avec un bon résultat. Adapter ainsi * une fonction * aux données de l'histogramme peut être une solution dans certains cas. – krzym

Questions connexes