web-dev-qa-db-fra.com

Équivalent de `polyfit` pour un polynôme 2D dans Python

Je voudrais trouver une solution des moindres carrés pour les coefficients a dans

z = (a0 + a1*x + a2*y + a3*x**2 + a4*x**2*y + a5*x**2*y**2 + a6*y**2 +
     a7*x*y**2 + a8*x*y)

étant donné les tableaux x, y et z de longueur 20. Fondamentalement, je recherche l'équivalent de numpy.polyfit mais pour un polynôme 2D.

Cette question est similaire, mais la solution est fournie via MATLAB.

17
Justin Gabitzsch

Voici un exemple montrant comment vous pouvez utiliser numpy.linalg.lstsq pour cette tâche:

import numpy as np

x = np.linspace(0, 1, 20)
y = np.linspace(0, 1, 20)
X, Y = np.meshgrid(x, y, copy=False)
Z = X**2 + Y**2 + np.random.Rand(*X.shape)*0.01

X = X.flatten()
Y = Y.flatten()

A = np.array([X*0+1, X, Y, X**2, X**2*Y, X**2*Y**2, Y**2, X*Y**2, X*Y]).T
B = Z.flatten()

coeff, r, rank, s = np.linalg.lstsq(A, B)

les coefficients d'ajustement coeff sont:

array([ 0.00423365,  0.00224748,  0.00193344,  0.9982576 , -0.00594063,
        0.00834339,  0.99803901, -0.00536561,  0.00286598])

Notez que coeff[3] et coeff[6] correspondent respectivement à X**2 et Y**2, et ils sont proches de 1. car les données d'exemple ont été créées avec Z = X**2 + Y**2 + small_random_component.

13

Excellente réponse. Juste pour ajouter le code pour reconstruire la fonction en utilisant la solution des moindres carrés pour les coefficients a,

def poly2Dreco(X, Y, c):
    return (c[0] + X*c[1] + Y*c[2] + X**2*c[3] + X**2*Y*c[4] + X**2*Y**2*c[5] + 
           Y**2*c[6] + X*Y**2*c[7] + X*Y*c[8])
0
Francisco Grings

Sur la base des réponses de @Saullo et @Francisco, j'ai créé une fonction que j'ai trouvée utile:

def polyfit2d(x, y, z, kx=3, ky=3, order=None):
    '''
    Two dimensional polynomial fitting by least squares.
    Fits the functional form f(x,y) = z.

    Notes
    -----
    Resultant fit can be plotted with:
    np.polynomial.polynomial.polygrid2d(x, y, soln.reshape((kx+1, ky+1)))

    Parameters
    ----------
    x, y: array-like, 1d
        x and y coordinates.
    z: np.ndarray, 2d
        Surface to fit.
    kx, ky: int, default is 3
        Polynomial order in x and y, respectively.
    order: int or None, default is None
        If None, all coefficients up to maxiumum kx, ky, ie. up to and including x^kx*y^ky, are considered.
        If int, coefficients up to a maximum of kx+ky <= order are considered.

    Returns
    -------
    Return paramters from np.linalg.lstsq.

    soln: np.ndarray
        Array of polynomial coefficients.
    residuals: np.ndarray
    rank: int
    s: np.ndarray

    '''

    # grid coords
    x, y = np.meshgrid(x, y)
    # coefficient array, up to x^kx, y^ky
    coeffs = np.ones((kx+1, ky+1))

    # solve array
    a = np.zeros((coeffs.size, x.size))

    # for each coefficient produce array x^i, y^j
    for index, (j, i) in enumerate(np.ndindex(coeffs.shape)):
        # do not include powers greater than order
        if order is not None and i + j > order:
            arr = np.zeros_like(x)
        else:
            arr = coeffs[i, j] * x**i * y**j
        a[index] = arr.flatten()

    # do leastsq fitting and return leastsq result
    return np.linalg.lstsq(a.T, np.ravel(z), rcond=None)

Et l'ajustement résultant peut être visualisé avec:

fitted_surf = np.polynomial.polynomial.polygrid2d(x, y, soln.reshape((kx+1,ky+1)))
plt.matshow(fitted_surf)
0
Paddy Harrison