Câu hỏi Tính toán độ lệch chuẩn và trung bình từ một vectơ của các mẫu trong C ++ bằng cách sử dụng Boost


Có cách nào để tính toán độ lệch trung bình và chuẩn cho một véc tơ chứa các mẫu sử dụng không Tăng cường?

Hay tôi phải tạo ra một bộ tích lũy và nạp véc tơ vào nó?


75
2017-09-30 21:59


gốc




Các câu trả lời:


Sử dụng bộ tích lũy  cách tính toán phương tiện và độ lệch chuẩn trong Tăng cường.

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;


42
2017-09-30 22:48



Lưu ý, thẻ đó :: phương sai tính toán phương sai theo công thức gần đúng. tag :: variance (lazy) tính theo công thức chính xác, cụ thể: second moment - squared mean sẽ tạo ra kết quả không chính xác nếu phương sai rất nhỏ do lỗi làm tròn. Nó thực sự có thể tạo ra phương sai âm. - panda-34
Sử dụng thuật toán đệ quy (trực tuyến) nếu bạn biết bạn sẽ có nhiều số. Điều này sẽ chăm sóc cả hai vấn đề dưới và tràn. - Kemin Zhou


Tôi không biết liệu Boost có chức năng cụ thể hơn hay không, nhưng bạn có thể thực hiện nó với thư viện chuẩn.

Được std::vector<double> v, đây là cách ngây thơ:

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

Điều này dễ bị tràn hoặc tràn cho các giá trị lớn hoặc nhỏ. Cách tốt hơn một chút để tính toán độ lệch chuẩn là:

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ẬP NHẬT cho C ++ 11:

Cuộc gọi đến std::transform có thể được viết bằng hàm lambda thay vì std::minus và std::bind2nd(hiện không còn được dùng nữa):

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

175
2017-09-30 22:42



Vâng; rõ ràng, phần dưới phụ thuộc vào giá trị của mean được tính ở phần trên cùng. - musiphil
Tập phương trình đầu tiên không hoạt động. Tôi đặt int 10 & 2, và có một đầu ra của 4. Trong nháy mắt tôi nghĩ rằng đó là b / c nó giả định rằng (a-b) ^ 2 = a ^ 2-b ^ 2 - Charles L.
Điều này có bao gồm hiệu chỉnh bessel không? - SmallChess
@StudentT: Không, nhưng bạn có thể thay thế (v.size() - 1) cho v.size() ở dòng cuối cùng ở trên: std::sqrt(sq_sum / (v.size() - 1)). (Đối với phương pháp đầu tiên, có một chút phức tạp: std::sqrt(sq_sum / (v.size() - 1) - mean * mean * v.size() / (v.size() - 1)). - musiphil
Sử dụng std::inner_product cho tổng của hình vuông là rất gọn gàng. - Paul R


Nếu hiệu suất của bạn là quan trọng đối với bạn, và trình biên dịch của bạn hỗ trợ lambdas, phép tính stdev có thể được thực hiện nhanh hơn và đơn giản hơn: Trong các thử nghiệm với VS 2012, tôi thấy rằng mã sau đây vượt quá 10 X nhanh hơn mã Boost được đưa ra trong câu trả lời đã chọn ; nó cũng nhanh hơn 5 lần so với phiên bản an toàn hơn của câu trả lời sử dụng các thư viện chuẩn do musiphil cung cấp.

Lưu ý tôi đang sử dụng độ lệch chuẩn mẫu, vì vậy mã bên dưới cho kết quả hơi khác nhau (Tại sao có một Minus One trong độ lệch chuẩn)

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

53
2017-09-13 12:01



Cảm ơn bạn đã chia sẻ câu trả lời này ngay cả một năm sau đó. Bây giờ tôi đến một năm sau đó và thực hiện điều này chung cho cả hai loại giá trị và loại container. Xem ở đây (Lưu ý: Tôi đoán rằng vòng lặp dựa trên phạm vi của tôi nhanh như mã lambda của bạn.) - leemes
sự khác nhau giữa việc sử dụng std :: end (v) thay vì v.end () là gì? - spurra
Các std::end() chức năng đã được thêm vào bởi C ++ 11 tiêu chuẩn cho các trường hợp khi không có gì giống như v.end(). Các std::end có thể bị quá tải cho thùng chứa ít tiêu chuẩn - xem en.cppreference.com/w/cpp/iterator/end - pepr
Bạn có thể giải thích tại sao điều này nhanh hơn? - dev_nut
Vâng cho một điều, câu trả lời "an toàn" (giống như câu trả lời của tôi) làm cho 3 đi qua mảng: Một lần cho tổng, một lần cho diff-mean, và một lần cho bình phương. Trong mã của tôi chỉ có 2 đường chuyền - Nó làm cho hai chữ cái thứ hai chuyển thành một. Và (khi tôi nhìn lần cuối, cách đây khá lâu rồi!) Các cuộc gọi inner_product không được tối ưu hóa. Ngoài ra, mã "an toàn" sao chép v vào một mảng khác biệt hoàn toàn mới, làm tăng thêm độ trễ. Theo tôi, mã của tôi dễ đọc hơn - và dễ dàng chuyển sang JavaScript và các ngôn ngữ khác :) - Josh Greifer


Câu trả lời của tôi tương tự như Josh Greifer nhưng được khái quát hóa để hiệp phương sai mẫu. Phương sai mẫu chỉ là phương sai hiệp phương sai mẫu nhưng với hai đầu vào giống hệt nhau. Điều này bao gồm sự tương quan của Bessel.

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

1
2018-04-22 12:38





2x nhanh hơn các phiên bản trước đó - chủ yếu là vì các vòng chuyển đổi () và inner_product () được nối. Xin lỗi về shortcut / typedefs / macro của tôi: Flo = float. Cit = const iteration. CR const ref. VFlo - vectơ. Thử nghiệm trong 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);
}

0
2017-10-25 02:46





Tạo vùng chứa của riêng bạn:

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

Nó có một số hạn chế, nhưng nó hoạt động rất tốt khi bạn biết bạn đang làm gì.


-2
2017-08-08 22:50



Để trả lời câu hỏi: bởi vì hoàn toàn không cần. Việc tạo vùng chứa của riêng bạn hoàn toàn không có lợi ích gì so với việc viết một hàm miễn phí. - Konrad Rudolph
Tôi thậm chí không biết bắt đầu từ đâu. Bạn đang sử dụng danh sách làm cấu trúc dữ liệu cơ bản, bạn thậm chí không nhớ các giá trị, đó là một trong những lý do tôi có thể nghĩ đến để sử dụng cấu trúc giống như vùng chứa. Đặc biệt là nếu các giá trị cơ hội không thường xuyên và trung bình / stddev là cần thiết thường xuyên. - Creat


// có nghĩa là độ lệch trong c ++

/Độ lệch là chênh lệch giữa giá trị quan sát và giá trị thực của số lượng lãi suất (chẳng hạn như trung bình dân số) là lỗi và độ lệch là chênh lệch giữa giá trị được quan sát và ước tính giá trị thực (như ước tính có thể là trung bình mẫu) là số dư. Các khái niệm này được áp dụng cho dữ liệu ở các mức độ và tỷ lệ đo lường./

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

}


-6
2017-08-07 08:24