微妙的MatLab函数拟合(按值分离,不是参数)

时间:2013-05-07 14:57:49

标签: matlab

所以我有不少(超过60000)个数据点 f(x_k)= k ,此处 k = 0,1,2,...,N

功能单调增加,视觉上看起来非常流畅。我希望能够找到拟合的 F(x),使得对于每个 x_k ,碰巧 k <= F(x_k)&lt; K + 1

我该如何解决这个问题?


数据示例

x       0     1     3     5     8    10    14    16    20    23    27    29    35    37    41
f(x)    0     1     2     3     4     5     6     7     8     9    10    11    12    13    14

plot

1 个答案:

答案 0 :(得分:2)

(这看起来有点像查找表。也许是某种形式的图像处理应用程序?我在过去的生活中做了一些工具,需要不需要的东西。)

这是一次性问题,还是经常这样做,所以你需要速度?

我把它扔进SLM。由于我没有这些数据,我无法测试它或者自己给你任何结果,但只要你使用足够数量的结,确保你所希望的质量确定没有问题。你需要在右侧有额外的结,因为它似乎接近垂直渐近线,因此是一个奇点。样条通常倾向于不喜欢奇点,因为它们仍然是多项式的核心。

更好的是,交换x和y轴来进行拟合,从而拟合x = f(y)。左端点不是渐近线,因此不再存在奇点。现在你需要做的只是将结果限制为单调增加,然后向下凹(因此在任何地方都是负二阶导数。)你需要更少的结用于逆拟合,但是要使用足够的结以使合适的质量适合你的目标

要使用反向拟合,只需在反方向插值,即SLMEVAL能够执行的操作。我会看到它对你提供的一小部分测试数据的影响(只有默认的结数):

x = [0 1 3 5 8 10 14 16 20 23 27 29 35 37 41];
y = [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14];
slm = slmengine(y,x,'plot','on','increasing','on');

enter image description here

所以合适似乎是合理的,但我注意到你的数据看起来有点颠簸。可能确实很难找到一个平滑的解决方案,但完全符合您的要求。

让我们看看它有多好:

[x;y;slmeval(x,slm,-1)]'
ans =
         0         0    0.0190
    1.0000    1.0000    0.9656
    3.0000    2.0000    2.0522
    5.0000    3.0000    2.9239
    8.0000    4.0000    4.1096
   10.0000    5.0000    4.8419
   14.0000    6.0000    6.1963
   16.0000    7.0000    6.8331
   20.0000    8.0000    8.0638
   23.0000    9.0000    8.9699
   27.0000   10.0000   10.1459
   29.0000   11.0000   10.7088
   35.0000   12.0000   12.2942
   37.0000   13.0000   12.8285
   41.0000   14.0000       NaN

它完全错过了最后一点,拒绝推断。但其余的并不遥远。但是他们确实没有达到你的要求,因为

是不正确的
k <= F(x_k) < k+1

当然,我没有在规范中建立具有这种要求的样条。如果我一般尝试解决这个问题,我可能会编写直接估计曲线上的值的代码,没有样条中介。然后,我可以轻松地强制执行您的约束,找到满足您的误差条要求和单调性的最平滑点集,这也尽可能接近原始数据。当然,这将涉及一个大型系统解决,具有60k未知数。我不知道lsqlin如何处理这个大问题,但如果时间成问题,还有其他解决方案可能会这样做。

再次,将您的测试数据作为一个小规模示例:

x = [0 1 3 5 8 10 14 16 20 23 27 29 35 37 41]';
n = numel(x);
k = (0:(n-1))';

% The "unrounding" bound constraints
LB = k;
UB = k+1;

% The best fit possible
Afit = speye(n,n);

% And as smooth as possible
ind = 1:(n-2);
% could do this with diff of course
dx1 = x(ind+1) - x(ind);
dx2 = x(ind+2) - x(ind + 1);

% central second finite difference, for unequal spacing
den = dx1.*dx2.*(dx1 + dx2)/2;
Areg = spdiags([dx2./den,-(dx1 + dx2)./den,dx1./den],[0 1 2],n-2,n);
rhs = [k;zeros(n-2,1)];

% monotonicity constraints...
Amono = spdiags(repmat([1 -1],14,1),[0 1],n-1,n);
bmono = zeros(n-1,1);

% choose a value for r, that allows you to control the smoothness
% larger values of r will make the curve smoother, but the bounds
% will always be enforced. I played with it, and r = 5 seemed a
% reasonable compromise here.
r = 5;
yhat = lsqlin([Afit;r*Areg],rhs,Amono,bmono,[],[],LB,UB);

lsqlin有点不高兴,因为此时它不处理这种形式的稀疏问题。因此,它会发出一个警告,即它将问题转换为完整问题。

Warning: Large-scale algorithm can handle bound constraints only;
    using medium-scale algorithm instead. 
> In lsqlin at 270 
Warning: This problem formulation not yet available for sparse matrices.
Converting to full to solve. 
> In lsqlin at 320 
Optimization terminated.

当然,对于60k未知数的问题,这种转换完全是不可接受的。不要在60k数据点上尝试!!!!!!!!!!!!!!!!你的电脑会被冻结。

它是怎么做的?

disp([x,k,yhat,k+1])
         0         0    0.4356    1.0000
    1.0000    1.0000    1.0000    2.0000
    3.0000    2.0000    2.0504    3.0000
    5.0000    3.0000    3.0000    4.0000
    8.0000    4.0000    4.2026    5.0000
   10.0000    5.0000    5.0000    6.0000
   14.0000    6.0000    6.2739    7.0000
   16.0000    7.0000    7.0000    8.0000
   20.0000    8.0000    8.0916    9.0000
   23.0000    9.0000    9.0000   10.0000
   27.0000   10.0000   10.2497   11.0000
   29.0000   11.0000   11.0000   12.0000
   35.0000   12.0000   12.2994   13.0000
   37.0000   13.0000   13.0000   14.0000
   41.0000   14.0000   14.0594   15.0000

它运作得很好,虽然它对你所遇到的大问题来说是一个猥亵的比例。也许还有另一个优化器(可能在TOMLAB或其他软件包中)可以处理大规模稀疏线性问题,受线性和约束约束的影响。你也可能希望将第一个点强制为零,但这是微不足道的。

最后一个选项,如果说1000点是可行的,使用上述方案一次一批地重建1010个曲线。 lsqlin应该能够毫无问题地处理那个大小的问题。在末端留下一些重叠,每个重叠区域中的5个点应该足够。然后在重叠区域中平均结果。