Fitting_Example

# Fitting Function Models¶

A functional model allows for the creation of data, fitting of experimental data, plotting and symbolic manipulation of mathematical models based on functions. This includes symbolic intergration, differentiation and algebra. This module requires sympy and numpy to work.

In [1]:

    from pyMez.Code.Analysis.Fitting import *
    import matplotlib.pyplot as plt
    import random
    from ipywidgets import *

    Importing pyMez, this should take roughly 30 seconds
    Importing Code.DataHandlers.GeneralModels
    It took 0.352035 s to import Code.DataHandlers.GeneralModels
    Importing Code.DataHandlers.HTMLModels
    It took 0.059006 s to import Code.DataHandlers.HTMLModels
    Importing Code.DataHandlers.NISTModels
    The module smithplot was not found,please put it on the python path
    It took 0.40404 s to import Code.DataHandlers.NISTModels
    Importing Code.DataHandlers.TouchstoneModels
    It took 0.0 s to import Code.DataHandlers.TouchstoneModels
    Importing Code.DataHandlers.XMLModels
    It took 0.044004 s to import Code.DataHandlers.XMLModels
    Importing Code.DataHandlers.ZipModels
    It took 0.004001 s to import Code.DataHandlers.ZipModels
    Importing Code.InstrumentControl.Experiments
    It took 1.188119 s to import Code.InstrumentControl.Experiments
    Importing Code.InstrumentControl.Instruments
    It took 0.0 s to import Code.InstrumentControl.Instruments
    Importing Code.Utils.Names
    It took 0.0 s to import Code.Utils.Names
    It took 2.051205 s to import all of the active modules

## Creation of a new functional model¶

1. Pass parameters as a list or a string separated by spaces
2. Pass variables as a list or a string seperated by spaces
3. Pass an equation that sympy understands

In [2]:

    line=FunctionalModel(parameters=["m","b"],variables="x",equation="m*x+b")

In [3]:

    # now line can be point on any line
    line(m=1.2,b=3,x=.1)

Out[3]:

    3.12

In [4]:

    # or you can set the parameters
    line.set_parameters(m=3,b=1)
    # now any x_list you pass it as an np.array can be calculated
    line(np.array([-1,0,1]))

Out[4]:

    array([-2,  1,  4])

In [5]:

    %matplotlib inline
    # This can be used to plot functions
    x_data=np.linspace(-10,10,1000)
    plt.plot(x_data,line(x_data))
    plt.title(str(line))
    plt.show()

In [6]:

    # if you have a data set it can actually fit the data using the model
    x_data=np.linspace(-10,10,1000)
    # make a line with a little noise
    y_data=np.array([2*x + 3.5 +random.gauss(0,5) for x in x_data])
    # now to fit we use line.fit_data
    line.fit_data(x_data,y_data)
    plt.plot(x_data,y_data,label="Data")
    plt.plot(x_data,line(x_data),"rx",label="Fit")
    plt.title(str(line))
    plt.legend(loc=4)
    plt.show()

In [7]:

    # this works with even more complicated functions
    gaussian=FunctionalModel(parameters="alpha x0 delta",variables="x",equation="alpha*exp(-1*(x-x0)^2/(2*delta**2))")

In [8]:

    print(gaussian)

    alpha*exp(-(x - x0)**2/(2*delta**2))

In [9]:

    # it also has a latex form
    x_data=np.linspace(-1,1,1000)
    plt.plot(x_data,gaussian(alpha=1,x0=.1,delta=.1,x=x_data))
    plt.title("${0}$".format(gaussian.to_latex()))

Out[9]:

    Text(0.5,1,'$\\alpha e^{- \\frac{\\left(x - x_{0}\\right)^{2}}{2 \\delta^{2}}}$')

In [10]:

    # Now you have the ability to add functions
    gauss_line=line+gaussian
    x_data=np.linspace(-1,1,1000)
    plt.plot(x_data,gauss_line(alpha=1,x0=.1,delta=.1,m=1.2,b=.1,x=x_data))
    plt.title("${0}$".format(gauss_line.to_latex()))

Out[10]:

    Text(0.5,1,'$\\alpha e^{- \\frac{\\left(x - x_{0}\\right)^{2}}{2 \\delta^{2}}} + b + m x$')

