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


Antworten:


52

Die Verwendung von Akkumulatoren ist der Weg, 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;

 


5
Beachten Sie, dass tag :: Varianz die Varianz anhand einer ungefähren Formel berechnet. tag :: Varianz (faul) berechnet nach einer exakten Formel, insbesondere: second moment - squared meanDies führt zu einem falschen Ergebnis, wenn die Varianz aufgrund von Rundungsfehlern sehr gering ist. Es kann tatsächlich eine negative Varianz erzeugen.
Panda-34

Verwenden Sie den rekursiven (Online-) Algorithmus, wenn Sie wissen, dass Sie viele Zahlen haben werden. Dies behebt sowohl Unter- als auch Überlaufprobleme.
Kemin Zhou

214

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

Vorausgesetzt std::vector<double> v, dies ist 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 kann bei großen oder kleinen Werten zu Über- oder Unterlauf führen. 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::transformkann mit einer Lambda-Funktion anstelle von std::minusund std::bind2nd(jetzt veraltet) geschrieben werden:

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

1
Ja; Offensichtlich hängt der untere Teil vom Wert ab, der meanim oberen Teil berechnet wurde.
Musiphil

7
Der erste Satz von Gleichungen funktioniert nicht. Ich habe int 10 & 2 gesetzt und eine Ausgabe von 4 erhalten. Auf einen Blick denke ich, dass es b / c ist, dass (ab) ^ 2 = a ^ 2-b ^ 2
Charles L.

2
@ CharlesL.: Es sollte funktionieren und 4 ist die richtige Antwort.
Musiphil

3
@StudentT: Nein, aber Sie können ersetzen (v.size() - 1)für v.size()oben in der letzten Zeile: std::sqrt(sq_sum / (v.size() - 1)). (Für die erste Methode, es ist ein wenig kompliziert: std::sqrt(sq_sum / (v.size() - 1) - mean * mean * v.size() / (v.size() - 1)).
musiphil

5
Die Verwendung std::inner_productfür die Summe der Quadrate ist sehr ordentlich.
Paul R

65

Wenn Ihnen die Leistung 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 mehr als 10-mal schneller ist als der in der ausgewählten Antwort angegebene Boost-Code ;; Es ist auch 5-mal schneller als die sicherere Version der Antwort mit Standardbibliotheken von musiphil.

Hinweis: Ich verwende die Standardabweichung des Beispiels, daher liefert der folgende Code leicht unterschiedliche Ergebnisse ( Warum es in Standardabweichungen ein Minus eins gibt )

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

Vielen Dank, dass Sie diese Antwort auch ein Jahr später geteilt haben. Jetzt komme ich ein weiteres Jahr später und mache dieses generisch sowohl für den Werttyp als auch für den Containertyp. Siehe hier (Hinweis: Ich denke, dass meine
bereichsbasierte

2
Was ist der Unterschied zwischen der Verwendung von std :: end (v) anstelle von v.end ()?
Spurra

3
Die std::end()Funktion wurde vom C ++ 11-Standard für Fälle hinzugefügt, in denen es nichts Vergleichbares gibt v.end(). Die std::endkönnen für den weniger Standardcontainer überladen werden - siehe en.cppreference.com/w/cpp/iterator/end
pepr

Können Sie erklären, warum das schneller geht?
dev_nut

4
Zum einen durchläuft die "sichere" Antwort (die meiner Antwort entspricht) drei Durchgänge durch das Array: Einmal für die Summe, einmal für den Diff-Mittelwert und einmal für die Quadrierung. In meinem Code gibt es nur 2 Durchgänge - es werden die zweiten beiden Durchgänge zu einem zusammengeführt. Und (als ich das letzte Mal vor einiger Zeit nachgesehen habe!) Wurden die Aufrufe von inner_product nicht wegoptimiert. Außerdem kopiert der "sichere" Code v in ein völlig neues Array von Diffs, was die Verzögerung erhöht. Meiner Meinung nach ist mein Code auch besser lesbar - und lässt sich leicht auf JavaScript und andere Sprachen
Josh Greifer

5

Die Verbesserung des auf der Antwort von musiphil , können Sie eine Standardabweichung Funktion ohne den temporären Vektor schreiben diff, nur ein einziges mit inner_productGespräch mit dem C ++ 11 Lambda - Funktionen:

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 sie besser lesbar ist, aber ich habe die Leistung noch nicht getestet.


1
Ich denke, dies berechnet die Varianz, nicht die Standardabweichung.
sg_man

Die Standardabweichung wird berechnet, indem durch N und nicht durch N-1 dividiert wird. Warum teilen Sie die sq_sum durch func.size () - 1?
Pocjoc

Ich schätze, ich berechne die "korrigierte Standardabweichung" (siehe z. B. en.wikipedia.org/wiki/… )
Codierung

2

Es scheint, dass die folgende elegante rekursive Lösung nicht erwähnt wurde, obwohl es sie schon lange gibt. In Bezug 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);

Für eine Liste von n>=2Werten lautet die Schätzung der Standardabweichung:

stddev = std::sqrt(variance_n / (n-1)). 

Hoffe das hilft!


1

Meine Antwort ist ähnlich wie die von Josh Greifer, aber verallgemeinert, um die Kovarianz zu untersuchen. Die Stichprobenvarianz ist nur die Stichproben-Kovarianz, wobei die beiden Eingänge identisch sind. Dies schließt Bessels 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);
    }

