簡體   English   中英

在Matlab中查找繪圖中的點

[英]Finding points along a plot in Matlab

我有以下圖表和創建該圖表的數據文件。 我想讓Matlab為我找到以下幾點:

  1. [y,x]表示100%線所示的峰值
  2. [x]表示曲線穿過y = 0線的位置
  3. [x]其中y為第1部分中50%和20%的峰值。

是否有任何人們都知道的附加工具或軟件包可以幫助我實現這一目標? 我需要為一組圖表做這個,所以合理自動化的東西是理想的。

我當然可以在Matlab中編寫編程和計算部分,這只是能夠加載數據文件,將其與曲線或函數匹配,並找到各種[x,y]坐標。

數據圖

好的,這就行了。 據我所知,Matlab中沒有任何一個例行程序來做你想做的事情; 你必須自己制作一個。 有幾點需要注意:

  • 顯而易見,線性插值數據最容易做,應該沒有問題

  • 使用單個多項式插值也不是太難,盡管還有一些細節要處理。 尋找峰值應該是業務的第一順序,其涉及找到衍生物的roots (例如,使用roots )。 找到峰值后,通過將多項式偏移此量,找到所有所需級別(0%,20%,50%)的多項式的根。

  • 使用三次樣條( spline )是最復雜的。 對於具有完整立方體的所有子區間,應重復上面針對一般多項式概述的例程,同時考慮到最大值也可能位於子區間的邊界的可能性,並且發現的任何根和極值可能位於區間之外立方體有效的地方(也不要忘記spline使用的x偏移)。

這是我對所有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%的交叉點在結束時都是錯誤的。因此,在復制粘貼之前,請更徹底地檢查所有內容:)

正如我之前所說的,正如您可以清楚地看到的那樣,如果底層數據不適合,使用單個多項式進行插值通常會引入很多問題。 此外,正如您可以從這些圖中清楚地看到的那樣,插值方法會非常強烈地影響交叉點的位置 - 最重要的是您至少要了解哪些模型是您數據的基礎。

對於一般情況,三次樣條通常是最好的方法。 但是,這是一種通用方法,它將為您(以及您的出版物的讀者)提供對數據准確性的錯誤認知。 使用三次樣條曲線可以首先了解相交是什么以及它們的行為方式,但是一旦真實模型變得更加清晰,就會回過頭來重新審視分析。 當然,當用於通過數據創建更平滑,更“視覺上吸引人”的曲線時,當然不發布三次樣條曲線:)

這不是一個完整的答案,但Matlab具有內置函數,可以完成您想要做的大部分工作。

  • max可以幫助您找到100%的產品線
  • polyfit將以最小二乘意義為您提供一組多項式擬合。 如果你想讓它完全通過n個點,我相信你至少需要使用n-1度。
  • roots將為您提供剛剛找到的多項式的過零點。 您還可以通過減去常數來使用它來查找20%和50%的交叉點。 如果有多個交叉點,您將需要最接近您最初找到的最大交叉點的交叉點。 (你確定交叉點總是存在嗎?)

要找到全局最大使用MAX功能:

[ymax, imax] = max(y);
xmax = x(imax);
line(xlim,[ymax ymax],'Color','r')
line(xmax,ymax,'Color','r','LineStyle','o')

其余的你可以使用偉大的FileExchange提交 - “快速和穩健的曲線交叉點”

y = 0處的線可以用xlimyline0 = [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);

所有這一切都假定你想要你的情節中的海峽線的交叉點,而不是插值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM