用二次曲线平滑小数据集

3
我正在进行一些特定的信号分析,需要一种能够平滑给定钟形分布曲线的方法。滑动平均方法不能产生我想要的结果。我希望保留我的拟合曲线的最小/最大值和一般形状,但解决采样中的不一致性。
简而言之:如果给出一组模拟简单二次曲线的数据,你会推荐哪种统计平滑方法?
如果可能,请提供实现、库或框架的参考。
谢谢 SO!
编辑:一些有用的数据
(一个可能的信号图)

alt text

深色的二次曲线是我“拟合”的轻色相连数据点的曲线。
在我的图表中,约为-44的样本是一个问题(即潜在的样本不一致性)。我需要这条曲线更好地“适应”分布,并克服不趋势的值。希望这能帮到您!

“通过“取样不一致”这个术语,您是指采样率是可变的,还是指信号噪声?” - Judge Maygarden
@Judge Maygarden - 我添加了一个可能的图表,显示一个值远离曲线的趋势。 - Rev316
你需要更好地描述你的数据。这是一个分布、时间序列还是其他什么?你想拟合一个二次函数,还是更灵活的模型?最佳模型将取决于你的数据生成方式。在给定模型类的情况下,有各种估计方法可供选择。 - Tristan
@Tristen,我确实想拟合一个二次函数(因此标题)。这是一段时间内的小阅读材料(3-10平均)。这应该足够了解信息。 - Rev316
4个回答

3
#include <iostream>
#include <math.h>

struct WeightedData 
{
double x;
double y;
double weight;
};

void findQuadraticFactors(WeightedData *data, double &a, double &b, double &c, unsigned int const datasize)
{
double w1 = 0.0;
double wx = 0.0, wx2 = 0.0, wx3 = 0.0, wx4 = 0.0;
double wy = 0.0, wyx = 0.0, wyx2 = 0.0;
double tmpx, tmpy;
double den;

for (unsigned int i = 0; i < datasize; ++i) 
    {
    double x = data[i].x;
    double y = data[i].y;
    double w = data[i].weight;  

    w1 += w;
    tmpx = w * x;
    wx += tmpx;
    tmpx *= x;
    wx2 += tmpx;
    tmpx *= x;
    wx3 += tmpx;
    tmpx *= x;
    wx4 += tmpx;
    tmpy = w * y;
    wy += tmpy;
    tmpy *= x;
    wyx += tmpy;
    tmpy *= x;
    wyx2 += tmpy;
    }

den = wx2 * wx2 * wx2 - 2.0 * wx3 * wx2 * wx + wx4 * wx * wx + wx3 * wx3 * w1 - wx4 * wx2 * w1;
if (den == 0.0) 
    {
    a = 0.0;
    b = 0.0;
    c = 0.0;
    }
else    
    {
    a = (wx * wx * wyx2 - wx2 * w1 * wyx2 - wx2 * wx * wyx + wx3 * w1 * wyx + wx2 * wx2 * wy - wx3 * wx * wy) / den;
    b = (-wx2 * wx * wyx2 + wx3 * w1 * wyx2 + wx2 * wx2 * wyx - wx4 * w1 * wyx - wx3 * wx2 * wy + wx4 * wx * wy) / den;
    c = (wx2 * wx2 * wyx2 - wx3 * wx * wyx2 - wx3 * wx2 * wyx + wx4 * wx * wyx + wx3 * wx3 * wy - wx4 * wx2 * wy) / den;
    }

}

double findY(double const a, double const b, double const c, double const x)
{       
return a * x * x + b * x + c; 
};




int main(int argc, char* argv[])
{
WeightedData data[9];
data[0].weight=1; data[0].x=1; data[0].y=-52.0; 
data[1].weight=1; data[1].x=2; data[1].y=-48.0; 
data[2].weight=1; data[2].x=3; data[2].y=-43.0; 
data[3].weight=1; data[3].x=4; data[3].y=-44.0; 
data[4].weight=1; data[4].x=5; data[4].y=-35.0; 
data[5].weight=1; data[5].x=6; data[5].y=-31.0; 
data[6].weight=1; data[6].x=7; data[6].y=-32.0; 
data[7].weight=1; data[7].x=8; data[7].y=-43.0; 
data[8].weight=1; data[8].x=9; data[8].y=-52.0; 

double a=0.0, b=0.0, c=0.0;
findQuadraticFactors(data, a, b, c, 9);
std::cout << " x \t y" << std::endl;
for (int i=0; i<9; ++i)
    {
    std::cout << " " << data[i].x << ", " << findY(a,b,c,data[i].x) << std::endl;
    }
}

3
“二次”曲线和“钟形”通常意味着高斯正态分布。获取最佳估计的高斯分布非常容易:您计算样本均值和方差,然后您的平滑近似为
y = exp(-squared(x-mean)/variance)

如果您想用二次曲线来近似平滑曲线,我建议计算具有最小平方误差的二次多项式。我无法记住这个公式,但如果您学过微积分,请写出总平方误差(逐点)的公式,并对您的二次函数系数进行微分。将一阶导数设置为零并解出最佳逼近值。 或者您可以查找相关信息。
最后,如果您只是想要一个看起来平滑的曲线来近似一组点,则三次样条是您最好的选择。这些曲线不一定有意义,但您将获得一个漂亮的平滑逼近。

0

要不试试简单的数字低通滤波器

y[0] = x[0];
for (i = 1; i < len; ++i)
    y[i] = a * x[i] + (1.0 - a) * y[i - 1];

在这种情况下,x[] 是您的输入数据,y[] 是过滤后的输出。 a 系数是一个介于 0 和 1 之间的值,您应该进行微调。当 a 接近 0 时,截止频率 减小,而 a 值为 1 时则复制输入。

感谢您提供的代码,但是这种方法的问题在于当系数降低到足以“修复”差异时,形状已经发生了很大变化,最小/最大拟合变得不准确。 - Rev316
如果是这种情况,那么你需要增加采样率才能使这种方法起作用。这是一个选项吗? - Judge Maygarden
很遗憾,它不是这样的。在我的情况下,一个平均样本簇会有3到10个。 - Rev316

0

也许您的移动平均参数设置不正确(样本窗口太小或太大)?

这只是在您的钟形曲线上叠加的噪声吗?噪声频率与您试图检索的信号有多接近?提供您正在尝试提取的内容的图片可能会帮助我们确定解决方案。

如果您可以合理猜测函数参数,可以尝试使用最小二乘拟合进行某种拟合算法。这些技术通常对噪声具有一定的免疫能力。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接