#pragma once /*!\brief calculates mean, min, max, etc. as running values. The idea is that you simply add values and ask for a stat whenever needed. The clear() method resets the object and makes it able to work with new data. Adding values can be doing with weight (addValue) or without (operator +=). You can remove a value; for Min or Max this has no effect as this would require buffering all data. The mostFrequent assumes the data contains integer classes. Then the class that is found most often will be the output. Weighting, again, assumes integer values. Beware that if you pass data that is not really class-data, the memory consumption can become large (and the result will be rather uninteresting). The variance won't take the decreasing degrees of freedom into consideration when weights are provided. The object is ready to use with int, float and double types. If other types are needed, you may need to specialise an isZero function for each new type. -*/ #include "QuickSort.h" #include "TInterval.h" namespace NSet { /* Average used to be 'Mean' but that does not sound positive enough */ enum EStatType { stCount = 0, stAverage, stMedian, stRMS, stStdDev, stVariance, stNormVariance, stMin, stMax, stExtreme, stSum, stSqSum }; template class TStatValue { public: TStatValue( void ) { weighted_ = true; clear(); } inline void clear(); inline TStatValue& addValue(T data,T weight=1); inline TStatValue& addValues(int sz,const T* data,const T* weights=0); inline TStatValue& replaceValue(T olddata,T newdata,T wt=1); inline TStatValue& removeValue(T data,T weight=1); inline double getValue(EStatType) const; inline int getIndex(EStatType) const; //!< only for Median, Min and Max inline TStatValue& operator +=( T t ) { return addValue(t); } inline int size() const { return nrused_; } inline bool isEmpty() const { return size() == 0; } bool isWeighted() const { return weighted_; } inline double sum() const; inline double sqSum() const; inline T minValue(int* index_of_min=0) const; inline T maxValue(int* index_of_max=0) const; inline T extreme(int* index_of_extr=0) const; //参数二nPolicy为,当样点个数为偶数时的取值方式=-1:较低中值,0:平均中值,1:较高中值 inline T median(int* index_of_median=0) const; inline double rms() const; inline double stdDev() const; inline double average() const; inline double variance() const; inline double normVariance() const; inline T clipVal(float ratio,bool upper) const; //!< require median; 0 <= ratio <= 1 TTypeSet vals_; protected: int nrused_; int minidx_; int maxidx_; T minval_; T maxval_; double sum_x; double sum_xx; double sum_w; double sum_wx; double sum_wxx; bool weighted_; inline bool IsZero( const T& t ) const { return !t; } mutable int curmedidx_; inline bool findMedIdx(T) const; inline bool addExceptMed(T,T); inline bool remExceptMed(T,T); }; inline bool isZero( const float& val ) { return fabs(val) < 1e-6 ? true:false; } inline bool isZero( const double& val ) { return fabs(val) < 1e-10 ? true:false; } inline bool TStatValue::IsZero( const float& val ) const { return fabs(val) < 1e-6 ? true:false; } inline bool TStatValue::IsZero( const double& val ) const { return fabs(val) < 1e-10 ? true:false; } template inline void TStatValue::clear() { sum_x = sum_w = sum_xx = sum_wx = sum_wxx = 0; nrused_ = minidx_ = maxidx_ = curmedidx_ = 0; minval_ = maxval_ = 0; vals_.erase(); } static int ___RmsScale___=4; template inline bool TStatValue::addExceptMed( T val, T wt ) { if ( nrused_ == 0 ) minval_ = maxval_ = val; else { if ( val < minval_ ) minval_ = val; if ( val > maxval_ ) maxval_ = val; } sum_x += val; sum_xx += val * val * ___RmsScale___*___RmsScale___; if ( isWeighted() ) { sum_w += wt; sum_wx += wt * val; sum_wxx += wt * val * val; } nrused_++; return true; } template inline bool TStatValue::remExceptMed( T val, T wt ) { if(nrused_ <= 0) return false; sum_x -= val; sum_xx -= val * val * ___RmsScale___*___RmsScale___; if ( isWeighted() ) { sum_w -= wt; sum_wx -= wt * val; sum_wxx -= wt * val * val; } nrused_--; return true; } template inline TStatValue& TStatValue::addValue( T val, T wt ) { if ( !addExceptMed(val, wt) ) return *this; vals_ += val; return *this; } template inline TStatValue& TStatValue::addValues( int sz, const T* data, const T* weights ) { for ( int idx=0; idx inline bool TStatValue::findMedIdx( T val ) const { if ( curmedidx_ >= vals_.size() ) curmedidx_ = 0; if ( vals_[curmedidx_] != val ) // oh well, need to search anyway { curmedidx_ = vals_.indexOf( val ); if ( curmedidx_ < 0 ) { curmedidx_ = 0; return false; } } return true; } template inline TStatValue& TStatValue::replaceValue( T oldval, T newval, T wt ) { while( findMedIdx(oldval) ) { remExceptMed( oldval, wt ); addExceptMed( newval, wt ); if ( vals_.isEmpty() ) break; vals_[curmedidx_] = newval; curmedidx_++; } return *this; } template inline TStatValue& TStatValue::removeValue( T val, T wt ) { while ( findMedIdx(val) ) { remExceptMed( val, wt ); if ( vals_.isEmpty() ) break; vals_.remove( curmedidx_ ); } return *this; } template inline double TStatValue::getValue( EStatType t ) const { switch ( t ) { case stCount: return size(); case stAverage: return average(); case stMedian: return median(); case stRMS: return rms(); case stStdDev: return stdDev(); case stVariance: return variance(); case stNormVariance:return normVariance(); case stMin: return minValue(); case stMax: return maxValue(); case stExtreme: return extreme(); case stSum: return sum(); case stSqSum: return sqSum(); } return 0; } template inline int TStatValue::getIndex( EStatType t ) const { int ret; switch ( t ) { case Min: minValue( &ret ); break; case Max: maxValue( &ret ); break; case Extreme: extreme( &ret ); break; case Median: median( &ret ); break; default: ret = 0; break; } return ret; } template inline double TStatValue::stdDev() const { double v = variance(); return v > 0 ? ::sqrt( v ) : 0; } template inline double TStatValue::average() const { if ( nrused_==0 ) return 0; if(!isWeighted()) return ((double)sum_x) / nrused_; return isZero(sum_w) ? 0 : ((double)sum_wx) / sum_w; } template inline T TStatValue::median( int* idx_of_med ) const { if ( idx_of_med ) *idx_of_med = 0; const int sz = vals_.size(); if ( sz < 2 ) return sz < 1 ? 0 : vals_[0]; //快速排序时,保存原始索引号 int* m_pSourceIndex=NULL; int mididx = sz / 2; T* valarr = const_cast( vals_.arr() ); if ( !idx_of_med ) CQuickSort::quickSort( valarr, sz ); else { if(m_pSourceIndex==NULL) m_pSourceIndex = new int[sz]; for(int i=0; i::quickSort( valarr, m_pSourceIndex, sz ); *idx_of_med = m_pSourceIndex[ mididx ]; } if ( sz%2 == 0 ) { //参数二nPolicy为,当样点个数为偶数时的取值方式=-1:较低中值,0:平均中值,1:较高中值 const int policy = 0; if ( policy == 0 ) { if(m_pSourceIndex) delete[] m_pSourceIndex; return (vals_[mididx] + vals_[mididx-1]) / 2; } else if ( policy == -1 ) { mididx--; if ( idx_of_med && m_pSourceIndex!=NULL ) *idx_of_med = m_pSourceIndex[ mididx ]; } } if(m_pSourceIndex) delete[] m_pSourceIndex; return vals_[ mididx ]; } template inline double TStatValue::sum() const { return sum_x; } template inline double TStatValue::sqSum() const { return sum_xx; } template inline double TStatValue::rms() const { if ( !isWeighted() ) return sqrt( ((double)sum_xx) / nrused_ ); return isZero(sum_w) ? 0 : sqrt( ((double)sum_wxx)/sum_w ); } template inline double TStatValue::variance() const { if ( nrused_ < 2 ) return 0; if ( !isWeighted() ) return ( sum_xx - (sum_x * ((double)sum_x) / nrused_) ) / (nrused_ - 1); return (sum_wxx - (sum_wx * ((double)sum_wx) / sum_w) ) / ( (nrused_-1) * ((double)sum_w) / nrused_ ); } template inline double TStatValue::normVariance() const { if ( nrused_ < 2 ) return 0; double fact = 0.1; double avg = average(); double var = variance(); return var / (avg*avg + fact*var); } template inline T TStatValue::minValue( int* index_of_min ) const { if ( index_of_min ) *index_of_min = minidx_; return minval_; } template inline T TStatValue::maxValue( int* index_of_max ) const { if ( index_of_max ) *index_of_max = maxidx_; return maxval_; } template inline T TStatValue::extreme( int* index_of_extr ) const { if ( index_of_extr ) *index_of_extr = 0; const T maxcmp = maxval_ < 0 ? -maxval_ : maxval_; const T mincmp = minval_ < 0 ? -minval_ : minval_; if ( maxcmp < mincmp ) { if ( index_of_extr ) *index_of_extr = minidx_; return minval_; } else { if ( index_of_extr ) *index_of_extr = maxidx_; return maxval_; } } template inline T TStatValue::clipVal( float ratio, bool upper ) const { median(); const int lastidx = vals_.size(); const float fidx = ratio * lastidx; const int idx = fidx <= 0 ? 0 : (fidx > lastidx ? lastidx : (int)fidx); return vals_[upper ? lastidx - idx : idx]; } }//namespace