Zauważ, że tag :: variance oblicza wariancję według przybliżonego wzoru. tag :: variance (lazy) oblicza według dokładnej formuły, a konkretnie: second moment - squared meanco da niepoprawny wynik, jeśli wariancja jest bardzo mała z powodu błędów zaokrąglenia. W rzeczywistości może powodować ujemną wariancję.
panda-34,
Użyj algorytmu rekurencyjnego (online), jeśli wiesz, że będziesz mieć dużo liczb. Pozwoli to rozwiązać problemy z niedopełnieniem i przepełnieniem.
Kemin Zhou
217
Nie wiem, czy Boost ma bardziej specyficzne funkcje, ale możesz to zrobić za pomocą standardowej biblioteki.
Biorąc pod uwagę std::vector<double> v, jest to naiwny sposób:
#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);
Jest to podatne na przepełnienie lub niedomiar dla dużych lub małych wartości. Nieco lepszym sposobem obliczenia odchylenia standardowego jest:
Tak; oczywiście dolna część zależy od wartości meanobliczonej w górnej części.
musiphil
7
Pierwszy zestaw równań nie działa. Wstawiłem int 10 i 2 i otrzymałem wynik 4. Na pierwszy rzut oka wydaje mi się, że to b / c zakłada, że (ab) ^ 2 = a ^ 2-b ^ 2
Charles L.
2
@CharlesL .: Powinno działać, a 4 to poprawna odpowiedź.
musiphil
3
@StudentT: Nie, ale można zastąpić (v.size() - 1)dla v.size()w ostatnim wierszu powyżej: std::sqrt(sq_sum / (v.size() - 1)). (W przypadku pierwszej metody, to jest trochę skomplikowane: std::sqrt(sq_sum / (v.size() - 1) - mean * mean * v.size() / (v.size() - 1)).
musiphil
6
Używanie std::inner_productdo sumy kwadratów jest bardzo dokładne.
Paul R
65
Jeśli wydajność jest dla Ciebie ważna, a Twój kompilator obsługuje lambdy, obliczenie wartości odchylenia standardowego może być szybsze i prostsze: W testach z VS 2012 stwierdziłem, że poniższy kod jest ponad 10 razy szybszy niż kod Boost podany w wybranej odpowiedzi ; jest również 5 razy szybsza niż bezpieczniejsza wersja odpowiedzi przy użyciu standardowych bibliotek dostarczonych przez musiphil.
Dzięki za podzielenie się tą odpowiedzią nawet rok później. Teraz przyszedłem kolejny rok później i zrobiłem ten ogólny zarówno dla typu wartości, jak i typu kontenera. Zobacz tutaj (uwaga: wydaje mi się, że moja pętla for oparta na zakresie jest tak szybka, jak twój kod lambda).
leemes
2
jaka jest różnica między używaniem std :: end (v) zamiast v.end ()?
spurra
3
std::end()Funkcja została dodana przez standard C ++ 11 w przypadkach, gdy nie ma nic podobnego v.end(). std::endMoże być przeładowany do mniej standardowym kontenerze - patrz en.cppreference.com/w/cpp/iterator/end
PEPR
Czy możesz wyjaśnić, dlaczego jest to szybsze?
dev_nut
4
Cóż, po pierwsze, "bezpieczna" odpowiedź (która jest podobna do mojej odpowiedzi) powoduje przejście przez tablicę 3: Raz dla sumy, raz dla średniej różnicy i raz dla podniesienia do kwadratu. W moim kodzie są tylko 2 przebiegi - to połączenie dwóch drugich w jeden. I (kiedy ostatnio patrzyłem, całkiem dawno temu!) Wywołania inner_product nie zostały zoptymalizowane. Dodatkowo „bezpieczny” kod kopiuje v do zupełnie nowej tablicy różnic, co dodaje więcej opóźnienia. Moim zdaniem mój kod też jest bardziej czytelny - i łatwo go przeportować do JavaScript i innych języków :)
Josh Greifer
5
Poprawiając odpowiedź przez musiphila , możesz napisać funkcję odchylenia standardowego bez wektora tymczasowego diff, używając tylko jednego inner_productwywołania z możliwościami lambda C ++ 11:
Podejrzewam, że wielokrotne odejmowanie jest tańsze niż zużywanie dodatkowej pamięci pośredniej i myślę, że jest bardziej czytelne, ale jeszcze nie testowałem wydajności.
Wydaje się, że nie wspomniano o następującym eleganckim rozwiązaniu rekurencyjnym, chociaż istnieje ono od dawna. Nawiązując do sztuki programowania komputerowego Knutha,
Moja odpowiedź jest podobna do Josha Greifera, ale uogólniona na próbkę kowariancji. Wariancja próbki to po prostu kowariancja próbki, ale z dwoma identycznymi danymi wejściowymi. Obejmuje to korelację Bessela.
2x szybsze niż poprzednie wersje - głównie dlatego, że pętle transform () i inner_product () są połączone. Przepraszam za mój skrót / typedefs / makro: Flo = float. CR stała ref. VFlo - wektor. Przetestowano w VS2010
#define fe(EL, CONTAINER) for each (auto EL in CONTAINER) //VS2010Flo stdDev(VFlo CR crVec){
SZ n = crVec.size(); if (n < 2) return0.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);
returnsqrt(fPreSqrt);
}
Czy pętlę Cit () można zapisać jako for( float f : crVec ) { fSqSum += f * f; fSum += f; } ?
Elfen Dew
1
Tak w C ++ 11. Próba użycia makr, które uniezależniają wersję. Zaktualizowano kod. PS. Ze względu na czytelność zazwyczaj preferuję 1 akcję na LOC. Kompilator powinien zobaczyć, że są to ciągłe iteracje i dołączyć do nich, jeśli „myśli”, że jednorazowe powtórzenie jest szybsze. Robiąc to w małych, krótkich krokach (bez użycia np. Std :: inner_product ()), rodzaj stylu assemblera, wyjaśnia nowemu czytelnikowi, co to znaczy. Binarny będzie mniejszy z powodu efektu ubocznego (w niektórych przypadkach).
slyy2048
„Próba użycia makr, które sprawiają, że jest niezależna od wersji” - jednak ograniczasz się do niestandardowej konstrukcji Visual C ++ „dla każdej” ( stackoverflow.com/questions/197375/… )
kodowanie
-3
Stwórz swój własny kontener:
template <classT>
classstatList :publicstd::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();
}
};
Ma pewne ograniczenia, ale działa pięknie, gdy wiesz, co robisz.
Odpowiadając na pytanie: ponieważ absolutnie nie ma takiej potrzeby. Utworzenie własnego kontenera nie ma absolutnie żadnych korzyści w porównaniu z napisaniem bezpłatnej funkcji.
Konrad Rudolph
1
Nie wiem nawet, od czego zacząć. Używasz listy jako podstawowej struktury danych, nawet nie buforujesz wartości, co byłby jednym z niewielu powodów, dla których przychodzi mi do głowy użycie struktury podobnej do kontenera. Zwłaszcza jeśli wartości przypadkowe są rzadkie, a średnia / odchylenie standardowe są często potrzebne.
Creat
-7
// oznacza odchylenie w C ++
/ Odchylenie, które jest różnicą między wartością obserwowaną a prawdziwą wartością wielkości będącej przedmiotem zainteresowania (np. oszacowanie może być średnią z próby) jest wartością resztkową. Pojęcia te mają zastosowanie do danych w przedziałach i poziomach współczynnika pomiaru. /
#include<iostream>#include<conio.h>usingnamespacestd;
/* run this program using the console pauser or add your own getch, system("pause") or input loop */intmain(int argc, char** argv){
int i,cnt;
cout<<"please inter count:\t";
cin>>cnt;
float *num=newfloat [cnt];
float *s=newfloat [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();
return0;
Odpowiedzi:
Korzystanie z akumulatorów jest sposobem obliczania średnich i odchyleń standardowych w trybie 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;
źródło
second moment - squared mean
co da niepoprawny wynik, jeśli wariancja jest bardzo mała z powodu błędów zaokrąglenia. W rzeczywistości może powodować ujemną wariancję.Nie wiem, czy Boost ma bardziej specyficzne funkcje, ale możesz to zrobić za pomocą standardowej biblioteki.
Biorąc pod uwagę
std::vector<double> v
, jest to naiwny sposób:#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);
Jest to podatne na przepełnienie lub niedomiar dla dużych lub małych wartości. Nieco lepszym sposobem obliczenia odchylenia standardowego jest:
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 dla C ++ 11:
Wywołanie do
std::transform
można zapisać za pomocą funkcji lambda zamiaststd::minus
istd::bind2nd
(obecnie przestarzałe):std::transform(v.begin(), v.end(), diff.begin(), [mean](double x) { return x - mean; });
źródło
mean
obliczonej w górnej części.(v.size() - 1)
dlav.size()
w ostatnim wierszu powyżej:std::sqrt(sq_sum / (v.size() - 1))
. (W przypadku pierwszej metody, to jest trochę skomplikowane:std::sqrt(sq_sum / (v.size() - 1) - mean * mean * v.size() / (v.size() - 1))
.std::inner_product
do sumy kwadratów jest bardzo dokładne.Jeśli wydajność jest dla Ciebie ważna, a Twój kompilator obsługuje lambdy, obliczenie wartości odchylenia standardowego może być szybsze i prostsze: W testach z VS 2012 stwierdziłem, że poniższy kod jest ponad 10 razy szybszy niż kod Boost podany w wybranej odpowiedzi ; jest również 5 razy szybsza niż bezpieczniejsza wersja odpowiedzi przy użyciu standardowych bibliotek dostarczonych przez musiphil.
Uwaga Używam przykładowego odchylenia standardowego, więc poniższy kod daje nieco inne wyniki ( Dlaczego w odchyleniach standardowych występuje minus jeden )
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));
źródło
std::end()
Funkcja została dodana przez standard C ++ 11 w przypadkach, gdy nie ma nic podobnegov.end()
.std::end
Może być przeładowany do mniej standardowym kontenerze - patrz en.cppreference.com/w/cpp/iterator/endPoprawiając odpowiedź przez musiphila , możesz napisać funkcję odchylenia standardowego bez wektora tymczasowego
diff
, używając tylko jednegoinner_product
wywołania z możliwościami 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 )); }
Podejrzewam, że wielokrotne odejmowanie jest tańsze niż zużywanie dodatkowej pamięci pośredniej i myślę, że jest bardziej czytelne, ale jeszcze nie testowałem wydajności.
źródło
Wydaje się, że nie wspomniano o następującym eleganckim rozwiązaniu rekurencyjnym, chociaż istnieje ono od dawna. Nawiązując do sztuki programowania komputerowego Knutha,
następnie dla listy
n>=2
wartości oszacowanie odchylenia standardowego wynosi:stddev = std::sqrt(variance_n / (n-1)).
Mam nadzieję że to pomoże!
źródło
Moja odpowiedź jest podobna do Josha Greifera, ale uogólniona na próbkę kowariancji. Wariancja próbki to po prostu kowariancja próbki, ale z dwoma identycznymi danymi wejściowymi. Obejmuje to korelację Bessela.
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); }
źródło
2x szybsze niż poprzednie wersje - głównie dlatego, że pętle transform () i inner_product () są połączone. Przepraszam za mój skrót / typedefs / makro: Flo = float. CR stała ref. VFlo - wektor. Przetestowano w 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); }
źródło
for( float f : crVec ) { fSqSum += f * f; fSum += f; }
?Stwórz swój własny kontener:
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(); } };
Ma pewne ograniczenia, ale działa pięknie, gdy wiesz, co robisz.
źródło
// oznacza odchylenie w C ++
/ Odchylenie, które jest różnicą między wartością obserwowaną a prawdziwą wartością wielkości będącej przedmiotem zainteresowania (np. oszacowanie może być średnią z próby) jest wartością resztkową. Pojęcia te mają zastosowanie do danych w przedziałach i poziomach współczynnika pomiaru. /
#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;
}
źródło