You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

461 lines
9.3 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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 T>
class TStatValue
{
public:
TStatValue( bool bWeighted = false) { weighted_ = bWeighted; clear(); }
inline void clear();
inline TStatValue<T>& addValue(T data,T weight=1);
inline TStatValue<T>& addValues(int sz,const T* data,const T* weights=0);
inline TStatValue<T>& replaceValue(T olddata,T newdata,T wt=1);
inline TStatValue<T>& removeValue(T data,T weight=1);
inline double getValue(EStatType, double wt = 1) const;
inline int getIndex(EStatType) const; //!< only for Median, Min and Max
inline TStatValue<T>& 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;
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>nPolicyΪ<79><CEAA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊż<CEAA><C5BC>ʱ<EFBFBD><CAB1>ȡֵ<C8A1><D6B5>ʽ=-1<><31><EFBFBD>ϵ<EFBFBD><CFB5><EFBFBD>ֵ<EFBFBD><D6B5>0:ƽ<><C6BD><EFBFBD><EFBFBD>ֵ<EFBFBD><D6B5>1<EFBFBD><31><EFBFBD>ϸ<EFBFBD><CFB8><EFBFBD>ֵ
inline T median(int* index_of_median=0) const;
inline double rms(double wt = 1) 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<T> 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;
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;
}
template <class T>
inline bool TStatValue<T>::IsZero( const T& val ) const
{
return fabs(val) < 1e-6 ? true:false;
}
template <class T> inline
void TStatValue<T>::clear()
{
sum_x = sum_w = sum_xx = sum_wx = sum_wxx = 0;
nrused_ = minidx_ = maxidx_ = curmedidx_ = 0;
minval_ = maxval_ = 0;
vals_.erase();
}
template <class T>
inline double TStatValue<T>::rms(double wt /*= 1*/) const
{
if ( !isWeighted() )
return sqrt( ((double)sum_xx * wt) / nrused_ );
return isZero(sum_w) ? 0 : sqrt( ((double)sum_wxx * wt)/sum_w );
}
template <class T> inline
bool TStatValue<T>::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;
if ( isWeighted() )
{
sum_w += wt;
sum_wx += wt * val;
sum_wxx += wt * val * val;
}
nrused_++;
return true;
}
template <class T> inline
bool TStatValue<T>::remExceptMed( T val, T wt )
{
if(nrused_ <= 0) return false;
sum_x -= val;
sum_xx -= val * val;
if ( isWeighted() )
{
sum_w -= wt;
sum_wx -= wt * val;
sum_wxx -= wt * val * val;
}
nrused_--;
return true;
}
template <class T> inline
TStatValue<T>& TStatValue<T>::addValue( T val, T wt )
{
if ( !addExceptMed(val, wt) )
return *this;
vals_ += val;
return *this;
}
template <class T> inline
TStatValue<T>& TStatValue<T>::addValues( int sz, const T* data, const T* weights )
{
for ( int idx=0; idx<sz; idx++ )
addValue( data[idx], weights ? weights[idx] : 1 );
return *this;
}
template <class T> inline
bool TStatValue<T>::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 <class T> inline
TStatValue<T>& TStatValue<T>::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 <class T> inline
TStatValue<T>& TStatValue<T>::removeValue( T val, T wt )
{
while ( findMedIdx(val) )
{
remExceptMed( val, wt );
if ( vals_.isEmpty() )
break;
vals_.remove( curmedidx_ );
}
return *this;
}
template <class T> inline
double TStatValue<T>::getValue( EStatType t, double wt ) const
{
switch ( t )
{
case stCount: return size();
case stAverage: return average();
case stMedian: return median();
case stRMS: return rms(wt);
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 <class T> inline
int TStatValue<T>::getIndex( EStatType t ) const
{
int ret;
switch ( t )
{
case stMin: minValue( &ret ); break;
case stMax: maxValue( &ret ); break;
case stExtreme: extreme( &ret ); break;
case stMedian: median( &ret ); break;
default: ret = 0; break;
}
return ret;
}
template <class T>
inline double TStatValue<T>::stdDev() const
{
double v = variance();
return v > 0 ? ::sqrt( v ) : 0;
}
template <class T>
inline double TStatValue<T>::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 <class T>
inline T TStatValue<T>::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];
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ԭʼ<D4AD><CABC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
int* m_pSourceIndex=NULL;
int mididx = sz / 2;
T* valarr = const_cast<T*>( vals_.arr() );
if ( !idx_of_med )
CQuickSort<T>::quickSort( valarr, sz );
else
{
if(m_pSourceIndex==NULL)
m_pSourceIndex = new int[sz];
for(int i=0; i<sz; i++) m_pSourceIndex[i]=i;
CQuickSort<T>::quickSort( valarr, m_pSourceIndex, sz );
*idx_of_med = m_pSourceIndex[ mididx ];
}
if ( sz%2 == 0 )
{
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>nPolicyΪ<79><CEAA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊż<CEAA><C5BC>ʱ<EFBFBD><CAB1>ȡֵ<C8A1><D6B5>ʽ=-1<><31><EFBFBD>ϵ<EFBFBD><CFB5><EFBFBD>ֵ<EFBFBD><D6B5>0:ƽ<><C6BD><EFBFBD><EFBFBD>ֵ<EFBFBD><D6B5>1<EFBFBD><31><EFBFBD>ϸ<EFBFBD><CFB8><EFBFBD>ֵ
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 <class T>
inline double TStatValue<T>::sum() const
{
return sum_x;
}
template <class T>
inline double TStatValue<T>::sqSum() const
{
return sum_xx;
}
template <class T>
inline double TStatValue<T>::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 <class T>
inline double TStatValue<T>::normVariance() const
{
if ( nrused_ < 2 ) return 0;
double fact = 0.1;
double avg = average();
double var = variance();
return var / (avg*avg + fact*var);
}
template <class T>
inline T TStatValue<T>::minValue( int* index_of_min ) const
{
if ( index_of_min ) *index_of_min = minidx_;
return minval_;
}
template <class T>
inline T TStatValue<T>::maxValue( int* index_of_max ) const
{
if ( index_of_max ) *index_of_max = maxidx_;
return maxval_;
}
template <class T>
inline T TStatValue<T>::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 <class T>
inline T TStatValue<T>::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