Y a-t-il un moyen de calculer la moyenne et l'écart-type pour un vecteur contenant des échantillons en utilisant Boost ?
Ou dois-je créer un accumulateur et y faire passer le vecteur ?
Y a-t-il un moyen de calculer la moyenne et l'écart-type pour un vecteur contenant des échantillons en utilisant Boost ?
Ou dois-je créer un accumulateur et y faire passer le vecteur ?
Je ne sais pas si Boost propose des fonctions plus spécifiques, mais vous pouvez le faire avec la bibliothèque standard.
Étant donné std::vector v
, voici la manière naïve de le faire :
#include
double sum = std::accumulate(v.begin(), v.end(), 0.0);
double mean = sum / v.size();
double sq_sum = std::inner_product(v.begin(), v.end(), v.begin(), 0.0);
double stdev = std::sqrt(sq_sum / v.size() - mean * mean);
Ceci est susceptible de provoquer un dépassement ou un sous-dépassement pour des valeurs énormes ou très petites. Une manière légèrement meilleure de calculer l'écart type est la suivante :
double sum = std::accumulate(v.begin(), v.end(), 0.0);
double mean = sum / v.size();
std::vector diff(v.size());
std::transform(v.begin(), v.end(), diff.begin(),
std::bind2nd(std::minus(), mean));
double sq_sum = std::inner_product(diff.begin(), diff.end(), diff.begin(), 0.0);
double stdev = std::sqrt(sq_sum / v.size());
MISE À JOUR pour C++11 :
L'appel à std::transform
peut être écrit en utilisant une fonction lambda au lieu de std::minus
et std::bind2nd
(désormais obsolètes) :
std::transform(v.begin(), v.end(), diff.begin(), [mean](double x) { return x - mean; });
Le bas de la section de code dépend-il du haut? C'est-à-dire, est-ce que le "mean" dans la deuxième section est le même que le "mean" défini dans la première section?
Oui ; évidemment, la partie basse dépend de la valeur de mean
calculée dans la partie haute.
Le premier ensemble d'équations ne fonctionne pas. J'ai mis int 10 & 2, et j'ai obtenu une sortie de 4. À première vue, je pense que c'est parce qu'il suppose que (a-b)^2 = a^2-b^2
Si la performance est importante pour vous et que votre compilateur prend en charge les lambdas, le calcul de l'écart type peut être plus rapide et plus simple: dans des tests avec VS 2012, j'ai trouvé que le code suivant est plus de 10 fois plus rapide que le code Boost donné dans la réponse choisie ; il est aussi 5 fois plus rapide que la version plus sûre de la réponse utilisant les bibliothèques standard donnée par musiphil.
Note : j'utilise l'écart type d'échantillon, donc le code ci-dessous donne des résultats légèrement différents (Pourquoi il y a un moins un dans les écarts types)
double sum = std::accumulate(std::begin(v), std::end(v), 0.0);
double m = sum / v.size();
double accum = 0.0;
std::for_each(std::begin(v), std::end(v), [&](const double d) {
accum += (d - m) * (d - m);
});
double stdev = sqrt(accum / (v.size() - 1));
Merci d'avoir partagé cette réponse même un an plus tard. Maintenant, je reviens un an plus tard et j'ai rendu celle-ci générique pour les types de valeurs et de conteneurs. Voir ici (Remarque : je suppose que ma boucle basée sur une plage est aussi rapide que votre code lambda.)
La fonction std::end()
a été ajoutée par la norme C++11 pour les cas où il n'y a rien de tel que v.end()
. La std::end
peut être surchargée pour le conteneur moins standard -- voir en.cppreference.com/w/cpp/iterator/end
Utiliser des accumulateurs est la façon de calculer les moyennes et les écarts-types dans Boost.
accumulator_set > acc;
for_each(a_vec.begin(), a_vec.end(), bind(ref(acc), _1));
cout << mean(acc) << endl;
cout << sqrt(variance(acc)) << endl;
Notez que la balise variance calcule la variance selon une formule approximative. La balise variance (paresseuse) calcule selon une formule exacte, spécifiquement : deuxième moment - carré de la moyenne
, ce qui peut produire un résultat incorrect si la variance est très faible en raison d'erreurs d'arrondi. Il peut en fait produire une variance négative.
Amélioration de la réponse de musiphil, vous pouvez écrire une fonction d'écart type sans le vecteur temporaire diff
, en utilisant simplement un seul appel inner_product
avec les capacités de lambda de C++11 :
double stddev(std::vector const & func)
{
double mean = std::accumulate(func.begin(), func.end(), 0.0) / func.size();
double sq_sum = std::inner_product(func.begin(), func.end(), func.begin(), 0.0,
[](double const & x, double const & y) { return x + y; },
[mean](double const & x, double const & y) { return (x - mean)*(y - mean); });
return std::sqrt(sq_sum / func.size() - 1);
}
Je soupçonne que soustraire plusieurs fois est moins cher que d'utiliser un stockage intermédiaire supplémentaire, et je pense que c'est plus lisible, mais je n'ai pas encore testé les performances.
Quant à l'explication de pourquoi utiliser N-1 (comme dans func.size() - 1
), voir ces questions - notez comment la question stipule que nous avons un "vecteur contenant des échantillons".
Il semble que la solution récursive élégante suivante n'a pas été mentionnée, bien qu'elle existe depuis longtemps. En référence à l'Art of Computer Programming de Knuth,
mean_1 = x_1, variance_1 = 0; //conditions initiales; cas limite;
//pour k >= 2,
mean_k = mean_k-1 + (x_k - mean_k-1) / k;
variance_k = variance_k-1 + (x_k - mean_k-1) * (x_k - mean_k);
alors pour une liste de n>=2
valeurs, l'estimation de l'écart-type est:
stddev = std::sqrt(variance_n / (n-1)).
J'espère que cela aidera!
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
0 votes
Pour une solution en une seule passe, consultez stackoverflow.com/questions/7616511/…