बूस्ट का उपयोग करके C ++ में नमूनों के वेक्टर से माध्य और मानक विचलन की गणना करें


91

क्या बूस्ट का उपयोग कर नमूनों वाले वेक्टर के लिए माध्य और मानक विचलन की गणना करने का एक तरीका है ?

या क्या मुझे एक संचायक बनाना है और उसमें वेक्टर को खिलाना है?


एक पास समाधान के लिए, stackoverflow.com/questions/7616511/…
गुलज़ार

जवाबों:


52

संचायक का उपयोग बूस्ट में साधन और मानक विचलन की गणना करने का तरीका है ।

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;

 


5
ध्यान दें, कि टैग :: विचरण एक अनुमानित सूत्र द्वारा विचरण की गणना करता है। टैग :: विचरण (आलसी) एक सटीक सूत्र द्वारा गणना करता है, विशेष रूप से: second moment - squared meanजो गलत परिणाम उत्पन्न करेगा यदि राउंडिंग त्रुटियों के कारण विचरण बहुत छोटा है। यह वास्तव में नकारात्मक विचरण पैदा कर सकता है।
पांडा -34

यदि आप जानते हैं कि आप बहुत अधिक संख्या में जा रहे हैं, तो पुनरावर्ती (ऑनलाइन) एल्गोरिथ्म का उपयोग करें। यह अंडर और ओवरफ्लो दोनों समस्याओं का ध्यान रखेगा।
केमिन झोउ

217

मुझे नहीं पता कि बूस्ट में अधिक विशिष्ट कार्य हैं, लेकिन आप इसे मानक पुस्तकालय के साथ कर सकते हैं।

यह देखते हुए 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());

C ++ 11 के लिए अद्यतन :

और (अब पदावनत) के std::transformबजाय एक लंबो फ़ंक्शन का उपयोग करके कॉल को लिखा जा सकता है :std::minusstd::bind2nd

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

1
हाँ; जाहिर है, नीचे का हिस्सा meanशीर्ष भाग में गणना के मूल्य पर निर्भर करता है ।
मुशीफिल

7
समीकरणों का पहला सेट काम नहीं करता है। मैंने int 10 और 2 डाला, और 4 का आउटपुट मिला। एक नज़र में मुझे लगता है कि यह b / c है यह मानता है कि (ab) ^ 2 = a ^ 2-b ^ 2
चार्ल्स एल।

2
@CharlesL .: यह काम करना चाहिए, और 4 सही उत्तर है।
21

3
@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))
musiphil

6
std::inner_productवर्गों का योग के लिए उपयोग बहुत साफ है।
पॉल आर

65

यदि प्रदर्शन आपके लिए महत्वपूर्ण है, और आपका कंपाइलर लैम्ब्डा का समर्थन करता है, तो स्टदेव गणना को तेज और सरल बनाया जा सकता है: वीएस 2012 के साथ परीक्षणों में मैंने पाया है कि चुने गए उत्तर में दिए गए बूस्ट कोड की तुलना में निम्नलिखित कोड 10 एक्स तेज है। ; यह भी मुस्तफ़िल द्वारा दिए गए मानक पुस्तकालयों का उपयोग कर उत्तर के सुरक्षित संस्करण की तुलना में 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));

इस उत्तर को एक साल बाद भी साझा करने के लिए धन्यवाद। अब मैं एक और साल बाद आता हूं और इस एक जेनेरिक को वैल्यू टाइप और कंटेनर टाइप दोनों के लिए बनाया है। यहाँ देखें (नोट: मेरा अनुमान है कि लूप के लिए मेरी रेंज-आधारित आपके लैम्ब्डा कोड जितनी तेज़ है।)
लेम्स

2
v.end () के बजाय std :: end (v) का उपयोग करने में क्या अंतर है?
spurra

3
std::end()समारोह के मामलों के लिए सी ++ 11 मानक से जोड़ा गया है जब की तरह कुछ भी नहीं है वहाँ v.end()std::endकम मानक कंटेनर के लिए अतिभारित किया जा सकता - देख en.cppreference.com/w/cpp/iterator/end
pepr

क्या आप बता सकते हैं कि यह तेजी क्यों है?
dev_nut

4
अच्छी तरह से एक बात के लिए, "सुरक्षित" उत्तर (जो मेरे उत्तर की तरह है) 3 सरणी से गुजरता है: एक बार राशि के लिए, एक बार अंतर-मतलब के लिए, और एक बार स्क्वेरिंग के लिए। मेरे कोड में केवल 2 पास हैं - यह दूसरे दो पास को एक में मिला रहा है। और (जब मैंने आखिरी बार देखा था, अब से कुछ समय पहले!) भीतर_प्रचार कॉल दूर अनुकूलित नहीं थे। "सेफ" कोड के अलावा, v पूरी तरह से अलग सरणी के नए सरणी में प्रतिलिपि बनाता है, जो अधिक विलंब जोड़ता है। मेरी राय में मेरा कोड अधिक पठनीय भी है - और आसानी से जावास्क्रिप्ट और अन्य भाषाओं में पोर्ट किया जाता है :)
जोश ग्रीफर

