2013-03-03 13 views
7

Ho eseguito alcune modifiche in python usando numpy (che usa i minimi quadrati).Come eseguire un polinomio con punti fissi

Mi chiedevo se c'era un modo per adattarlo ai dati forzandoli attraverso alcuni punti fissi? In caso contrario, esiste un'altra libreria in python (o un'altra lingua a cui posso collegarmi - ad es. C)?

NOTA So che è possibile forzare i punto fisso spostandola all'origine e costringendo il termine costante a zero, come si nota qui, ma chiedevo più in generale per 2 o più punti fissi:

http://www.physicsforums.com/showthread.php?t=523360

+0

Non sono sicuro che l'interpolazione aiuterà qui - se il mio modello polinomiale non passa attraverso i punti giusti, nessuna quantità di interpolazione lo farà. – JPH

+2

Per punti fissi intendi sia xey sia fisso, giusto? Puoi usare http://en.wikipedia.org/wiki/Lagrange_polynomial per interpolare mentre aggiusti quei punti. – dranxo

+1

Grazie ... sembra interessante. Per il momento ho fatto un lavoro intorno al punto in cui aggiungo i punti fissi ai dati, e li appesantisco più del resto .... Sembra funzionare ok ish ... – JPH

risposta

8

Se si utilizza curve_fit(), è possibile utilizzare sigma argomento per dare ad ogni punto un peso. Il seguente esempio dà la prima, centrale, ultimo punto molto piccolo sigma, quindi il risultato raccordo sarà molto vicino a questi tre punti:

N = 20 
x = np.linspace(0, 2, N) 
np.random.seed(1) 
noise = np.random.randn(N)*0.2 
sigma =np.ones(N) 
sigma[[0, N//2, -1]] = 0.01 
pr = (-2, 3, 0, 1) 
y = 1+3.0*x**2-2*x**3+0.3*x**4 + noise 

def f(x, *p): 
    return np.poly1d(p)(x) 

p1, _ = optimize.curve_fit(f, x, y, (0, 0, 0, 0, 0), sigma=sigma) 
p2, _ = optimize.curve_fit(f, x, y, (0, 0, 0, 0, 0)) 

x2 = np.linspace(0, 2, 100) 
y2 = np.poly1d(p)(x2) 
plot(x, y, "o") 
plot(x2, f(x2, *p1), "r", label=u"fix three points") 
plot(x2, f(x2, *p2), "b", label=u"no fix") 
legend(loc="best") 

enter image description here

11

il matematicamente corretto modo di fare una misura con fissa i punti è usare Lagrange multipliers. Fondamentalmente, si modifica la funzione obiettivo che si desidera minimizzare, che è normalmente la somma dei quadrati dei residui, aggiungendo un parametro aggiuntivo per ogni punto fisso. Non sono riuscito ad alimentare una funzione obiettivo modificata a uno dei minimizzatori di scipy.Ma per un polinomio, si può capire i dettagli con carta e penna e convertire il vostro problema nella soluzione di un sistema lineare di equazioni:

def polyfit_with_fixed_points(n, x, y, xf, yf) : 
    mat = np.empty((n + 1 + len(xf),) * 2) 
    vec = np.empty((n + 1 + len(xf),)) 
    x_n = x**np.arange(2 * n + 1)[:, None] 
    yx_n = np.sum(x_n[:n + 1] * y, axis=1) 
    x_n = np.sum(x_n, axis=1) 
    idx = np.arange(n + 1) + np.arange(n + 1)[:, None] 
    mat[:n + 1, :n + 1] = np.take(x_n, idx) 
    xf_n = xf**np.arange(n + 1)[:, None] 
    mat[:n + 1, n + 1:] = xf_n/2 
    mat[n + 1:, :n + 1] = xf_n.T 
    mat[n + 1:, n + 1:] = 0 
    vec[:n + 1] = yx_n 
    vec[n + 1:] = yf 
    params = np.linalg.solve(mat, vec) 
    return params[:n + 1] 

Per verificare che funziona, provare quanto segue, dove n è il numero di punti, d il grado del polinomio e f il numero di punti fissi:

n, d, f = 50, 8, 3 
x = np.random.rand(n) 
xf = np.random.rand(f) 
poly = np.polynomial.Polynomial(np.random.rand(d + 1)) 
y = poly(x) + np.random.rand(n) - 0.5 
yf = np.random.uniform(np.min(y), np.max(y), size=(f,)) 
params = polyfit_with_fixed(d, x , y, xf, yf) 
poly = np.polynomial.Polynomial(params) 
xx = np.linspace(0, 1, 1000) 
plt.plot(x, y, 'bo') 
plt.plot(xf, yf, 'ro') 
plt.plot(xx, poly(xx), '-') 
plt.show() 

enter image description here

e naturalmente il polinomio montato va exac tly attraverso i punti:

>>> yf 
array([ 1.03101335, 2.94879161, 2.87288739]) 
>>> poly(xf) 
array([ 1.03101335, 2.94879161, 2.87288739]) 
+0

Se si utilizza il poly1d() costruttore suggerito qui: https://docs.scipy.org/doc/numpy/reference/generated/numpy.polyfit.html, l'ordine della fetta params è il contrario di quello che ci si aspetta. La semplice soluzione è di cambiare 'return params [: n + 1]' a 'restituisce params [: n + 1] [:: - 1]'. – ijustlovemath

4

Un modo semplice ed immediato è quello di utilizzare vincolata minimi quadrati cui vincoli sono ponderati con un numero abbastanza grande M, come:

from numpy import dot 
from numpy.linalg import solve 
from numpy.polynomial.polynomial import Polynomial as P, polyvander as V 

def clsq(A, b, C, d, M= 1e5): 
    """A simple constrained least squared solution of Ax= b, s.t. Cx= d, 
    based on the idea of weighting constraints with a largish number M.""" 
    return solve(dot(A.T, A)+ M* dot(C.T, C), dot(A.T, b)+ M* dot(C.T, d)) 

def cpf(x, y, x_c, y_c, n, M= 1e5): 
    """Constrained polynomial fit based on clsq solution.""" 
    return P(clsq(V(x, n), y, V(x_c, n), y_c, M)) 

Ovviamente questo non è poi un inclusiva pallottola d'argento soluzione, ma a quanto pare sembra funzionare bene ragionevole con un esempio semplice (for M in [0, 4, 24, 124, 624, 3124]):

In []: x= linspace(-6, 6, 23) 
In []: y= sin(x)+ 4e-1* rand(len(x))- 2e-1 
In []: x_f, y_f= linspace(-(3./ 2)* pi, (3./ 2)* pi, 4), array([1, -1, 1, -1]) 
In []: n, x_s= 5, linspace(-6, 6, 123)  

In []: plot(x, y, 'bo', x_f, y_f, 'bs', x_s, sin(x_s), 'b--') 
Out[]: <snip> 

In []: for M in 5** (arange(6))- 1: 
    ....:  plot(x_s, cpf(x, y, x_f, y_f, n, M)(x_s)) 
    ....: 
Out[]: <snip> 

In []: ylim([-1.5, 1.5]) 
Out[]: <snip> 
In []: show() 

e l'uscita di produzione come: fits with progressive larger M

Edit: 'esatto' soluzione Aggiunto:

from numpy import dot 
from numpy.linalg import solve 
from numpy.polynomial.polynomial import Polynomial as P, polyvander as V 
from scipy.linalg import qr 

def solve_ns(A, b): return solve(dot(A.T, A), dot(A.T, b)) 

def clsq(A, b, C, d): 
    """An 'exact' constrained least squared solution of Ax= b, s.t. Cx= d""" 
    p= C.shape[0] 
    Q, R= qr(C.T) 
    xr, AQ= solve(R[:p].T, d), dot(A, Q) 
    xaq= solve_ns(AQ[:, p:], b- dot(AQ[:, :p], xr)) 
    return dot(Q[:, :p], xr)+ dot(Q[:, p:], xaq) 

def cpf(x, y, x_c, y_c, n): 
    """Constrained polynomial fit based on clsq solution.""" 
    return P(clsq(V(x, n), y, V(x_c, n), y_c)) 

e testare la misura:

In []: x= linspace(-6, 6, 23) 
In []: y= sin(x)+ 4e-1* rand(len(x))- 2e-1 
In []: x_f, y_f= linspace(-(3./ 2)* pi, (3./ 2)* pi, 4), array([1, -1, 1, -1]) 
In []: n, x_s= 5, linspace(-6, 6, 123) 
In []: p= cpf(x, y, x_f, y_f, n) 
In []: p(x_f) 
Out[]: array([ 1., -1., 1., -1.]) 
Problemi correlati