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
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
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()