0

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

Kann die Cit () - Schleife als geschrieben werden for( float f : crVec ) { fSqSum += f * f; fSum += f; } ?
Elfen Dew

1
Ja in C ++ 11. Der Versuch, Makros zu verwenden, die die Version unabhängig machen. Der Code wurde aktualisiert. PS. Aus Gründen der Lesbarkeit bevorzuge ich normalerweise 1 Aktion pro LOC. Der Compiler sollte erkennen, dass dies konstante Iterationen sind, und sie verbinden, wenn er "denkt", dass es schneller ist, einmal zu iterieren. Wenn Sie dies in kleinen kurzen Schritten tun (ohne z. B. std :: inner_product () zu verwenden), wird dem neuen Leser erklärt, was dies bedeutet. Binär wird durch Nebenwirkungen (in einigen Fällen) kleiner.
Slyy2048

"Der Versuch, Makros zu verwenden, die die Version unabhängig machen" - Sie beschränken sich jedoch auf das nicht standardmäßige Visual C ++ "für jedes" Konstrukt ( stackoverflow.com/questions/197375/… )
Codierung

-3

Erstellen Sie Ihren eigenen Container:

template <class T>
class statList : public std::list<T>
{
    public:
        statList() : std::list<T>::list() {}
        ~statList() {}
        T mean() {
           return accumulate(begin(),end(),0.0)/size();
        }
        T stddev() {
           T diff_sum = 0;
           T m = mean();
           for(iterator it= begin(); it != end(); ++it)
               diff_sum += ((*it - m)*(*it -m));
           return diff_sum/size();
        }
};

Es hat einige Einschränkungen, aber es funktioniert wunderbar, wenn Sie wissen, was Sie tun.


3
Um die Frage zu beantworten: weil es absolut keine Notwendigkeit gibt. Das Erstellen eines eigenen Containers hat absolut keine Vorteile gegenüber dem Schreiben einer kostenlosen Funktion.
Konrad Rudolph

1
Ich weiß nicht einmal, wo ich damit anfangen soll. Wenn Sie eine Liste als zugrunde liegende Datenstruktur verwenden, werden die Werte nicht einmal zwischengespeichert. Dies ist einer der wenigen Gründe, warum ich mir vorstellen kann, eine containerähnliche Struktur zu verwenden. Besonders wenn die Werte selten zufällig sind und der Mittelwert / Standardwert häufig benötigt wird.
Erstellen Sie

-7

// bedeutet Abweichung in c ++

/ Eine Abweichung, die eine Differenz zwischen einem beobachteten Wert und dem wahren Wert einer interessierenden Größe (z. B. einem Populationsmittelwert) ist, ist ein Fehler und eine Abweichung, die die Differenz zwischen dem beobachteten Wert und einer Schätzung des wahren Werts (z Eine Schätzung kann ein Stichprobenmittelwert sein. Diese Konzepte gelten für Daten in den Intervall- und Verhältnisniveaus der Messung. /.

#include <iostream>
#include <conio.h>
using namespace std;

/* run this program using the console pauser or add your own getch,     system("pause") or input loop */

int main(int argc, char** argv)
{
int i,cnt;
cout<<"please inter count:\t";
cin>>cnt;
float *num=new float [cnt];
float   *s=new float [cnt];
float sum=0,ave,M,M_D;

for(i=0;i<cnt;i++)
{
    cin>>num[i];
    sum+=num[i];    
}
ave=sum/cnt;
for(i=0;i<cnt;i++)
{
s[i]=ave-num[i];    
if(s[i]<0)
{
s[i]=s[i]*(-1); 
}
cout<<"\n|ave - number| = "<<s[i];  
M+=s[i];    
}
M_D=M/cnt;
cout<<"\n\n Average:             "<<ave;
cout<<"\n M.D(Mean Deviation): "<<M_D;
getch();
return 0;

}}

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.