web-dev-qa-db-fra.com

Calculer la moyenne et l'écart type à partir d'un vecteur d'échantillons en C ++ à l'aide de Boost

Existe-t-il un moyen de calculer la moyenne et l'écart type d'un vecteur contenant des échantillons à l'aide de Boost ?

Ou dois-je créer un accumulateur et y insérer le vecteur?

82
user393144

Utiliser des accumulateurs est la manière de calculer les moyennes et les écarts-types en Boost .

accumulator_set<double, stats<tag::variance> > acc;
for_each(a_vec.begin(), a_vec.end(), bind<void>(ref(acc), _1));

cout << mean(acc) << endl;
cout << sqrt(variance(acc)) << endl;

49
David Nehme

Je ne sais pas si Boost a des fonctions plus spécifiques, mais vous pouvez le faire avec la bibliothèque standard.

Donné std::vector<double> v, c'est la manière naïve:

#include <numeric>

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 déborder ou de déborder pour des valeurs énormes ou infimes. Une façon 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<double> diff(v.size());
std::transform(v.begin(), v.end(), diff.begin(),
               std::bind2nd(std::minus<double>(), mean));
double sq_sum = std::inner_product(diff.begin(), diff.end(), diff.begin(), 0.0);
double stdev = std::sqrt(sq_sum / v.size());

UPDATE pour C++ 11:

L'appel à std::transform peut être écrit en utilisant une fonction lambda au lieu de std::minus et std::bind2nd (maintenant obsolète):

std::transform(v.begin(), v.end(), diff.begin(), [mean](double x) { return x - mean; });
198
musiphil

Si les performances sont importantes pour vous et que votre compilateur prend en charge lambdas, le calcul de stdev peut être effectué plus rapidement et plus simplement: lors des tests avec VS 2012, j'ai constaté que le code suivant est plus de 10 fois plus rapide que le code Boost indiqué dans la réponse choisie. ; il est également 5 fois plus rapide que la version plus sûre de la réponse utilisant des bibliothèques standard fournies par musiphil.

Remarque J'utilise un exemple d'écart-type. Le code ci-dessous donne donc 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));
61
Josh Greifer

En améliorant le la réponse de musiphil , vous pouvez écrire une fonction de déviation standard sans le vecteur temporaire diff, en utilisant simplement un seul inner_product appel avec les capacités lambda C++ 11:

double stddev(std::vector<double> 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 suppose que faire la soustraction plusieurs fois revient moins cher que d’utiliser une mémoire intermédiaire supplémentaire, ce qui est plus lisible, mais je n’ai pas encore testé la performance.

2
codeling

2x plus rapide que les versions précédentes mentionnées - principalement parce que les boucles de transformation () et inner_product () sont jointes. Désolé pour mon raccourci/typedefs/macro: Flo = float. CR const réf. VFlo - vecteur. Testé en VS2010

#define fe(EL, CONTAINER)   for each (auto EL in CONTAINER)  //VS2010
Flo stdDev(VFlo CR crVec) {
    SZ  n = crVec.size();               if (n < 2) return 0.0f;
    Flo fSqSum = 0.0f, fSum = 0.0f;
    fe(f, crVec) fSqSum += f * f;       // EDIT: was Cit(VFlo, crVec) {
    fe(f, crVec) fSum   += f;
    Flo fSumSq      = fSum * fSum;
    Flo fSumSqDivN  = fSumSq / n;
    Flo fSubSqSum   = fSqSum - fSumSqDivN;
    Flo fPreSqrt    = fSubSqSum / (n - 1);
    return sqrt(fPreSqrt);
}
1
slyy2048

Ma réponse est similaire à celle de Josh Greifer mais généralisée pour échantillonner la covariance. La variance d'échantillon est juste une covariance d'échantillon, mais avec les deux entrées identiques. Cela inclut la corrélation de Bessel.

    template <class Iter> typename Iter::value_type cov(const Iter &x, const Iter &y)
    {
        double sum_x = std::accumulate(std::begin(x), std::end(x), 0.0);
        double sum_y = std::accumulate(std::begin(y), std::end(y), 0.0);

        double mx =  sum_x / x.size();
        double my =  sum_y / y.size();

        double accum = 0.0;

        for (auto i = 0; i < x.size(); i++)
        {
            accum += (x.at(i) - mx) * (y.at(i) - my);
        }

        return accum / (x.size() - 1);
    }
1
SmallChess

Il semble que la solution récursive élégante suivante n’a pas été mentionnée, bien qu’elle existe depuis longtemps. Se référant à l'art de la programmation informatique de Knuth,

mean_1 = x_1, variance_1 = 0;            //initial conditions; Edge case;

//for 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);

puis pour une liste de n>=2 valeurs, l’estimation de l’écart type est:

std = variance_n / (n-1). 

J'espère que cela t'aides!

0
galactica