In [11]:

    # we can take a derivative
    print(gauss_line.d())
    # or a higher order one
    print(gauss_line.d(order=4))
    # or with respect to a parameter
    print(gauss_line.d(respect_to="delta"))

    -alpha*(2*x - 2*x0)*exp(-(x - x0)**2/(2*delta**2))/(2*delta**2) + m
    3*alpha*exp(-(x - x0)**2/(2*delta**2))/delta**4 - alpha*(2*x - 2*x0)**2*exp(-(x - x0)**2/(2*delta**2))/delta**6 - alpha*(2*x - 2*x0)*(8*x - 8*x0)*exp(-(x - x0)**2/(2*delta**2))/(8*delta**6) + alpha*(2*x - 2*x0)**4*exp(-(x - x0)**2/(2*delta**2))/(16*delta**8)
    alpha*(x - x0)**2*exp(-(x - x0)**2/(2*delta**2))/delta**3

In [12]:

    # we could even plot/fit using the derivative of any order
    gauss_line=line+gaussian
    x_data=np.linspace(-1,1,1000)
    gauss_line_d3=gauss_line.d(order=3)
    plt.plot(x_data,gauss_line_d3(alpha=1,x0=.1,delta=.1,m=1.2,b=.1,x=x_data))
    plt.title("${0}$".format(gauss_line_d3.to_latex()))

Out[12]:

    Text(0.5,1,'$\\frac{\\alpha}{2 \\delta^{4}} \\left(2 x - 2 x_{0}\\right) e^{- \\frac{\\left(x - x_{0}\\right)^{2}}{2 \\delta^{2}}} + \\frac{\\alpha}{4 \\delta^{4}} \\left(8 x - 8 x_{0}\\right) e^{- \\frac{\\left(x - x_{0}\\right)^{2}}{2 \\delta^{2}}} - \\frac{\\alpha}{8 \\delta^{6}} \\left(2 x - 2 x_{0}\\right)^{3} e^{- \\frac{\\left(x - x_{0}\\right)^{2}}{2 \\delta^{2}}}$')

In [13]:

    # or create data and fit it
    gauss_line=line+gaussian
    x_data=np.linspace(-1,1,1000)
    output_noise=np.random.normal(0,100,len(x_data))
    gauss_line_d3=gauss_line.d(order=3)
    y_data=gauss_line_d3(alpha=1,x0=.1,delta=.1,m=1.2,b=.1,x=x_data)+output_noise
    plt.plot(x_data,y_data)
    plt.title("${0}$".format(gauss_line_d3.to_latex()))
    fig=gauss_line_d3.plot_fit(x_data,
                               y_data,
                               **{"title":"Fit of Noisy Data","initial_guess":{"alpha":1,"x0":.1,"delta":.1,"m":1.2,"b":.1}})

In [14]:

    # now we can create a DataSimulator object to change the output noise on any FunctionalModel and fit it
    model=line+gaussian
    model_2=line+gaussian
    model.set_parameters(m=.5,b=3.4,delta=1,x0=10.,alpha=20)
    simulated_data=DataSimulator(model=model,output_noise_type="normal",output_noise_width=2.,output_noise_center=0.)
    simulated_data.set_x(-25,25,1000)
    plt.plot(simulated_data.x,simulated_data.data)
    fig=model_2.plot_fit(simulated_data.x,simulated_data.data,**{"title":False,"initial_guess":model.parameter_values})

