示例#1
0
def fit_shown_data(f='a*sin(x)+b', p='a=1.5, b', bg=None, a=None, command="", settings={}, axes="gca", **kwargs):
    """
    Loops over the shown data, performing a fit in a separate figure.
    Loops over the shown data, performing a fit in a separate figure.
    ***kwargs are sent to fit()
    """

    # get the axes
    if axes == "gca": axes = _pylab.gca()

    xlabel=axes.get_xlabel()
    ylabel=axes.get_ylabel()
    if xlabel == '' :
        xlabel='x'
    if ylabel == '' :
        ylabel='y'

    # get the xlimits
    xmin, xmax = axes.get_xlim()

    # get the output axes
    fn0 = axes.figure.number
    
    # create the data object for fitting
    d = _s.data.standard(xlabel,ylabel,None)

    # generate the model
    model = _s.models.curve(f=f, p=p, bg=bg, a=a, globs=globals())

    # loop over the data
    lines = axes.get_lines()
    results = []
    for n in range(len(lines)):
        line = lines[n]
        if isinstance(line, _mpl.lines.Line2D):
            # get the trimmed data from the line
            x, y    = line.get_data()
            x, y, e = _fun.trim_data(x,y,None,[xmin,xmax])

            # put together a data object with the right parameters
            d.path = "Line: "+line.get_label()
            d[xlabel] = x
            d[ylabel] = y
            settings['xscript'] = xlabel
            settings['yscript'] = ylabel

            # do the fit
            print '\n\n\nLINE:', n+1, '/', len(lines)
            model.fit_parameters = None
            settings['autopath'] = False
            settings['figure']   = axes.figure.number+1
            results.append(model.fit(d, command, settings))
            results[-1]['databox'] = d

            # make sure we didn't quit.
            if results[-1]['command'] == 'q': break

            # prepare for the next file.
            command=''
            if results[-1].has_key('settings'): settings = results[-1]['settings']

    # clean up
    settings = dict()
    _pylab.figure(fn0)
    return results
示例#2
0
def fit_shown_data(f='a*sin(x)+b',
                   p='a=1.5, b',
                   bg=None,
                   a=None,
                   command="",
                   settings={},
                   axes="gca",
                   **kwargs):
    """
    Loops over the shown data, performing a fit in a separate figure.
    Loops over the shown data, performing a fit in a separate figure.
    ***kwargs are sent to fit()
    """

    # get the axes
    if axes == "gca": axes = _pylab.gca()

    xlabel = axes.get_xlabel()
    ylabel = axes.get_ylabel()
    if xlabel == '':
        xlabel = 'x'
    if ylabel == '':
        ylabel = 'y'

    # get the xlimits
    xmin, xmax = axes.get_xlim()

    # get the output axes
    fn0 = axes.figure.number

    # create the data object for fitting
    d = _s.data.standard(xlabel, ylabel, None)

    # generate the model
    model = _s.models.curve(f=f, p=p, bg=bg, a=a, globs=globals())

    # loop over the data
    lines = axes.get_lines()
    results = []
    for n in range(len(lines)):
        line = lines[n]
        if isinstance(line, _mpl.lines.Line2D):
            # get the trimmed data from the line
            x, y = line.get_data()
            x, y, e = _fun.trim_data(x, y, None, [xmin, xmax])

            # put together a data object with the right parameters
            d.path = "Line: " + line.get_label()
            d[xlabel] = x
            d[ylabel] = y
            settings['xscript'] = xlabel
            settings['yscript'] = ylabel

            # do the fit
            print '\n\n\nLINE:', n + 1, '/', len(lines)
            model.fit_parameters = None
            settings['autopath'] = False
            settings['figure'] = axes.figure.number + 1
            results.append(model.fit(d, command, settings))
            results[-1]['databox'] = d

            # make sure we didn't quit.
            if results[-1]['command'] == 'q': break

            # prepare for the next file.
            command = ''
            if results[-1].has_key('settings'):
                settings = results[-1]['settings']

    # clean up
    settings = dict()
    _pylab.figure(fn0)
    return results
