2010-02-26 4 views
15

J'ai un itérateur de chiffres, par exemple un objet fichier:calculer la moyenne et la variance avec une itération

f = open("datafile.dat") 

maintenant je veux calculer:

mean = get_mean(f) 
sigma = get_sigma(f, mean) 

Quelle est la meilleure mise en œuvre? Supposons que le fichier soit grand et que je veuille éviter de le lire deux fois.

+0

Voulez-vous éviter de lire deux fois le fichier ou éviter de répéter deux fois? – truppo

+0

Je ne pense pas que vous nous montrez le code complet. Vous passez un fichier 'get_mean()' où 'fsum()' accepte uniquement les listes de nombres. –

+0

Pourquoi avez-vous besoin de placer une restriction sur la modification de la fonction? Si vous avez réarrangé la formule pour la variance, je pense que vous pouvez obtenir quelque chose comme sqrt (1/(n-1) * (somme (li ** 2 pour li en l) + n * mm * somme (li pour li en l) + n * mm)) où n est len ​​(l). Autrement dit, si mes calculs sont corrects. Ensuite, vous pouvez répéter une fois, en calculant les termes de la somme dans le refactoring ci-dessus et la moyenne en même temps. – chradcliffe

Répondre

11

Si vous voulez itérer une fois, vous pouvez écrire votre fonction somme:

def mysum(l): 
    s2 = 0 
    s = 0 
    for e in l: 
     s += e 
     s2 += e * e 
    return (s, s2) 

et utiliser le résultat dans votre fonction sigma.

Modifier: maintenant, vous pouvez calculer la variance comme ceci: (s2 - (s * s)/N)/N

En tenant compte du commentaire de @Adam Bowen,
garder à l'esprit que si nous utilisons mathématique astuces et transformer les formules originales
nous pouvons dégrader les résultats.

+13

Avec cette solution, la moyenne est 's/n' et la variance est' s2/n - mean * mean' c'est-à-dire, la moyenne des carrés moins le carré de la moyenne. Cependant, vous devez être conscient que le calcul de la variance de cette façon peut être inexact pour n grand en raison de la différence d'échelle entre s2 et e * e pendant l'accumulation. Malheureusement, cela signifie que pour un n grand l'algorithme à deux passages est beaucoup plus précis (et un meilleur choix). –

+0

@Adam Bowen, merci. J'ai oublié de mentionner cela. –

+3

Cette réponse est référencée dans [PEP 450] (http://www.python.org/dev/peps/pep-0450/) comme étant un conseil pour une approche naïve du calcul de la variance avec des caractéristiques de stabilité et de précision médiocres. Voir pour comparaison les fonctions de variance dans le module proposé de Python 3.4+ ['statistics'] (http://hg.python.org/cpython/file/tip/Lib/statistics.py). – badp

2

Créer une liste à partir de l'itérable ou utiliser itertools.tee().

+1

mais le fichier entier ne doit-il pas être conservé en mémoire? parce que get_sigma a besoin de l'entrée de get_mean, dans ce cas pourquoi ne pas simplement charger le fichier entier en mémoire –

+0

maintenant je sais enfin comment je peux faire du codeblock avec un lien –

+0

't1, t2 = tee (...)' ne vaut pas le coup si vous voulez consommer tout 't1 'tout d'abord et tout' t2' plus tard.Dans ce cas, il suffit d'utiliser 'list (seq)' et d'itérer sur – Kos

1

Je ne suis pas sûr qu'il y ait beaucoup de choix.

Dans tous les cas, vous devrez itérer deux fois, car l'écart type nécessitera l'information moyenne sur chaque valeur.

Si vous avez assez de mémoire, vous pouvez gagner sur l'accès E/S en chargeant votre fichier en mémoire lors de la première itération mais c'est à propos de cela IMO.

+0

Ceci est faux, selon les articles de Wikipedia cités ci-dessous ... – Mapio

0

Vous avez deux solutions

  1. Faites une liste de votre iterator et en boucle autant de fois que vous le souhaitez. L'inconvénient est que tout sera en mémoire, donc pas adapté si votre fichier est grand. Utilisation simple de itertools.tee aussi vous ne sauvera pas

  2. Il n'y a pas d'autre solution, à moins , vous n'avez pas besoin de passer la sortie de get_mean à get_sigma, parce que dans ce cas, ils ne peuvent être en série, mais si vous supprimez cette restriction, vous pouvez exécuter les deux fonctions en utilisant des fils parallèles et utiliser itertools.tee d'avoir deux itérateurs d'un

5

Je pense que Nick D a la bonne réponse. Si vous souhaitez calculer la moyenne et la variance dans un balayage du fichier (et si vous ne voulez pas vraiment que deux fonctions soient appelées l'une après l'autre), vous pouvez collecter la somme des valeurs et des valeurs. leurs carrés et ils utilisent de telles sommes (avec le nombre d'éléments lus) pour calculer à la fois la moyenne et la variance.

Il y a quelques problèmes de stabilité numérique, mais l'idée dans

http://en.wikipedia.org/wiki/Computational_formula_for_the_variance

est l'ingrédient de base dont vous avez besoin. Quelques détails sont à

http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance

où je vous suggère de lire le « algorithme Naïf ».

Hope this helps,

Massimo

1

Comme je pense qu'il ya de bons éléments dispersés dans plusieurs réponses, je voudrais résumer:

  • Si votre fichier est trop grand pour si vous voulez une bonne précision dans la variance, vous devez lire le fichier deux fois (en un passage, la variance est la différence entre deux grands nombres, ce qui n'est pas précis en raison des limitations du nombre à virgule flottante). Notez que votre système d'exploitation est susceptible de fournir une accélération automatique pour la lecture du second fichier, car il peut encore être en RAM pendant la deuxième passe. Si vous ne tenez pas compte de la précision de la variance, vous pouvez simplement parcourir une fois le fichier et calculer les quantités suggérées par Nick D, avec les détails fournis dans le commentaire d'Adam Bowen.

0

Vous pouvez utiliser la carte réduire d'une manière élégante de la mode

échantillon est la liste que vous voulez obtenir sa variance

échantillon = [a, b, c, .. .]

mean = float(reduce(lambda x,y : x+y, sample))/len(sample) 

variance = reduce(lambda x,y: x+y, map(lambda xi: (xi-mean)**2, sample))/ len(sample) 

Dans une ligne succincte du code:

variance = reduce(lambda x,y: x+y, map(lambda xi: (xi-(float(reduce(lambda x,y : x+y, sample))/len(sample)))**2, sample))/ len(sample) 
+0

pour obtenir la moyenne, vous n'avez pas besoin de tout cela: vous pouvez simplement «somme (échantillon)/len (échantillon)» et similaires pour la variance. Le gros point ici, vous itérez deux fois sur mon * gros * fichier. Ma question a demandé * une itération * –

+0

oups! - oui, vous avez raison. Mais c'est un bon prétexte pour utiliser les fonctions lambda =) – Juan

Questions connexes