Observe que tag :: variance calcula a variância por uma fórmula aproximada. tag :: variance (lazy) calcula por uma fórmula exata, especificamente: second moment - squared meanque produzirá resultado incorreto se a variância for muito pequena por causa de erros de arredondamento. Na verdade, pode produzir variação negativa.
panda-34
Use o algoritmo recursivo (online) se você souber que terá muitos números. Isso vai cuidar de problemas de sub e estouro.
Kemin Zhou
216
Não sei se Boost tem funções mais específicas, mas você pode fazer isso com a biblioteca padrão.
Dado std::vector<double> v, esta é a maneira ingênua:
#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);
Isso é suscetível a estouro ou estouro negativo para valores enormes ou minúsculos. Uma maneira ligeiramente melhor de calcular o desvio padrão é:
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());
ATUALIZAÇÃO para C ++ 11:
A chamada para std::transformpode ser escrita usando uma função lambda em vez de std::minuse std::bind2nd(agora obsoleto):
std::transform(v.begin(), v.end(), diff.begin(),[mean](double x){return x - mean;});
Sim; obviamente, a parte inferior depende do valor meancalculado na parte superior.
musiphil
7
O primeiro conjunto de equações não funciona. Coloquei int 10 e 2 e obtive uma saída de 4. À primeira vista, acho que b / c, ele pressupõe que (ab) ^ 2 = a ^ 2-b ^ 2
Charles L.
2
@CharlesL .: Deve funcionar, e 4 é a resposta correta.
musiphil
3
@StudentT: Não, mas você pode substituir (v.size() - 1)para v.size()na última linha acima: std::sqrt(sq_sum / (v.size() - 1)). (Para o primeiro método, que é um pouco complicado: std::sqrt(sq_sum / (v.size() - 1) - mean * mean * v.size() / (v.size() - 1)).
musiphil
5
Usar std::inner_productpara soma de quadrados é muito legal.
Paul R
65
Se o desempenho é importante para você, e seu compilador suporta lambdas, o cálculo stdev pode ser feito mais rápido e simples: Em testes com VS 2012 descobri que o código a seguir é 10 vezes mais rápido que o código Boost fornecido na resposta escolhida ; também é 5 X mais rápido do que a versão mais segura da resposta usando bibliotecas padrão fornecidas por musiphil.
Observação Estou usando o desvio padrão da amostra, portanto, o código a seguir fornece resultados ligeiramente diferentes ( por que há um menos em desvios padrão )
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),[&](constdouble d){
accum +=(d - m)*(d - m);});double stdev = sqrt(accum /(v.size()-1));
Obrigado por compartilhar esta resposta mesmo um ano depois. Agora venho outro ano depois e tornei este genérico para o tipo de valor e o tipo de contêiner. Veja aqui (Observação: acho que meu loop for baseado em intervalo é tão rápido quanto seu código lambda.)
leemes
2
qual é a diferença entre usar std :: end (v) em vez de v.end ()?
spurra
3
A std::end()função foi adicionada pelo padrão C ++ 11 para casos em que não há nada semelhante v.end(). O std::endpode ser sobrecarregado para o contêiner menos padrão - consulte en.cppreference.com/w/cpp/iterator/end
pepr
Você pode explicar por que isso é mais rápido?
dev_nut
4
Bem, para começar, a resposta "segura" (que é como minha resposta) dá 3 passagens pelo array: uma vez para a soma, uma vez para a média diferencial e uma vez para o quadrado. No meu código, há apenas 2 passagens - está combinando as duas segundas passagens em uma. E (quando eu olhei pela última vez, há um bom tempo agora!) As chamadas inner_product não foram otimizadas. Além disso, o código "seguro" copia v em um array totalmente novo de diffs, o que adiciona mais atraso. Na minha opinião, meu código também é mais legível - e é facilmente portado para JavaScript e outras linguagens :)
Josh Greifer
5
Aprimorando a resposta por musiphil , você pode escrever uma função de desvio padrão sem o vetor temporário diff, apenas usando uma única inner_productchamada com os recursos lambda do C ++ 11:
Suspeito que fazer a subtração várias vezes é mais barato do que usar o armazenamento intermediário adicional e acho que é mais legível, mas ainda não testei o desempenho.
Acho que isso é calcular a variância, não o desvio padrão.
sg_man
O desvio padrão é calculado dividindo por N e não por N-1. Por que você divide sq_sum por func.size () - 1?
pocjoc
Acho que estou computando o "desvio padrão corrigido" (ver, por exemplo, en.wikipedia.org/wiki/… )
codificação
2
Parece que a seguinte solução recursiva elegante não foi mencionada, embora já exista há muito tempo. Referindo-se à Arte da Programação de Computador de Knuth,
Minha resposta é semelhante a Josh Greifer, mas generalizada para covariância de amostra. A variância da amostra é apenas a covariância da amostra, mas com as duas entradas idênticas. Isso inclui a correlação de Bessel.
2x mais rápido do que as versões mencionadas antes - principalmente porque os loops transform () e inner_product () são unidos. Desculpe pelo meu atalho / typedefs / macro: Flo = float. CR const ref. VFlo - vetor. Testado em 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);return sqrt(fPreSqrt);}
O loop Cit () pode ser escrito como for( float f : crVec ) { fSqSum += f * f; fSum += f; } ?
Elfen Dew
1
Sim em C ++ 11. Tentando usar macros que o tornem independente de versão. Atualizado o código. PS. Para facilitar a leitura, geralmente prefiro 1 ação por LOC. O compilador deve ver que essas são iterações constantes e uni-las se "achar" mais rápido iterar uma vez. Fazendo isso em pequenos passos curtos (sem usar std :: inner_product () por exemplo), uma espécie de estilo de montagem, explica ao novo leitor o que significa. O binário será menor por efeito colateral (em alguns casos).
slyy2048
"Tentando usar macros que o tornam independente da versão" - ainda assim você se limita ao Visual C ++ não padrão "para cada" construção ( stackoverflow.com/questions/197375/… )
codificação
-3
Crie seu próprio contêiner:
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();}};
Ele tem algumas limitações, mas funciona perfeitamente quando você sabe o que está fazendo.
Para responder à pergunta: porque não há absolutamente nenhuma necessidade. Criar seu próprio contêiner não traz absolutamente nenhum benefício em comparação com escrever uma função gratuita.
Konrad Rudolph
1
Eu nem sei por onde começar com isso. Você está usando uma lista como a estrutura de dados subjacente, você nem mesmo armazena os valores em cache, o que seria um dos poucos motivos pelos quais posso pensar para usar uma estrutura semelhante a um contêiner. Especialmente se os valores mudam com pouca frequência e a média / desvio padrão são necessários com frequência.
Cria
-7
// significa desvio em c ++
/ Um desvio que é uma diferença entre um valor observado e o valor verdadeiro de uma quantidade de interesse (como uma média populacional) é um erro e um desvio que é a diferença entre o valor observado e uma estimativa do valor verdadeiro (como uma estimativa pode ser uma média da amostra) é um resíduo. Esses conceitos são aplicáveis para dados nos níveis de intervalo e razão de medição. /
#include<iostream>#include<conio.h>usingnamespace 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=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;
Respostas:
Usar acumuladores é a maneira de calcular médias e desvios padrão no Boost .
fonte
second moment - squared mean
que produzirá resultado incorreto se a variância for muito pequena por causa de erros de arredondamento. Na verdade, pode produzir variação negativa.Não sei se Boost tem funções mais específicas, mas você pode fazer isso com a biblioteca padrão.
Dado
std::vector<double> v
, esta é a maneira ingênua:Isso é suscetível a estouro ou estouro negativo para valores enormes ou minúsculos. Uma maneira ligeiramente melhor de calcular o desvio padrão é:
ATUALIZAÇÃO para C ++ 11:
A chamada para
std::transform
pode ser escrita usando uma função lambda em vez destd::minus
estd::bind2nd
(agora obsoleto):fonte
mean
calculado na parte superior.(v.size() - 1)
parav.size()
na última linha acima:std::sqrt(sq_sum / (v.size() - 1))
. (Para o primeiro método, que é um pouco complicado:std::sqrt(sq_sum / (v.size() - 1) - mean * mean * v.size() / (v.size() - 1))
.std::inner_product
para soma de quadrados é muito legal.Se o desempenho é importante para você, e seu compilador suporta lambdas, o cálculo stdev pode ser feito mais rápido e simples: Em testes com VS 2012 descobri que o código a seguir é 10 vezes mais rápido que o código Boost fornecido na resposta escolhida ; também é 5 X mais rápido do que a versão mais segura da resposta usando bibliotecas padrão fornecidas por musiphil.
Observação Estou usando o desvio padrão da amostra, portanto, o código a seguir fornece resultados ligeiramente diferentes ( por que há um menos em desvios padrão )
fonte
std::end()
função foi adicionada pelo padrão C ++ 11 para casos em que não há nada semelhantev.end()
. Ostd::end
pode ser sobrecarregado para o contêiner menos padrão - consulte en.cppreference.com/w/cpp/iterator/endAprimorando a resposta por musiphil , você pode escrever uma função de desvio padrão sem o vetor temporário
diff
, apenas usando uma únicainner_product
chamada com os recursos lambda do C ++ 11:Suspeito que fazer a subtração várias vezes é mais barato do que usar o armazenamento intermediário adicional e acho que é mais legível, mas ainda não testei o desempenho.
fonte
Parece que a seguinte solução recursiva elegante não foi mencionada, embora já exista há muito tempo. Referindo-se à Arte da Programação de Computador de Knuth,
então, para uma lista de
n>=2
valores, a estimativa do desvio padrão é:Espero que isto ajude!
fonte
Minha resposta é semelhante a Josh Greifer, mas generalizada para covariância de amostra. A variância da amostra é apenas a covariância da amostra, mas com as duas entradas idênticas. Isso inclui a correlação de Bessel.
fonte
2x mais rápido do que as versões mencionadas antes - principalmente porque os loops transform () e inner_product () são unidos. Desculpe pelo meu atalho / typedefs / macro: Flo = float. CR const ref. VFlo - vetor. Testado em VS2010
fonte
for( float f : crVec ) { fSqSum += f * f; fSum += f; }
?Crie seu próprio contêiner:
Ele tem algumas limitações, mas funciona perfeitamente quando você sabe o que está fazendo.
fonte
// significa desvio em c ++
/ Um desvio que é uma diferença entre um valor observado e o valor verdadeiro de uma quantidade de interesse (como uma média populacional) é um erro e um desvio que é a diferença entre o valor observado e uma estimativa do valor verdadeiro (como uma estimativa pode ser uma média da amostra) é um resíduo. Esses conceitos são aplicáveis para dados nos níveis de intervalo e razão de medição. /
}
fonte