示例#3
0
    def fit(self, data, command="", settings={}):
        """
        This generates xdata, ydata, and eydata from the three scripts
        (or auto-sets the error and updates it depending on the fit),
        fits the data, stores the results (and scripts) in the data file's header
        and saves the data in a new file.

        data            instance of a data class
        command         initial interactive fit command
        interactive     set to False to automatically fit without confirmation
        """

        iterable_settings = ["min", "max", "xb1", "xb2", "auto_error", "subtract",
                             "smooth", "coarsen", "show_guess", "show_error",
                             "show_background", "plot_all", "xscript", "yscript",
                             "eyscript"]

        # dictionary of settings like "min" and "skip"
        default_settings = {"min"               : None,
                            "max"               : None,
                            "xb1"               : 0,
                            "xb2"               : -1,
                            "auto_error"        : False,
                            "subtract"          : False,
                            "smooth"            : 0,
                            "coarsen"           : 0,
                            "show_guess"        : False,
                            "show_error"        : True,
                            "show_background"   : True,
                            "plot_all"          : False,
                            "eyscript"          : None,
                            "output_path"       : None,
                            "output_columns"    : None,

                            "skip"              : True,
                            "guess"             : None,
                            "save_file"         : True,
                            "file_tag"          : 'fit_',
                            "figure"            : 0,
                            "autofit"           : False,
                            "fullsave"          : False,
                            }
        if not settings.has_key('eyscript'): default_settings["auto_error"] = True

        # fill in the non-supplied settings with defaults
        for k in default_settings.keys():
            if not k in settings.keys():
                settings[k] = default_settings[k]

        # determine the number of parallel fits from the yscript
        if _s.fun.is_iterable(settings['yscript']): number_of_fits = len(settings['yscript'])
        else:                                       number_of_fits = 1

        # In general we're going to have a list of datas and scripts etc, so make
        # sure we're in a position to do this.
        if not _s.fun.is_iterable(data): data = [data]

        # fill out the arrays so they match the number of fits
        while len(data) < number_of_fits: data.append(data[-1])

        # make sure the various settings are lists too
        for k in iterable_settings:

            # make them all iterable
            if not _s.fun.is_iterable(settings[k]):
                settings[k] = [settings[k]]

            # make sure they're all the right length
            while len(settings[k]) < number_of_fits: settings[k].append(settings[k][-1])

        # Initialize the fit_parameters (we haven't any yet!)
        fit_parameters = None
        fit_errors     = None
        
        format_figures = True

        # set up the figures
        axes2s = []
        axes1s = []
        figs  = []
        for n in range(len(data)):
            figs.append(_pylab.figure(settings["figure"]+n))
            figs[n].clear()
            axes2s.append(_pylab.subplot(211))
            axes1s.append(_pylab.subplot(212, sharex=axes2s[n]))
            axes2s[n].set_position([0.15, 0.78, 0.70, 0.13])
            axes1s[n].set_position([0.15, 0.08, 0.70, 0.64])

        # Now keep trying to fit until the user says its okay or gives up.
        hold_plot=False
        while True:

            # Plot everything.
            if hold_plot:
                hold_plot=False
            else:
                if settings["skip"]: print "Plotting but not optimizing... (<enter> to fit)"
                else:                print "Beginning fit routine..."

                # assemble all the data
                xdatas  = []
                ydatas  = []
                eydatas = []
                xs      = []
                ys      = []
                eys     = []
                for n in range(len(data)):
                    # get the data based on the scripts
                    xdatas.append(data[n](settings["xscript"][n]))
                    ydatas.append(data[n](settings["yscript"][n]))
                    if settings["eyscript"][n] == None: eydatas.append(xdatas[n]*0.0 + (max(ydatas[n])-min(ydatas[n]))/20.0)
                    else:                               eydatas.append(data[n](settings["eyscript"][n]))

                    # now sort the data in case it's jaggy!
                    matrix_to_sort = _n.array([xdatas[n], ydatas[n], eydatas[n]])
                    sorted_matrix  = _fun.sort_matrix(matrix_to_sort, 0)
                    xdatas[n]  = sorted_matrix[0]
                    ydatas[n]  = sorted_matrix[1]
                    eydatas[n] = sorted_matrix[2]

                    # now trim all the data based on xmin and xmax
                    xmin = settings["min"][n]
                    xmax = settings["max"][n]
                    if xmin==None: xmin = min(xdatas[n])-1
                    if xmax==None: xmax = max(xdatas[n])+1
                    [x, y, ey] = _fun.trim_data(xdatas[n], ydatas[n], eydatas[n], [xmin, xmax])

                    # smooth and coarsen
                    [x,y,ey] = _fun.smooth_data( x,y,ey,settings["smooth"][n])
                    [x,y,ey] = _fun.coarsen_data(x,y,ey,settings["coarsen"][n])

                    # append to the temporary trimmed data sets.
                    xs.append(x)
                    ys.append(y)
                    eys.append(ey)



                # now do the first optimization. Start by guessing parameters from
                # the data's shape. This writes self.p0
                if settings["guess"]==None:
                    self.guess(xs, ys, settings["xb1"], settings["xb2"])
                else:
                    self.write_to_p0(settings['guess'])

                print "\n  FUNCTION:"
                for s in self.function_string:
                    print "    "+s
                print "\n  GUESS:"
                for n in range(len(self.pnames)):
                    print "    "+self.pnames[n]+" = "+str(self.p0[n])
                print


                # now do the first optimization
                if not settings["skip"]:

                    # actually do the least-squares optimization
                    fit_output = self.optimize(xs, ys, eys, self.p0)

                    # optimize puts out a float if there's only one parameter. Annoying.
                    if not _s.fun.is_iterable(fit_output[0]):
                            fit_parameters = _n.array([fit_output[0]])
                    else:   fit_parameters = fit_output[0]

                    # If we're doing auto error, now we should scale the error so that
                    # the reduced xi^2 is 1
                    if settings["auto_error"]:

                        # guess the correction to the y-error we're fitting (sets the reduced chi^2 to 1)
                        rms = _n.sqrt(self.residuals_variance(fit_parameters,xs,ys,eys))
                        print "  initial reduced chi^2 =", list(rms**2)
                        print "  scaling errors by", list(rms), "and re-optimizing..."
                        for n in range(len(eys)):
                            eys[n]     = rms[n] * eys[n]
                            eydatas[n] = rms[n] * eydatas[n]

                        # optimize with new improved errors, using the old fit to start
                        fit_output = self.optimize(xs,ys,eys,p0=fit_parameters)

                        # optimize puts out a float if there's only one parameter. Annoying.
                        if not _s.fun.is_iterable(fit_output[0]):
                              fit_parameters = _n.array([fit_output[0]])
                        else: fit_parameters = fit_output[0]

                    # Now that the fitting is done, show the output

                    # grab all the information from fit_output
                    fit_covariance = fit_output[1]
                    fit_reduced_chi_squared = list(self.residuals_variance(fit_parameters,xs,ys,eys))
                    if not fit_covariance == None:
                        # get the error vector and correlation matrix from (scaled) covariance
                        [fit_errors, fit_correlation] = _fun.decompose_covariance(fit_covariance)
                    else:
                        print "  WARNING: No covariance matrix popped out of model.optimize()"
                        fit_errors      = fit_parameters
                        fit_correlation = None

                    print "  reduced chi^2 is now", fit_reduced_chi_squared

                    # print the parameters
                    print "\n  FUNCTION:"
                    for s in self.function_string:
                        print "    "+s
                    print "\n  FIT:"
                    for n in range(0,len(self.pnames)): print "    "+self.pnames[n]+" =", fit_parameters[n], "+/-", fit_errors[n]
                    print


                # get the data to plot and plot it.
                for n in range(len(axes1s)):

                    if settings["plot_all"][n]:
                        x_plot  = xdatas[n]
                        y_plot  = ydatas[n]
                        ey_plot = eydatas[n]
                        [x_plot, y_plot, ey_plot] = _fun.smooth_data (x_plot, y_plot, ey_plot, settings["smooth"][n])
                        [x_plot, y_plot, ey_plot] = _fun.coarsen_data(x_plot, y_plot, ey_plot, settings["coarsen"][n])

                    else:
                        # this data is already smoothed and coarsened before the fit.
                        x_plot  = xs[n]
                        y_plot  = ys[n]
                        ey_plot = eys[n]

                    # now plot everything

                    # set up the axes
                    axes1 = axes1s[n]
                    axes2 = axes2s[n]
                    _pylab.hold(True)
                    axes1.clear()
                    axes2.clear()

                    # by default, the thing to subtract is 0.
                    thing_to_subtract = y_plot*0.0

                    # get the fit data if we're supposed to so we can know the thing to subtract
                    if not fit_parameters==None:
                        # get the fit and fit background for plotting (so we can subtract it!)
                        y_fit            = self.evaluate  (fit_parameters, x_plot, n)
                        y_fit_background = self.background(fit_parameters, x_plot, n)
                        if settings["subtract"][n]: thing_to_subtract = y_fit_background

                    # plot the guess
                    if settings["show_guess"][n]:
                        y_guess = self.evaluate(self.p0, x_plot, n)
                        axes1.plot(x_plot, y_guess-thing_to_subtract, color='gray', label='guess')
                        if settings["show_background"]:
                            y_guess_background = self.background(self.p0, x_plot, n)
                            axes1.plot(x_plot, y_guess_background-thing_to_subtract, color='gray', linestyle='--', label='guess background')

                    # Plot the data
                    if settings["show_error"][n]:
                        axes1.errorbar(x_plot, y_plot-thing_to_subtract, ey_plot, linestyle='', marker='D', mfc='blue', mec='w', ecolor='b', label='data')
                    else:
                        axes1.plot(    x_plot, y_plot-thing_to_subtract,              linestyle='', marker='D', mfc='blue', mec='w', label='data')

                    # plot the fit
                    if not fit_parameters == None and not settings["skip"]:
                        axes1.plot(    x_plot, y_fit-thing_to_subtract,            color='red', label='fit')
                        if settings["show_background"][n]:
                            axes1.plot(x_plot, y_fit_background-thing_to_subtract, color='red', linestyle='--', label='fit background')

                        # plot the residuals in the upper graph
                        axes2.errorbar(x_plot, (y_plot-y_fit)/ey_plot, ey_plot*0.0+1.0, linestyle='',  marker='o', mfc='blue', mec='w', ecolor='b')
                        axes2.plot    (x_plot, 0*x_plot,                                linestyle='-', color='k')

                    # come up with a title
                    title1 = data[n].path

                    # second line of the title is the model
                    title2 = "eyscript="+str(settings["eyscript"][n])+", model: " + str(self.function_string[n])

                    # third line is the fit parameters
                    title3 = ""
                    if not settings["skip"] and not fit_parameters==None:
                        t = []
                        for i in range(0,len(self.pnames)):
                            t.append(self.pnames[i]+"=%.4g+/-%.2g" % (fit_parameters[i], fit_errors[i]))
                        title3 = title3+_fun.join(t[0:4],", ")
                        if len(t)>3: title3 = title3+'\n'+_fun.join(t[4:],", ")
                    else:
                        title3 = title3+"(no fit performed)"

                    # Start by formatting the previous plot
                    axes2.set_title(title1+"\n"+title2+"\nFit: "+title3)
                    axes1.set_xlabel(settings["xscript"][n])
                    axes1.set_ylabel(settings["yscript"][n])

                    # set the position of the legend
                    axes1.legend(loc=[1.01,0], borderpad=0.02, prop=_FontProperties(size=7))

                    # set the label spacing in the legend
                    axes1.get_legend().labelsep = 0.01

                    # set up the title label
                    axes2.title.set_horizontalalignment('right')
                    axes2.title.set_size(8)
                    axes2.title.set_position([1.0,1.010])

                    fig = _pylab.figure(axes1.get_figure().number)
                    if format_figures: _st.format_figure(fig)
                    _st.auto_zoom(axes1)
                    _pylab.draw()
                    _wx.Yield()