2012-09-23 27 views
5

enter image description here¿Cómo ajustar una curva por una serie de líneas segmentadas en Matlab?

Tengo una curva de registro simple como la anterior. ¿Hay alguna función en Matlab que pueda ajustarse a esta curva mediante líneas segmentadas y mostrar los puntos inicial y final de estos segmentos de línea? He revisado la caja de herramientas de ajuste de curva en matlab. Parece que se ajustan a la curva por una línea o por algunas funciones. No quiero ajustar curvas solo por una línea.

Si no hay una función directa, cualquier alternativa para lograr el mismo objetivo está bien conmigo. Mi objetivo es ajustar la curva mediante líneas segmentadas y obtener ubicaciones de los puntos finales de estos segmentos.

Respuesta

7

En primer lugar, su problema no se llama ajuste de curvas. El ajuste de curva es cuando tiene datos, y encuentra la mejor función que lo describe, en cierto sentido. Tú, por otro lado, quieres crear una aproximación lineal por partes de tu función.

que sugieren la siguiente estrategia:

  1. Dividir manualmente en secciones. El tamaño de sección debe depender de la derivada, derivada grande -> pequeña sección
  2. Muestra la función en los nodos entre las secciones
  3. Busque una interpolación lineal que pase por los puntos mencionados anteriormente.

Aquí hay un ejemplo de un código que hace eso. Puede ver que la línea roja (interpolación) está muy cerca de la función original, a pesar de la pequeña cantidad de secciones. Esto sucede debido al tamaño de sección adaptativo.

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

muchas gracias por las explicaciones. Lo siento por mi fondo superficial para la interpolación lineal y Matlab. Creo que lo que hiciste es genial. Sin embargo, tengo dificultades para modificar mis códigos en consecuencia. Mi información original, y, es un vector de fila 1 * 73 cuya distribución se parece a la gráfica normal en la solución de cjh. ¿Puede señalar cómo puede modificar sus códigos para mostrar el resultado final en un diagrama del eje log-log (No el log (log (x)) cálculo)? Muchas gracias de nuevo, – Cassie

5

solución adaptativa de Andrey proporciona un ajuste global más precisa. Sin embargo, si lo que quieres son segmentos de una longitud fija, aquí hay algo que debería funcionar, usando un método que también devuelve un conjunto completo de todos los valores ajustados. Podría ser vectorizado si se necesita velocidad.

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 - Buena respuesta en general. Por cierto, pensé que nuestros dos métodos se pueden mejorar aún más haciendo una minimización de la suma de todos los errores, en lugar de muestrear la función. Por ejemplo, en su primer gráfico, el segundo nodo se puede mover por debajo del valor de la función, para aumentar el ajuste total. –

+0

+1 - sí, estaba pensando lo mismo :) Especialmente para funciones convexas o cóncavas, ese tipo de método siempre superará a la interpolación lineal. – cjh

+0

@cjh, gracias por su ayuda también. ¿Qué quiere decir "segmento de longitud fija"? ¿Quiere decir que su solución hace que x se extienda en la misma longitud? – Cassie

0

Ésta no es una respuesta exacta a esta pregunta, pero desde que llegué aquí basado en una búsqueda, me gustaría responder a la cuestión relacionada de cómo crear (no encajar) una Función lineal por partes que pretende representar la media (o mediana, o alguna otra función) de los datos de intervalo en un diagrama de dispersión.

En primer lugar, una alternativa relacionada pero más sofisticada que usa la regresión, que aparentemente tiene some MATLAB code listed on the wikipedia page, es Multivariate adaptive regression splines.

La solución aquí es simplemente el cálculo de la media en intervalos superpuestos para obtener puntos

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 

Por ejemplo, para calcular la media sobre algunos X emparejado y datos Y tuve práctico con intervalos de longitud 0.1, que tiene más o menos un tercio de solapamiento cada uno (imagen ver dispersión) otro con:

Scatter plot example for Xdat and Ydat

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

x =

las columnas 1 a 8

0.0552 0.0868 0.1170 0.1475 0.1844 0.2173 0.2498 0.2834 

columnas 9 a través de 15

0.3182 0.3561 0.3875 0.4178 0.4494 0.4671 0.4822 

y =

las columnas 1 a 8

0.9992 0.9983 0.9971 0.9955 0.9927 0.9905 0.9876 0.9846 

columnas 9 a través de 15

0.9803 0.9750 0.9707 0.9653 0.9598 0.9560 0.9537 

Vemos que cuando x aumenta, y tiende a disminuir levemente y A partir de ahí, es bastante fácil dibujar segmentos de línea y/o realizar algún otro tipo de suavizado.

(Nótese que no consiguió vectorizar esta solución;. Una versión mucho más rápido podría asumirse si se ordena datos extendidos)

Cuestiones relacionadas