In [15]:

    # now we can create an interactive plot
    # By passing a keyword set of parameters we can change which parameters that we interact with
    def plot_fit(**options):
        defaults= {"output_noise_type":"normal",
                  "output_noise_width":2,
                  "output_noise_center":0,
                  "m":.5,
                  "b":3.4,
                  "delta":1,
                  "x0":10,
                  "alpha":20,
                  "x_min":-25,
                  "x_max":25,
                  "number_points":1000}
        fit_options={}
        for key,value in defaults.items():
            fit_options[key]=value
        for key,value in options.items():
            fit_options[key]=value
        line=FunctionalModel(parameters=["m","b"],variables="x",equation="m*x+b")
        gaussian=FunctionalModel(parameters="alpha x0 delta",variables="x",equation="alpha*exp(-1*(x-x0)^2/(2*delta**2))")
        model=line+gaussian
        model_2=line+gaussian
        model.set_parameters(m=fit_options["m"],b=fit_options["b"],
                             delta=fit_options["delta"],x0=fit_options["x0"],alpha=fit_options["alpha"])
        simulated_data=DataSimulator(model=model,output_noise_type=fit_options["output_noise_type"],
                                     output_noise_width=fit_options["output_noise_width"],
                                     output_noise_center=fit_options["output_noise_center"])
        simulated_data.set_x(fit_options["x_min"],fit_options["x_max"],fit_options["number_points"])
        model_2.fit_data(simulated_data.x,simulated_data.data,**{"initial_guess":model.parameter_values})
        parameter_text="${0}$\n".format(sympy.latex(model_2.equation))
        for key,value in model_2.parameter_values.items():
            parameter_text=parameter_text+"{0} = {1:3.2g} \n".format(key,value)
        fig, (ax0, ax1, ax2) = plt.subplots(nrows=3)
        ax0.plot(simulated_data.x,simulated_data.data,label="Data")
        ax0.plot(simulated_data.x,model_2(simulated_data.x),"ro",label="Fit")
        ax0.legend()
        ax1.text(fit_options["x_max"]+7,
                 .1,
                 parameter_text,
                 fontsize=12)
        residuals=simulated_data.data-model_2(simulated_data.x)
        ax1.plot(simulated_data.x,residuals)
        ax2.hist(residuals)
        plt.show()

In [16]:

    interact(plot_fit,
             x0=(-20,20,1),
             alpha=(-100,100,1),
             m=(-2,2,.1),
             output_noise_width=(0,10,1),output_noise_type=["normal","uniform","triangular"])

Out[16]:

    <function __main__.plot_fit(**options)>

# Use case of electronic circuits¶

We can build electronic circuit models
$Z_{L}=i 2 \pi f L $
$Z_{C}=\frac{- i }{2 \pi f C} $
$Z_R=R$

In [17]:

    # We can use these models for simple electronic circuits
    Zc=FunctionalModel(variables="f",parameters="C",equation="-1*I/(2*pi*f*C)")
    Zl=FunctionalModel(variables="f",parameters="L",equation="I*(2*pi*f*L)")
    Zr=FunctionalModel(variables="f",parameters="R",equation="R")
    # now for frequencies
    frequency=np.linspace(0.1*10**3,1*10**9,10000)

    z_total=Zc**-1+Zl**-1+Zr**-1
    # a RLC circuit
    z_total=Zr+(Zc*Zl)/(Zc+Zl)
    # voltage across the resistor
    Vr=Zr/z_total

    # plot of voltage across a resistor
    Vr.set_parameters(C=10**(-10),L=10**-8,R=complex(50,0))
    plt.plot(frequency,abs(Vr(frequency)),label="Voltage Across R")
    plt.text(2*10**8,.5,"${0}$".format(sympy.latex(Vr.equation)),fontsize=34)
    plt.legend()

Out[17]:

    <matplotlib.legend.Legend at 0xb6042e8>

In [18]:

    # again we could create a simulated data set with output noise
    Vr_simulated_data=DataSimulator(model=Vr,
                                    output_noise_type="normal",
                                    output_noise_center=0,
                                    output_noise_width=.01)
    plt.plot(frequency,abs(Vr_simulated_data(frequency)),label="Functional Model")
    Vr_simulated_data.set_x(0,10**9,1000)
    plt.plot(Vr_simulated_data.x,Vr_simulated_data.data,label="Simulated Data With Output Noise")
    plt.title("Simulated Voltage Data")
    plt.legend(loc=4)

    C:\Users\sandersa\AppData\Local\Continuum\anaconda3\lib\site-packages\numpy\__init__.py:1: RuntimeWarning: divide by zero encountered in true_divide
      """
    C:\Users\sandersa\AppData\Local\Continuum\anaconda3\lib\site-packages\numpy\__init__.py:1: RuntimeWarning: invalid value encountered in true_divide
      """
    C:\Users\sandersa\AppData\Local\Continuum\anaconda3\lib\site-packages\numpy\core\numeric.py:501: ComplexWarning: Casting complex values to real discards the imaginary part
      return array(a, dtype, copy=False, order=order)

Out[18]:

    <matplotlib.legend.Legend at 0x985b8d0>