Equivalent of `polyfit` for a 2D polynomial in Python

后端 未结 3 2113
误落风尘
误落风尘 2020-11-29 05:45

I\'d like to find a least-squares solution for the a coefficients in

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


        
相关标签:
3条回答
  • 2020-11-29 06:32

    Based on the answers from @Saullo and @Francisco I have made a function which I have found helpful:

    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.ravel()
    
        # do leastsq fitting and return leastsq result
        return np.linalg.lstsq(a.T, np.ravel(z), rcond=None)
    

    And the resultant fit can be visualised with:

    fitted_surf = np.polynomial.polynomial.polyval2d(x, y, soln.reshape((kx+1,ky+1)))
    plt.matshow(fitted_surf)
    
    0 讨论(0)
  • 2020-11-29 06:38

    Here is an example showing how you can use numpy.linalg.lstsq for this task:

    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)
    

    the adjusting coefficients coeff are:

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

    Note that coeff[3] and coeff[6] respectively correspond to X**2 and Y**2, and they are close to 1. because the example data was created with Z = X**2 + Y**2 + small_random_component.

    0 讨论(0)
  • 2020-11-29 06:42

    Excellent answer by Saullo Castro. Just to add the code to reconstruct the function using the least-squares solution for the a coefficients,

    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 讨论(0)
提交回复
热议问题