使用Boost计算C++中样本矢量的均值和标准差

use*_*144 84 c++ algorithm statistics boost mean

有没有办法使用Boost计算含有样品的载体的平均值和标准偏差?

或者我是否必须创建一个累加器并将矢量输入其中?

mus*_*hil 202

我不知道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);
Run Code Online (Sandbox Code Playgroud)

对于巨大或微小的值,这容易发生溢出或下溢.计算标准偏差的更好方法是:

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());
Run Code Online (Sandbox Code Playgroud)

更新 C++ 11:

要将呼叫std::transform可以使用lambda函数,而不是被写入std::minusstd::bind2nd(现在已废弃):

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

  • 第一组方程不起作用.我输入了int 10和2,得到了4的输出.一眼就认为它是b/c它假设(ab)^ 2 = a ^ 2-b ^ 2 (6认同)
  • 使用`std :: inner_product`求平方和非常整齐. (4认同)
  • @CharlesL。:它应该有效,4 是正确答案。 (2认同)
  • @StudentT:不,但你可以在上面的最后一行用`(v.size() - 1)`代替`v.size()`:`std :: sqrt(sq_sum /(v.size() - 1 ))`.(对于第一种方法,它有点复杂:`std :: sqrt(sq_sum /(v.size() - 1) - mean*mean*v.size()/(v.size() - 1))` . (2认同)
  • 我可以直接确认第一个实现对于微小数字确实会溢出/下溢。我必须更改为第二个实现,然后我没有获得标准差的 NAN 值。**为了避免上溢/下溢,两行额外的代码是值得的!** (2认同)

Jos*_*fer 62

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

注意我正在使用样本标准偏差,因此下面的代码给出了略微不同的结果(为什么标准差中有一个减号)

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));
Run Code Online (Sandbox Code Playgroud)

  • 好吧,有一件事,"安全"答案(就像我的答案)在阵列中进行3次传递:一次为总和,一次为差异,一次为平方.在我的代码中只有2次传球 - 它将第二次传球合二为一.而且(当我最后一次看时,不久前现在!)内部产品调用没有被优化掉.此外,"安全"代码将v复制到一个全新的差异阵列中,这会增加更多延迟.在我看来,我的代码也更具可读性 - 并且很容易移植到JavaScript和其他语言:) (4认同)
  • 对于没有像`v.end()`这样的情况,`Cd11标准添加了`std :: end()`函数.`std :: end`可以为较不标准的容器重载 - 请参阅http://en.cppreference.com/w/cpp/iterator/end (3认同)
  • 感谢您在一年后分享这个答案。现在,又一年后,我将这个通用化为值类型和容器类型。[参见此处](http://ideone.com/grzG0S)(注意:我猜我的基于范围的 for 循环与您的 lambda 代码一样快。) (2认同)
  • 使用std :: end(v)而不是v.end()有什么区别? (2认同)

Dav*_*hme 50

使用累加器计算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;
Run Code Online (Sandbox Code Playgroud)

 

  • 注意,tag :: variance通过近似公式计算方差.tag :: variance(lazy)通过精确的公式计算,具体来说:"second moment - squared mean"如果由于舍入误差导致方差非常小,将产生不正确的结果.它实际上可以产生负差异. (4认同)

gal*_*ica 7

似乎以下优雅的递归解决方案尚未被提及,尽管它已经存在很长时间了。参考 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);
Run Code Online (Sandbox Code Playgroud)

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

stddev = std::sqrt(variance_n / (n-1)). 
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助!


cod*_*ing 6

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

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());
}
Run Code Online (Sandbox Code Playgroud)

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