我有以下图表和创建该图表的数据文件。我想让Matlab为我找到以下几点:
是否有人们知道哪些附加工具或软件包可以帮助我实现这一目标?我需要为一组地块做这个,所以合理自动化的东西是理想的。
我当然可以在Matlab中进行编程和计算部分,只需要加载数据文件,将其与曲线或函数匹配,然后找到各种[x,y]坐标。
答案 0 :(得分:6)
好的,这里有。据我所知,Matlab中没有任何一个例行程序来做你想做的事情;你必须自己制作一个。有几点需要注意:
显而易见,线性插值数据最容易做,应该没有问题
使用单个多项式插值也不是太难,尽管还有一些细节要处理。找到峰值应该是业务的第一顺序,这涉及找到衍生物的根(例如,使用roots
)。找到峰值后,通过将多项式偏移该量,找到所有期望水平(0%,20%,50%)的多项式的根。
使用三次样条(spline
)是最复杂的。对于具有完整立方体的所有子区间,应重复上面针对一般多项式概述的例程,同时考虑到最大值也可能位于子区间的边界的可能性,并且发现的任何根和极值可能位于区间之外立方有效的地方(也不要忘记x
使用的spline
- 偏移)。
以下是我对所有3种方法的实现:
%% Initialize
% ---------------------------
clc
clear all
% Create some bogus data
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);
%% Linear interpolation
% ---------------------------
% y peak
[y_peak, ind] = max(y);
x_peak = x(ind);
% y == 0%, 20%, 50%
lims = [0 20 50];
X = cell(size(lims));
Y = cell(size(lims));
for p = 1:numel(lims)
% the current level line to solve for
lim = y_peak*lims(p)/100;
% points before and after passing through the current limit
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)];
% interpolate and insert new data
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
% make a plot to verify
figure(1), clf, hold on
plot(x,y, 'r') % (this also plots the interpolation in this case)
plot(x_peak,y_peak, 'k.') % the peak
plot(X{1},Y{1}, 'r.') % the 0% intersects
plot(X{2},Y{2}, 'g.') % the 20% intersects
plot(X{3},Y{3}, 'b.') % the 50% intersects
% finish plot
xlabel('X'), ylabel('Y'), title('Linear interpolation')
legend(...
'Real data / interpolation',...
'peak',...
'0% intersects',...
'20% intersects',...
'50% intersects',...
'location', 'southeast')
%% Cubic splines
% ---------------------------
% Find cubic splines interpolation
pp = spline(x,y);
% Finding the peak requires finding the maxima of all cubics in all
% intervals. This means evaluating the value of the interpolation on
% the bounds of each interval, finding the roots of the derivative and
% evaluating the interpolation on those roots:
coefs = pp.coefs;
derivCoefs = bsxfun(@times, [3 2 1], coefs(:,1:3));
LB = pp.breaks(1:end-1).'; % lower bounds of all intervals
UB = pp.breaks(2:end).'; % upper bounds of all intervals
% rename for clarity
a = derivCoefs(:,1);
b = derivCoefs(:,2);
c = derivCoefs(:,3);
% collect and limits x-data
x_extrema = [...
LB, UB,...
LB + (-b + sqrt(b.*b - 4.*a.*c))./2./a,... % NOTE: data is offset by LB
LB + (-b - sqrt(b.*b - 4.*a.*c))./2./a,... % NOTE: data is offset by LB
];
x_extrema = x_extrema(imag(x_extrema) == 0);
x_extrema = x_extrema( x_extrema >= min(x(:)) & x_extrema <= max(x(:)) );
% NOW find the peak
[y_peak, ind] = max(ppval(pp, x_extrema(:)));
x_peak = x_extrema(ind);
% y == 0%, 20% and 50%
lims = [0 20 50];
X = cell(size(lims));
Y = cell(size(lims));
for p = 1:numel(lims)
% the current level line to solve for
lim = y_peak * lims(p)/100;
% find all 3 roots of all cubics
R = NaN(size(coefs,1), 3);
for ii = 1:size(coefs,1)
% offset coefficients to find the right intersects
C = coefs(ii,:);
C(end) = C(end)-lim;
% NOTE: data is offset by LB
Rr = roots(C) + LB(ii);
% prune roots
Rr( imag(Rr)~=0 ) = NaN;
Rr( Rr <= LB(ii) | Rr >= UB(ii) ) = NaN;
% insert results
R(ii,:) = Rr;
end
% now evaluate and save all valid points
X{p} = R(~isnan(R));
Y{p} = ppval(pp, X{p});
end
% as a sanity check, plot everything
xx = linspace(min(x(:)), max(x(:)), 20*numel(x));
yy = ppval(pp, xx);
figure(2), clf, hold on
plot(x,y, 'r') % the actual data
plot(xx,yy) % the cubic-splines interpolation
plot(x_peak,y_peak, 'k.') % the peak
plot(X{1},Y{1}, 'r.') % the 0% intersects
plot(X{2},Y{2}, 'g.') % the 20% intersects
plot(X{3},Y{3}, 'b.') % the 50% intersects
% finish plot
xlabel('X'), ylabel('Y'), title('Cubic splines interpolation')
legend(...
'Real data',...
'interpolation',...
'peak',...
'0% intersects',...
'20% intersects',...
'50% intersects',...
'location', 'southeast')
%% (N-1)th degree polynomial
% ---------------------------
% Find best interpolating polynomial
coefs = bsxfun(@power, x, n-1:-1:0) \ y;
% (alternatively, you can use polyfit() to do this, but this is faster)
% To find the peak, we'll have to find the roots of the derivative:
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);
% y == 0%, 20%, 50%
lims = [0 20 50];
X = cell(size(lims));
Y = cell(size(lims));
for p = 1:numel(lims)
% the current level line to solve for
lim = y_peak * lims(p)/100;
% offset coefficients as to find the right intersects
C = coefs;
C(end) = C(end)-lim;
% find and prune roots
R = roots(C);
R = R(imag(R) == 0);
R = R(R>min(x(:)) & R<max(x(:)));
% evaluate polynomial at these roots to get actual data
X{p} = R;
Y{p} = polyval(coefs, R);
end
% as a sanity check, plot everything
xx = linspace(min(x(:)), max(x(:)), 20*numel(x));
yy = polyval(coefs, xx);
figure(3), clf, hold on
plot(x,y, 'r') % the actual data
plot(xx,yy) % the cubic-splines interpolation
plot(x_peak,y_peak, 'k.') % the peak
plot(X{1},Y{1}, 'r.') % the 0% intersects
plot(X{2},Y{2}, 'g.') % the 20% intersects
plot(X{3},Y{3}, 'b.') % the 50% intersects
% finish plot
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%的交叉点在结束时都是错误的。因此,在复制粘贴之前,请更彻底地检查所有内容:)
正如我之前所说,正如您可以清楚地看到的,如果基础数据不适合,使用单个多项式进行插值通常会引入很多问题。此外,正如您可以从这些图中清楚地看到的那样,插值方法会非常强烈地影响交叉点的位置 - 至少一些想法对于您的数据所依据的模型至关重要
对于一般情况,三次样条通常是最好的方法。但是,这是一种通用方法,它将为您(以及您的出版物的读者)提供对数据准确性的错误认知。使用三次样条曲线可以首先了解相交是什么以及它们的行为方式,但是一旦真实模型变得更加清晰,就会回过头来重新审视分析。当然,当用于通过数据创建更平滑,更“视觉上吸引人”的曲线时,当然不使用三次样条线发布:)
答案 1 :(得分:1)
这不是一个完整的答案,但Matlab有内置函数,可以完成你想要做的大部分工作。
max
可以帮助您找到100%行polyfit
将以最小二乘意义为您提供一组多项式拟合。如果你想让它完全通过n个点,我相信你至少需要使用n-1度。roots
将为您提供刚刚找到的多项式的过零点。您还可以通过减去常数来使用它来查找20%和50%的交叉点。如果有多个交叉点,您将需要最接近您最初找到的最大交叉点的交叉点。 (你确定交叉点总是存在吗?)答案 2 :(得分:1)
找到全局最大使用MAX函数:
[ymax, imax] = max(y);
xmax = x(imax);
line(xlim,[ymax ymax],'Color','r')
line(xmax,ymax,'Color','r','LineStyle','o')
对于其他人,您可以使用出色的FileExchange提交 - "Fast and Robust Curve Intersections"。
y = 0处的行可以使用xlim
和yline0 = [0 0];
定义。然后就可以了
[x0, y0] = intersections(x,y,xlim,yline0); % function from FileExchange
x0close(1) = xmax - min(xmax-x0(x0<xmax));
x0close(2) = xmax + min(x0(x0>xmax)-xmax);
y0close = y0(ismember(x0,x0close));
line(xlim,yline0,'Color','r')
line(x0close,y0close,'Color','r','LineStyle','o')
除了
之外,对于20%和50%也可以这样做yline20 = repmat((ymax - y0(1))*0.2,1,2);
所有这一切都假定你想要你的情节中的海峡线的交叉点,而不是插值。