webentwicklung-frage-antwort-db.com.de

Berechnen Sie den Mittelwert und die Standardabweichung aus einem Vektor von Stichproben in C ++ mit Boost

Gibt es eine Möglichkeit, den Mittelwert und die Standardabweichung für einen Vektor, der Stichproben enthält, mit Boost zu berechnen?

Oder muss ich einen Akkumulator erstellen und den Vektor einspeisen?

82
user393144

Die Verwendung von Akkumulatoren ist die Methode, um Mittelwerte und Standardabweichungen in Boost zu berechnen.

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

Ich weiß nicht, ob Boost spezifischere Funktionen hat, aber Sie können dies mit der Standardbibliothek tun.

Bei std::vector<double> v Ist dies der naive Weg:

#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);

Dies ist anfällig für Über- oder Unterlauf bei großen oder kleinen Werten. Eine etwas bessere Methode zur Berechnung der Standardabweichung ist:

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 für C++ 11:

Der Aufruf von std::transform Kann mit einer Lambda-Funktion anstelle von std::minus Und std::bind2nd (Jetzt veraltet) geschrieben werden:

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

Wenn Leistung für Sie wichtig ist und Ihr Compiler Lambdas unterstützt, kann die stdev-Berechnung schneller und einfacher durchgeführt werden: In Tests mit VS 2012 habe ich festgestellt, dass der folgende Code über 10-mal schneller ist als der in der ausgewählten Antwort angegebene Boost-Code ; Es ist außerdem 5-mal schneller als die sicherere Version der Antwort, wenn Standardbibliotheken von musiphil verwendet werden.

Hinweis: Ich verwende eine Standardabweichung für die Stichprobe, daher liefert der folgende Code leicht abweichende Ergebnisse ( Warum gibt es eine Minus Eins in Standardabweichungen )

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

Indem Sie die Antwort von musiphil verbessern, können Sie eine Standardabweichungsfunktion ohne den temporären Vektor diff schreiben, indem Sie nur einen einzelnen inner_product Aufruf mit den C++ 11 Lambda-Fähigkeiten:

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 ));
}

Ich vermute, dass die mehrfache Subtraktion billiger ist als die Verwendung von zusätzlichem Zwischenspeicher, und ich denke, dass dies besser lesbar ist, aber ich habe die Leistung noch nicht getestet.

2
codeling

2x schneller als die zuvor genannten Versionen - hauptsächlich weil transform () und inner_product () Schleifen verbunden sind. Entschuldigung für meine Verknüpfung/typedefs/macro: Flo = float. CR const ref. VFlo - Vektor. Getestet in 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

Meine Antwort ist ähnlich wie Josh Greifer, aber verallgemeinert auf die Kovarianz. Die Stichprobenvarianz ist nur eine Stichproben-Kovarianz, wobei jedoch die beiden Eingänge identisch sind. Dies schließt die Besselsche Korrelation ein.

    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

Es scheint, dass die folgende elegante rekursive Lösung nicht erwähnt wurde, obwohl es sie schon lange gibt. Unter Bezugnahme auf Knuths Kunst der Computerprogrammierung,

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

dann für eine Liste von n>=2 Werte, die Schätzung der Standardabweichung ist:

std = variance_n / (n-1). 

Hoffe das hilft!

0
galactica