在c++中使用Boost计算样本向量的均值和标准差

Calculate mean and standard deviation from a vector of samples in C++ using Boost

本文关键字:向量 标准差 样本 计算 c++ Boost      更新时间:2023-10-16

是否有一种方法来计算包含Boost样本的向量的平均值和标准差?

还是必须创建一个累加器并将向量输入其中?

我不知道Boost是否有更具体的函数,但您可以使用标准库。

给定std::vector<double> v,这是朴素方式:

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

对于较大或较小的值,这很容易发生溢出或下溢。一个稍微好一点的计算标准偏差的方法是:

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 for c++ 11:

std::transform的调用可以使用lambda函数来代替std::minusstd::bind2nd(现在已弃用):

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

如果性能对你很重要,并且你的编译器支持lambdas, stdev计算可以更快更简单:在VS 2012的测试中,我发现以下代码比所选答案中给出的Boost代码快10倍以上;它也比使用musiphil提供的标准库的更安全版本的答案快5倍。

注意我使用的是样本标准偏差,所以下面的代码给出了稍微不同的结果(为什么在标准偏差中有一个- 1)

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

使用累加器在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;

,

通过musiphil改进答案,您可以编写一个没有临时向量diff的标准差函数,只需使用具有c++ 11 lambda功能的单个inner_product调用:

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

我怀疑做多次减法比使用额外的中间存储更便宜,我认为它更具可读性,但我还没有测试过性能。

关于为什么使用N-1(如func.size() - 1)的解释,请参阅这些问题-注意问题如何声明我们有一个"包含样本的向量"

似乎没有提到下面这个优雅的递归解决方案,尽管它已经存在很长时间了。参考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);

则对于n>=2值的列表,标准差的估计为:

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

希望这对你有帮助!

我的回答与Josh Greifer类似,但概括为样本协方差。样本方差就是两个输入相同的样本协方差。这包括贝塞尔相关。

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

比前面提到的版本快2倍——主要是因为transform()和inner_product()循环被连接起来了。抱歉我的快捷方式/typedefs/宏:Flo = float。const ref. VFlo - vector。在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步递归:

mean_k = 1/k * ((k-r) * mean_ (k-r) + sum_over_i_from_ (n-r + 1) _to_n (x_i)],

选择r使求和分量更接近。

创建自己的容器:

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

它确实有一些限制,但当你知道你在做什么时,它会很好地工作。

//表示c++中的偏差

/观测值与感兴趣的数量(如总体均值)的真实值之间的差异的偏差是误差,而观测值与真实值估计值(这种估计值可能是样本均值)之间的差异的偏差是残差。这些概念适用于测量区间和比率水平上的数据。/

#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<<"nn Average:             "<<ave;
cout<<"n M.D(Mean Deviation): "<<M_D;
getch();
return 0;

}