拉依达法则是基于统计的异常数据剔除方法,实现方法并不复杂。看到自己n年之前写的一个c++的实现,发现c++11之后的新特性可以拿来使用到这个function中去,使得函数本身更加条理,可读性更强。
当然这不是直接应用的版本,直接应用时,将这个filter实现为一个类更加科学实用一些,为了让代码看起来篇幅合适,就做了以下调整。但是以下代码是可以直接编译运行的。
当然,拉依达法则知适合处理数据量比较多的场景,采样点较少时要小心,小于10个点时此方法完全失效,点击这里可查看分析
#include <list>
#include <algorithm> // for_each
#include <numeric> // std::accumulate
#include <math.h> // sqrt
#include <stdio.h>
double layida_filter(std::list<double> &values)
{
static constexpr int THRD_THREE_SIGMA = 9;
static constexpr double THREE_SIGMA = 3.0;
int n = (int)values.size();
if (n == 0)
{
return 0.0; // nerver goes here
}
double dAver = std::accumulate(values.begin(), values.end(), 0.0)/n;
if (n <=THRD_THREE_SIGMA)
{
return dAver;
}
double dSigma(0.0);
std::for_each (values.begin(), values.end(), [&](const double d) {
dSigma += (d-dAver)*(d-dAver);
});
dSigma /= n-1;
dSigma = sqrt(dSigma);
for (auto it = values.begin();it!=values.end();) {
double delta = abs(*it - dAver);
if (delta > THREE_SIGMA*dSigma) {
it = values.erase(it);
} else {
it++;
}
}
if ((int)values.size() < n) {
return layida_filter(values);
}
return dAver;
}
std::list<double> _values; // used to store the buffer
constexpr static long unsigned int _avg_n = 30;
double apply(double data)
{
if (_values.size()>=_avg_n) {
_values.pop_front();
}
_values.push_back(data);
std::list<double> ls_values(_values);
double layida_avg = layida_filter(ls_values);
return layida_avg;
}
int main()
{
for (int i=0;i<20;++i)
{
printf("filter value: [%f]\n", apply(i*1.0));
}
return 0;
}