5

मुशीफिल द्वारा उत्तर पर सुधार , आप अस्थायी वेक्टर के बिना एक मानक विचलन फ़ंक्शन लिख सकते हैं diff, बस inner_productC ++ 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 std::sqrt(sq_sum / ( func.size() - 1 ));
}

मुझे संदेह है कि कई बार घटाव को अतिरिक्त मध्यवर्ती भंडारण का उपयोग करने से सस्ता है, और मुझे लगता है कि यह अधिक पठनीय है, लेकिन मैंने अभी तक प्रदर्शन का परीक्षण नहीं किया है।


1
मुझे लगता है कि यह विचलन की गणना कर रहा है, मानक विचलन नहीं।
sg_man

Std विचलन की गणना N से विभाजित की जाती है और N-1 द्वारा नहीं। आप fun_size () - 1 द्वारा sq_sum को क्यों विभाजित करते हैं?
पोकजॉक

मुझे लगता है कि मैं "सही मानक विचलन" की गणना कर रहा हूं (उदाहरण देखें en.wikipedia.org/wiki/… )
कोडिंग

2

ऐसा लगता है कि निम्नलिखित सुरुचिपूर्ण पुनरावर्ती समाधान का उल्लेख नहीं किया गया है, हालांकि यह लंबे समय से आसपास है। कंप्यूटर प्रोग्रामिंग के नथ की कला का जिक्र करते हुए,

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

उम्मीद है की यह मदद करेगा!


1

मेरा जवाब जोश ग्रीफर के समान है, लेकिन नमूना covariance के लिए सामान्यीकृत है। नमूना विचरण सिर्फ नमूना covariance है, लेकिन समान दो आदानों के साथ। इसमें बेसेल का सहसंबंध भी शामिल है।

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

0

उल्लेख से पहले के संस्करणों की तुलना में तेजी से 2x - ज्यादातर क्योंकि रूपांतरण () और इनर_प्रोडक्ट () लूप्स में शामिल हो गए हैं। मेरे शॉर्टकट / टाइपफेड / मैक्रो के बारे में क्षमा करें: फ़्लोट = फ़्लोट। सीआर कास्ट रेफरी। VFlo - वेक्टर। 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);
}

क्या नागरिक () लूप के रूप में लिखा जा सकता है for( float f : crVec ) { fSqSum += f * f; fSum += f; } ?
एलफिन ड्यू

1
हां C ++ 11 में। मैक्रोज़ का उपयोग करने की कोशिश कर रहा है जो इसे संस्करण को स्वतंत्र बनाता है। कोड अपडेट किया गया। पुनश्च। पठनीयता के लिए मैं आमतौर पर प्रति LOC पर 1 कार्रवाई पसंद करता हूं। कंपाइलर को यह देखना चाहिए कि वे लगातार पुनरावृत्तियाँ हैं और उनमें शामिल हों यदि यह "सोचता है" तो यह एक बार पुनरावृति करने के लिए तेज़ है। इसे छोटे छोटे चरणों में करना (एसटीडी का उपयोग किए बिना: इनर_प्रोडक्ट () जैसे), असेंबली-शैली का प्रकार, नए पाठक को समझाता है कि इसका क्या अर्थ है। बाइनरी साइड-इफेक्ट (कुछ मामलों में) से छोटा होगा।
slyy2048

"मैक्रोज़ का उपयोग करने की कोशिश करना जो इसे संस्करण को स्वतंत्र बनाते हैं" - फिर भी आप प्रत्येक "निर्माण ( stackoverflow.com/questions/197375/… ) के लिए अपने आप को गैर-मानक विज़ुअल C ++ तक सीमित करते हैं
कोडिंग

-3

अपना खुद का कंटेनर बनाएँ:

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

इसकी कुछ सीमाएँ हैं, लेकिन यह खूबसूरती से काम करता है जब आप जानते हैं कि आप क्या कर रहे हैं।


3
प्रश्न का उत्तर देने के लिए: क्योंकि इसकी कोई आवश्यकता नहीं है। एक नि: शुल्क फ़ंक्शन लिखने की तुलना में अपने खुद के कंटेनर बनाने का कोई लाभ नहीं है।
कोनराड रुडोल्फ

1
मुझे यह भी नहीं पता कि इसे कहां से शुरू करना है। आप अंतर्निहित डेटा संरचना के रूप में एक सूची का उपयोग कर रहे हैं, आप मूल्यों को कैश भी नहीं करते हैं, जो कुछ कारणों में से एक होगा जो मैं कंटेनर जैसी संरचना का उपयोग करने के बारे में सोच सकता हूं। खासतौर पर तब जब मान संयोगवश घटते हैं और अक्सर माध्य / stddev की जरूरत होती है।
बनाएं

-7

// का मतलब है 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;

}

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.