2012-09-23 16 views
5

enter image description hereJak dopasować krzywą za pomocą serii segmentowanych linii w programie Matlab?

Mam prostą krzywą loglog jak wyżej. Czy w Matlab jest jakaś funkcja, która może pasować do tej krzywej za pomocą segmentowanych linii i wyświetlać początkowy i końcowy punkt tych segmentów linii? Sprawdziłem zestaw narzędzi do dopasowywania krzywych w programie Matlab. Wydaje się, że dopasowują krzywą za pomocą jednej linii lub niektórych funkcji. Nie chcę dopasowywać krzywej tylko przez jedną linię.

Jeśli nie ma funkcji bezpośredniej, każda alternatywa dla osiągnięcia tego samego celu jest dla mnie dobra. Moim celem jest dopasowanie krzywej za pomocą segmentowanych linii i uzyskanie lokalizacji punktów końcowych tych segmentów.

Odpowiedz

7

Po pierwsze, twój problem nie jest nazywany dopasowaniem krzywej. Dopasowanie krzywej polega na tym, że masz dane i znajdujesz w pewnym sensie najlepszą funkcję, która je opisuje. Ty, z drugiej strony, chcesz stworzyć fragmentaryczne liniowe przybliżenie swojej funkcji.

Proponuję następującą strategię:

  1. Splitu ręcznie do sekcji. Rozmiar przekroju powinien zależeć od pochodnej, dużej pochodnej -> małej sekcji
  2. Próbkować funkcję w węzłach między sekcjami
  3. Znaleźć interpolację liniową, która przechodzi przez punkty wymienione powyżej.

Oto przykład kodu, który to robi. Widać, że czerwona linia (interpolacja) jest bardzo zbliżona do pierwotnej funkcji, pomimo małej ilości sekcji. Dzieje się tak ze względu na rozmiar sekcji adaptacyjnej.

enter image description here

function fitLogLog() 
    x = 2:1000; 
    y = log(log(x)); 

    %# Find section sizes, by using an inverse of the approximation of the derivative 
    numOfSections = 20; 
    indexes = round(linspace(1,numel(y),numOfSections)); 
    derivativeApprox = diff(y(indexes)); 
    inverseDerivative = 1./derivativeApprox; 
    weightOfSection = inverseDerivative/sum(inverseDerivative); 
    totalRange = max(x(:))-min(x(:)); 
    sectionSize = weightOfSection.* totalRange; 

    %# The relevant nodes 
    xNodes = x(1) + [ 0 cumsum(sectionSize)]; 
    yNodes = log(log(xNodes)); 

    figure;plot(x,y); 
    hold on; 
    plot (xNodes,yNodes,'r'); 
    scatter (xNodes,yNodes,'r'); 
    legend('log(log(x))','adaptive linear interpolation'); 
end 
+0

Dziękuję bardzo za wyjaśnienia. Przepraszamy za moje płytkie tło za interpolację liniową i Matlaba. Myślę, że to, co zrobiłeś, jest świetne. Jednak trudno mi odpowiednio zmodyfikować moje kody. Moje oryginalne dane, y, są wektorami rzędu 1 * 73, których rozkład wygląda jak normalny wykres w rozwiązaniu cjh. Czy możesz wskazać, w jaki sposób możesz modyfikować swoje kody, aby pokazać wynik końcowy w wykresie osi log-log (nie logi (log (x)))? Jeszcze raz bardzo dziękuję, – Cassie

5

rozwiązanie adaptacyjne Andrey zapewnia dokładniejsze dopasowanie ogólnej. Jeśli jednak chcesz mieć segmenty o ustalonej długości, to jest to coś, co powinno działać, używając metody, która zwraca również komplet wszystkich dopasowanych wartości. Może być wektoryzowany, jeśli potrzebna jest prędkość.

Nsamp = 1000;  %number of data samples on x-axis 
x = [1:Nsamp]; %this is your x-axis 
Nlines = 5;  %number of lines to fit 

fx = exp(-10*x/Nsamp); %generate something like your current data, f(x) 
gx = NaN(size(fx));  %this will hold your fitted lines, g(x) 

joins = round(linspace(1, Nsamp, Nlines+1)); %define equally spaced breaks along the x-axis 

dx = diff(x(joins)); %x-change 
df = diff(fx(joins)); %f(x)-change 

m = df./dx; %gradient for each section 

for i = 1:Nlines 
    x1 = joins(i); %start point 
    x2 = joins(i+1); %end point 
    gx(x1:x2) = fx(x1) + m(i)*(0:dx(i)); %compute line segment 
