好的,下面开始。据我所知,Matlab中没有内置程序可以实现你想要的功能,你需要自己编写一个程序。需要注意的几个事项:
显而易见,线性插值数据最容易处理,也不应该有什么问题。
使用单一多项式插值也不太难,不过需要注意更多细节。首要任务是找到峰值,这涉及到找到导数的根(例如使用roots
)。当找到峰值后,通过将多项式向上或向下偏移相应比例(0%,20%,50%),可以在所有所需级别上找到多项式的根。
使用三次样条插值(spline
)最为复杂。对于具有完整三阶段的所有子间隔,应重复上述用于一般多项式的例程,考虑到最大值也可能位于子间隔的边界上,并且任何找到的根和极值都可能在立方体有效的间隔之外(还要注意spline
使用的x偏移量)。
这是我对所有三种方法的实现:
clc
clear all
n = 25;
f = @(x) cos(x) .* sin(4*x/pi) + 0.5*rand(size(x));
x = sort( 2*pi * rand(n,1));
y = f(x);
[y_peak, ind] = max(y);
x_peak = x(ind);
lims = [0 20 50];
X = cell(size(lims));
Y = cell(size(lims));
for p = 1:numel(lims)
lim = y_peak*lims(p)/100;
after = (circshift(y<lim,1) & y>lim) | (circshift(y>lim,1) & y<lim);
after(1) = false;
before = circshift(after,-1);
xx = [x(before) x(after)];
yy = [y(before) y(after)];
new_X = x(before) - (y(before)-lim).*diff(xx,[],2)./diff(yy,[],2);
X{p} = new_X;
Y{p} = lim * ones(size(new_X));
end
figure(1), clf, hold on
plot(x,y, 'r')
plot(x_peak,y_peak, 'k.')
plot(X{1},Y{1}, 'r.')
plot(X{2},Y{2}, 'g.')
plot(X{3},Y{3}, 'b.')
xlabel('X'), ylabel('Y'), title('Linear interpolation')
legend(...
'Real data / interpolation',...
'peak',...
'0% intersects',...
'20% intersects',...
'50% intersects',...
'location', 'southeast')
pp = spline(x,y);
coefs = pp.coefs;
derivCoefs = bsxfun(@times, [3 2 1], coefs(:,1:3));
LB = pp.breaks(1:end-1).';
UB = pp.breaks(2:end).';
a = derivCoefs(:,1);
b = derivCoefs(:,2);
c = derivCoefs(:,3);
x_extrema = [...
LB, UB,...
LB + (-b + sqrt(b.*b - 4.*a.*c))./2./a,...
LB + (-b - sqrt(b.*b - 4.*a.*c))./2./a,...
];
x_extrema = x_extrema(imag(x_extrema) == 0);
x_extrema = x_extrema( x_extrema >= min(x(:)) & x_extrema <= max(x(:)) );
[y_peak, ind] = max(ppval(pp, x_extrema(:)));
x_peak = x_extrema(ind);
lims = [0 20 50];
X = cell(size(lims));
Y = cell(size(lims));
for p = 1:numel(lims)
lim = y_peak * lims(p)/100;
R = NaN(size(coefs,1), 3);
for ii = 1:size(coefs,1)
C = coefs(ii,:);
C(end) = C(end)-lim;
Rr = roots(C) + LB(ii);
Rr( imag(Rr)~=0 ) = NaN;
Rr( Rr <= LB(ii) | Rr >= UB(ii) ) = NaN;
R(ii,:) = Rr;
end
X{p} = R(~isnan(R));
Y{p} = ppval(pp, X{p});
end
xx = linspace(min(x(:)), max(x(:)), 20*numel(x));
yy = ppval(pp, xx);
figure(2), clf, hold on
plot(x,y, 'r')
plot(xx,yy)
plot(x_peak,y_peak, 'k.')
plot(X{1},Y{1}, 'r.')
plot(X{2},Y{2}, 'g.')
plot(X{3},Y{3}, 'b.')
xlabel('X'), ylabel('Y'), title('Cubic splines interpolation')
legend(...
'Real data',...
'interpolation',...
'peak',...
'0% intersects',...
'20% intersects',...
'50% intersects',...
'location', 'southeast')
coefs = bsxfun(@power, x, n-1:-1:0) \ y;
derivCoefs = (n-1:-1:1).' .* coefs(1:end-1);
Rderiv = roots(derivCoefs);
Rderiv = Rderiv(imag(Rderiv) == 0);
Rderiv = Rderiv(Rderiv >= min(x(:)) & Rderiv <= max(x(:)));
[y_peak, ind] = max(polyval(coefs, Rderiv));
x_peak = Rderiv(ind);
lims = [0 20 50];
X = cell(size(lims));
Y = cell(size(lims));
for p = 1:numel(lims)
lim = y_peak * lims(p)/100;
C = coefs;
C(end) = C(end)-lim;
R = roots(C);
R = R(imag(R) == 0);
R = R(R>min(x(:)) & R<max(x(:)));
X{p} = R;
Y{p} = polyval(coefs, R);
end
xx = linspace(min(x(:)), max(x(:)), 20*numel(x));
yy = polyval(coefs, xx);
figure(3), clf, hold on
plot(x,y, 'r')
plot(xx,yy)
plot(x_peak,y_peak, 'k.')
plot(X{1},Y{1}, 'r.')
plot(X{2},Y{2}, 'g.')
plot(X{3},Y{3}, 'b.')
xlabel('X'), ylabel('Y'), title('(N-1)th degree polynomial')
legend(...
'Real data',...
'interpolation',...
'peak',...
'0% intersects',...
'20% intersects',...
'50% intersects',...
'location', 'southeast')
这将导致三个图表:
(请注意,在(N-1)次多项式中会出现一些问题;20%的交叉点在末尾都是错误的。因此,在复制粘贴之前,请更加仔细地检查一切 :))
正如我之前所说,也正如您可以清楚地看到的那样,如果底层数据不适合单个多项式进行插值,则单个多项式进行插值通常会引入许多问题。而且,正如您可以从这些图表清楚地看到的那样,插值方法非常强烈地影响交点的位置 - 最重要的是,您必须至少有一些关于底层数据模型的想法。
对于一般情况,三次样条通常是最好的选择。然而,这是一种通用方法,并会给您(以及您的出版物读者)带来对数据准确性的虚假感知。使用三次样条获得有关相交点及其行为的第一个想法,但是一旦真实模型变得更加清晰,一定要回来重新审查您的分析。当只用于通过数据创建更平滑、更“视觉上吸引人”的曲线时,请勿使用三次样条进行发布 :)
y
值和y==0
的x
值将都是插值值...这确实是您想要的吗? - Rody Oldenhuis