Вычислите среднее и стандартное отклонение от вектора выборок в C++ с помощью Boost


есть ли способ вычислить среднее и стандартное отклонение для вектора, содержащего образцы, используя Boost?

или мне нужно создать аккумулятор и подать в него вектор?

8 77

8 ответов:

использование аккумуляторов и способ вычисления средних и стандартных отклонений в 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;

Я не знаю, есть ли у 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);

это восприимчиво к переполнению или underflow для огромных или крошечных значений. Немного лучший способ рассчитать стандартное отклонение:

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

обновление для C++11:

вызов std::transform можно записать с помощью лямбда-функции вместо std::minus и std::bind2nd(сейчас устарел):

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

если производительность важна для вас, и ваш компилятор поддерживает lambdas, вычисление stdev может быть сделано быстрее и проще: в тестах с VS 2012 я обнаружил, что следующий код более чем на 10 X быстрее, чем код Boost, указанный в выбранном ответе; это также на 5 X быстрее, чем более безопасная версия ответа с использованием стандартных библиотек, предоставленных musiphil.

Примечание я использую образец стандартного отклонения, поэтому приведенный ниже код дает несколько иные результаты (почему есть минус один в стандартных отклонениях)

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

мой ответ похож на Джоша грейфера, но обобщен для выборки ковариации. Выборочная дисперсия-это просто выборочная ковариация, но с двумя идентичными входами. Это включает в себя корреляцию Бесселя.

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

2x быстрее, чем упомянутые выше версии - в основном потому, что циклы transform() и inner_product() объединены. Извините за мой ярлык / typedefs / macro: Flo = float. Cit = const итерация. КЛ константная ссылка. Вфло-вектор. Испытания в VS2010

Flo     stdDev2(VFlo CR crVec) {
    SZ  n = crVec.size();                               if (n < 2) return 0.0f;
    Flo fSqSum = 0.0f, fSum = 0.0f;
    Cit(VFlo, crVec) {
        Flo f   = *cx;
        fSqSum  += f * f; 
        fSum    += f;
    } 
    Flo fSumSq      = fSum * fSum;
    Flo fSumSqDivN  = fSumSq / n;
    Flo fSubSqSum   = fSqSum - fSumSqDivN;
    Flo preSqrt     = fSubSqSum / (n-1);
    return  sqrt(preSqrt);
}

улучшение ответ от musiphil выше, вы можете написать функцию стандартного отклонения без временного вектора diff, просто используя один inner_product вызов с возможностями лямбда 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 sq_sum / ( func.size() - 1 );
}

Я подозреваю, что вычитание в несколько раз дешевле, чем использование дополнительного промежуточного хранилища, и я думаю, что это более читаемо, но я еще не тестировал производительность.

создайте свой собственный контейнер:

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

}