end 

subplot(2,1,1) 
h(1,:) = plot(x, fx, 'b', x, gx, 'k', joins, gx(joins), 'ro'); 
title('Normal Plot') 

subplot(2,1,2) 
h(2,:) = loglog(x, fx, 'b', x, gx, 'k', joins, gx(joins), 'ro'); 
title('Log Log Plot') 

for ip = 1:2 
    subplot(2,1,ip) 
    set(h(ip,:), 'LineWidth', 2) 
    legend('Data', 'Piecewise Linear', 'Location', 'NorthEastOutside') 
    legend boxoff 
end 

MATLAB plotted output

+0

+1 - ogólna dobra odpowiedź. Przy okazji, po prostu pomyślałem, że obie nasze metody można dalej ulepszyć, minimalizując sumę wszystkich błędów, zamiast próbkowania funkcji. Na przykład na pierwszym wykresie drugi węzeł może zostać przesunięty poniżej wartości funkcji, aby zwiększyć całkowite dopasowanie. –

+0

+1 - tak, ja po prostu myślałem to samo :) Szczególnie w przypadku funkcji wypukłych lub wklęsłych taka metoda zawsze przewyższałaby liniową interpolację. – cjh

+0

@cjh, dziękuję za pomoc. Co masz na myśli "segment o stałej długości"? Czy masz na myśli to, że twoje rozwiązanie powoduje, że x rozprzestrzenia się na tej samej długości? – Cassie

0

To nie jest dokładną odpowiedź na to pytanie, ale ponieważ ja przybyłem tutaj opiera się na poszukiwaniu, chciałbym odpowiedzieć na powiązane pytanie, w jaki sposób tworzyć (nie pasuje) Â elementarna funkcja liniowa, która ma reprezentować średnią (lub medianę, lub jakąś inną inną funkcję) danych interwałowych na wykresie punktowym.

Po pierwsze, powiązane, ale bardziej wyrafinowane alternatywne wykorzystanie regresji, która najwyraźniej ma wartość some MATLAB code listed on the wikipedia page, to Multivariate adaptive regression splines.

Rozwiązaniem tego problemu jest po prostu obliczyć średnią na nakładających odstępach czasu, aby zdobyć punkty

function [x, y] = intervalAggregate(Xdata, Ydata, aggFun, intStep, intOverlap) 
% intOverlap in [0, 1); 0 for no overlap of intervals, etc. 
% intStep this is the size of the interval being aggregated. 

minX = min(Xdata); 
maxX = max(Xdata); 

minY = min(Ydata); 
maxY = max(Ydata); 

intInc = intOverlap*intStep; %How far we advance each iteraction. 
if intOverlap <= 0 
    intInc = intStep; 
end 
nInt = ceil((maxX-minX)/intInc); %Number of aggregations 

parfor i = 1:nInt 
    xStart = minX + (i-1)*intInc; 
    xEnd = xStart + intStep; 
    intervalIndices = find((Xdata >= xStart) & (Xdata <= xEnd)); 
    x(i) = aggFun(Xdata(intervalIndices)); 
    y(i) = aggFun(Ydata(intervalIndices)); 
end 

Na przykład, aby obliczyć średnią nad jakimś sparowany X i Y danych miałem pod ręką z przerwami o długości 0.1 mający w przybliżeniu 1/3 zachodzą na siebie (patrz rozpraszania obrazu):

Scatter plot example for Xdat and Ydat

[x, y] = intervalAggregate (XDAT, Ydat, @mean, 0,1, 0,333)

x =

kolumny 1 do 8

0.0552 0.0868 0.1170 0.1475 0.1844 0.2173 0.2498 0.2834 

kolumny 9 do 15

0.3182 0.3561 0.3875 0.4178 0.4494 0.4671 0.4822 

Y =

kolumny od 1 do 8

0.9992 0.9983 0.9971 0.9955 0.9927 0.9905 0.9876 0.9846 

kolumny 9 do 15

0.9803 0.9750 0.9707 0.9653 0.9598 0.9560 0.9537 

Widać, że jako X wzrostów y dąży do zmniejszenia nieznacznego y. Stamtąd łatwo jest narysować odcinki linii i/lub wykonać inny rodzaj wygładzania.

(Zauważ, że nie próbują wektorować tego rozwiązania, a. Znacznie szybsza wersja mogłaby przyjąć jeśli xdata jest posortowana)

Powiązane problemy