2012-09-23 10 views
5

enter image description hereCome adattare una curva con una serie di linee segmentate in Matlab?

Ho una curva di loglog semplice come sopra. Esiste qualche funzione in Matlab che può adattare questa curva per linee segmentate e mostrare i punti iniziale e finale di questi segmenti di linea? Ho controllato la casella degli strumenti di adattamento della curva in MATLAB. Sembra che eseguano la curva in base a una riga o ad alcune funzioni. Non voglio curvare il fitting solo con una linea.

Se non esiste una funzione diretta, qualsiasi alternativa per raggiungere lo stesso obiettivo va bene per me. Il mio obiettivo è quello di adattare la curva per linee segmentate e ottenere posizioni dei punti finali di questi segmenti.

risposta

7

Prima di tutto, il problema non si chiama adattamento della curva. L'adattamento della curva avviene quando si hanno dati e si trova la migliore funzione che la descrive, in un certo senso. Tu, d'altra parte, vuoi creare un'approssimazione lineare a tratti della tua funzione.

suggerisco la seguente strategia:

  1. Split manualmente in sezioni. La dimensione della sezione dovrebbe dipendere dalla derivata, derivata grande -> piccola sezione
  2. Esempio della funzione ai nodi tra le sezioni
  3. Trova un'interpolazione lineare che passi attraverso i punti sopra menzionati.

Ecco un esempio di codice che lo fa. Potete vedere che la linea rossa (interpolazione) è molto vicina alla funzione originale, nonostante la piccola quantità di sezioni. Ciò accade a causa della dimensione della sezione adattiva.

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

La ringrazio molto per le spiegazioni. Scusa per il mio sfondo poco profondo per l'interpolazione lineare e Matlab. Penso che quello che hai fatto sia fantastico. Tuttavia, ho difficoltà a modificare i miei codici di conseguenza. I miei dati originali, y, sono un vettore di riga di 1 * 73 la cui distribuzione sembra la trama normale nella soluzione di cjh. Puoi indicare come puoi modificare i tuoi codici per mostrare il risultato finale in un grafico degli assi log-log (non il log (log (x)) calcolo)? Grazie mille ancora, – Cassie

5

soluzione adattiva di Andrey fornisce una misura complessiva più accurata. Se quello che vuoi sono segmenti di lunghezza fissa, tuttavia, ecco qualcosa che dovrebbe funzionare, usando un metodo che restituisce anche un insieme completo di tutti i valori adattati. Potrebbe essere vettorizzato se è necessaria la velocità.

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 - Buona risposta generale. A proposito, ho solo pensato che entrambi i nostri metodi possono essere ulteriormente migliorati facendo una minimizzazione della somma di tutti gli errori, invece di campionare la funzione. Ad esempio, nel primo grafico, il secondo nodo può essere spostato sotto il valore della funzione, per aumentare l'adattamento totale. –

+0

+1 - sì, stavo solo pensando la stessa cosa :) Soprattutto per le funzioni convesse o concave, quel tipo di metodo avrebbe sempre sovraperformato l'interpolazione lineare. – cjh

+0

@ cjh, grazie anche per il tuo aiuto. Cosa intendi per "segmento di lunghezza fissa"? Intendi dire che la tua soluzione rende x diffuso nella stessa lunghezza? – Cassie

0

Questa non è una risposta precisa a questa domanda, ma da quando sono arrivato qui sulla base di una ricerca, vorrei rispondere alla relativa domanda di come creare (non in forma) una funzione lineare a tratti che è intesa a rappresentare la media (o mediana, o qualche altra altra funzione) dei dati di intervallo in un grafico a dispersione.

In primo luogo, un'alternativa correlata ma più sofisticata che utilizza la regressione, che apparentemente ha some MATLAB code listed on the wikipedia page, è Multivariate adaptive regression splines.

La soluzione qui è solo per calcolare la media su intervalli sovrapposti per ottenere punti

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 

Per esempio, per calcolare la media su alcune X associato e dati Y che avevo a portata di mano con intervalli di lunghezza 0.1 avente approssimativamente 1/3 sovrappongono tra loro (vedi immagine scatter) con:

Scatter plot example for Xdat and Ydat

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

x =

colonne da 1 a 8

012.397.

colonne 9 attraverso 15

0.3182 0.3561 0.3875 0.4178 0.4494 0.4671 0.4822 

y =

colonne da 1 a 8

0.9992 0.9983 0.9971 0.9955 0.9927 0.9905 0.9876 0.9846 

colonne 9 a 15

0.9803 0.9750 0.9707 0.9653 0.9598 0.9560 0.9537 

Vediamo che al crescere di x, y tende a diminuire leggermente y. Da lì, è abbastanza facile disegnare segmenti di linea e/o eseguire un altro tipo di levigatura.

(Si noti che non ha cercato di vettorizzare di questa soluzione,. Una versione molto più veloce potrebbe essere assunta se Xdata è ordinato)

Problemi correlati