Python curve_fit with multiple independent variables

后端 未结 6 599
广开言路
广开言路 2020-11-30 00:27

Python\'s curve_fit calculates the best-fit parameters for a function with a single independent variable, but is there a way, using curve_fit or something else,

相关标签:
6条回答
  • 2020-11-30 00:56
    def func(X, a, b, c):
        x,y = X
        return np.log(a) + b*np.log(x) + c*np.log(y)
    
    # some artificially noisy data to fit
    x = np.linspace(0.1,1.1,101)
    y = np.linspace(1.,2., 101)
    a, b, c = 10., 4., 6.
    z = func((x,y), a, b, c) * 1 + np.random.random(101) / 100
    
    # initial guesses for a,b,c:
    p0 = 8., 2., 7.
    print curve_fit(func, (x,y), z, p0)
    
    0 讨论(0)
  • 2020-11-30 00:56

    optimizing a function with multiple input dimensions and a variable number of parameters

    This example shows how to fit a polynomial with a two dimensional input (R^2 -> R) by an increasing number of coefficients. The design is very flexible so that the callable f from curve_fit is defined once for any number of non-keyword arguments.

    minimal reproducible example

    import numpy as np
    import matplotlib.pyplot as plt
    from scipy.optimize import curve_fit
    
    def poly2d(xy, *coefficients):
        x = xy[:, 0]
        y = xy[:, 1]
        proj = x + y
        res = 0
        for order, coef in enumerate(coefficients):
            res += coef * proj ** order
        return res
    
    nx = 31
    ny = 21
    
    range_x = [-1.5, 1.5]
    range_y = [-1, 1]
    target_coefficients = (3, 0, -19, 7)
    
    xs = np.linspace(*range_x, nx)
    ys = np.linspace(*range_y, ny)
    im_x, im_y = np.meshgrid(xs, ys)
    xdata = np.c_[im_x.flatten(), im_y.flatten()]
    im_target = poly2d(xdata, *target_coefficients).reshape(ny, nx)
    
    fig, axs = plt.subplots(2, 3, figsize=(29.7, 21))
    axs = axs.flatten()
    
    ax = axs[0]
    ax.set_title('Unknown polynomial P(x+y)\n[secret coefficients: ' + str(target_coefficients) + ']')
    sm = ax.imshow(
        im_target,
        cmap = plt.get_cmap('coolwarm'),
        origin='lower'
        )
    fig.colorbar(sm, ax=ax)
    
    for order in range(5):
        ydata=im_target.flatten()
        popt, pcov = curve_fit(poly2d, xdata=xdata, ydata=ydata, p0=[0]*(order+1) )
    
        im_fit = poly2d(xdata, *popt).reshape(ny, nx)
    
        ax = axs[1+order]
        title = 'Fit O({:d}):'.format(order)
        for o, p in enumerate(popt):
            if o%2 == 0:
                title += '\n'
            if o == 0:
                title += ' {:=-{w}.1f} (x+y)^{:d}'.format(p, o, w=int(np.log10(max(abs(p), 1))) + 5)
            else:
                title += ' {:=+{w}.1f} (x+y)^{:d}'.format(p, o, w=int(np.log10(max(abs(p), 1))) + 5)
        title += '\nrms: {:.1f}'.format( np.mean((im_fit-im_target)**2)**.5 )
        ax.set_title(title)
        sm = ax.imshow(
            im_fit,
            cmap = plt.get_cmap('coolwarm'),
            origin='lower'
            )
        fig.colorbar(sm, ax=ax)
    
    for ax in axs.flatten():
        ax.set_xlabel('x')
        ax.set_ylabel('y')
    
    plt.show()
    

    P.S. The concept of this answer is identical to my other answer here, but the code example is way more clear. At the time given, I will delete the other answer.

    0 讨论(0)
  • 2020-11-30 00:58

    Yes, there is: simply give curve_fit a multi-dimensional array for xData.

    0 讨论(0)
  • 2020-11-30 01:02

    Yes. We can pass multiple variables for curve_fit. I have written a piece of code:

    import numpy as np
    x = np.random.randn(2,100)
    w = np.array([1.5,0.5]).reshape(1,2)
    esp = np.random.randn(1,100)
    y = np.dot(w,x)+esp
    y = y.reshape(100,)
    

    In the above code I have generated x a 2D data set in shape of (2,100) i.e, there are two variables with 100 data points. I have fit the dependent variable y with independent variables x with some noise.

    def model_func(x,w1,w2,b):
      w = np.array([w1,w2]).reshape(1,2)
      b = np.array([b]).reshape(1,1)
      y_p = np.dot(w,x)+b
      return y_p.reshape(100,)
    

    We have defined a model function that establishes relation between y & x.
    Note: The shape of output of the model function or predicted y should be (length of x,)

    popt, pcov = curve_fit(model_func,x,y)
    

    The popt is an 1D numpy array containing predicted parameters. In our case there are 3 parameters.

    0 讨论(0)
  • 2020-11-30 01:10

    Fitting to an unknown numer of parameters

    In this example, we try to reproduce some measured data measData. In this example measData is generated by the function measuredData(x, a=.2, b=-2, c=-.8, d=.1). I practice, we might have measured measData in a way - so we have no idea, how it is described mathematically. Hence the fit.

    We fit by a polynomial, which is described by the function polynomFit(inp, *args). As we want to try out different orders of polynomials, it is important to be flexible in the number of input parameters. The independent variables (x and y in your case) are encoded in the 'columns'/second dimension of inp.

    import numpy as np
    import matplotlib
    import matplotlib.pyplot as plt
    from scipy.optimize import curve_fit
    
    def measuredData(inp, a=.2, b=-2, c=-.8, d=.1):
        x=inp[:,0]
        y=inp[:,1]
        return a+b*x+c*x**2+d*x**3 +y
    
    def polynomFit(inp, *args):
        x=inp[:,0]
        y=inp[:,1]
        res=0
        for order in range(len(args)):
            print(14,order,args[order],x)
            res+=args[order] * x**order
        return res +y
    
    
    inpData=np.linspace(0,10,20).reshape(-1,2)
    inpDataStr=['({:.1f},{:.1f})'.format(a,b) for a,b in inpData]
    measData=measuredData(inpData)
    fig, ax = plt.subplots()
    ax.plot(np.arange(inpData.shape[0]), measData, label='measuered', marker='o', linestyle='none' )
    
    for order in range(5):
        print(27,inpData)
        print(28,measData)
        popt, pcov = curve_fit(polynomFit, xdata=inpData, ydata=measData, p0=[0]*(order+1) )
        fitData=polynomFit(inpData,*popt)
        ax.plot(np.arange(inpData.shape[0]), fitData, label='polyn. fit, order '+str(order), linestyle='--' )
        ax.legend( loc='upper left', bbox_to_anchor=(1.05, 1))
        print(order, popt)
    
    ax.set_xticklabels(inpDataStr, rotation=90)
    

    Result:

    0 讨论(0)
  • 2020-11-30 01:13

    You can pass curve_fit a multi-dimensional array for the independent variables, but then your func must accept the same thing. For example, calling this array X and unpacking it to x, y for clarity:

    import numpy as np
    from scipy.optimize import curve_fit
    
    def func(X, a, b, c):
        x,y = X
        return np.log(a) + b*np.log(x) + c*np.log(y)
    
    # some artificially noisy data to fit
    x = np.linspace(0.1,1.1,101)
    y = np.linspace(1.,2., 101)
    a, b, c = 10., 4., 6.
    z = func((x,y), a, b, c) * 1 + np.random.random(101) / 100
    
    # initial guesses for a,b,c:
    p0 = 8., 2., 7.
    print curve_fit(func, (x,y), z, p0)
    

    Gives the fit:

    (array([ 9.99933937,  3.99710083,  6.00875164]), array([[  1.75295644e-03,   9.34724308e-05,  -2.90150983e-04],
       [  9.34724308e-05,   5.09079478e-06,  -1.53939905e-05],
       [ -2.90150983e-04,  -1.53939905e-05,   4.84935731e-05]]))
    
    0 讨论(0)
提交回复
热议问题