def save_file(self, path="ask"): """ This will save all the header info and columns to an ascii file. """ if path == "ask": path = _dialogs.Save(self.file_extension, default_directory=self.directory) if path in ["", None]: print "Aborted." return False self.path = path # if the path exists, make a backup if _os.path.exists(path): _os.rename(path, path + ".backup") # get the delimiter if self.delimiter == None: delimiter = "\t" else: delimiter = self.delimiter # open the file and write the header f = open(path, 'w') for k in self.hkeys: # if this is a numpy array, turn it into a list if type(self.headers[k]) == type(array([])): self.headers[k] = self.headers[k].tolist() f.write(k + delimiter) # just write it f.write(str(self.headers[k]) + "\n") # now write the ckeys line f.write("\n") elements = [] for ckey in self.ckeys: elements.append(str(ckey)) f.write(_fun.join(elements, delimiter) + "\n") # now loop over the data for n in range(0, len(self[0])): # loop over each column elements = [] for m in range(0, len(self)): # write the data if there is any, otherwise, placeholder ("x") if n < len(self[m]): elements.append(str(self[m][n])) else: elements.append('_') f.write(_fun.join(elements, delimiter) + "\n") f.close()
def save_file(self, path="ask"): """ This will save all the header info and columns to an ascii file. """ if path=="ask": path = _dialogs.Save(self.file_extension, default_directory=self.directory) if path in ["", None]: print "Aborted." return False self.path=path # if the path exists, make a backup if _os.path.exists(path): _os.rename(path,path+".backup") # get the delimiter if self.delimiter==None: delimiter = "\t" else: delimiter = self.delimiter # open the file and write the header f = open(path, 'w') for k in self.hkeys: # if this is a numpy array, turn it into a list if type(self.headers[k]) == type(array([])): self.headers[k] = self.headers[k].tolist() f.write(k + delimiter) # just write it f.write(str(self.headers[k]) + "\n") # now write the ckeys line f.write("\n") elements = [] for ckey in self.ckeys: elements.append(str(ckey)) f.write(_fun.join(elements,delimiter)+"\n") # now loop over the data for n in range(0, len(self[0])): # loop over each column elements = [] for m in range(0, len(self)): # write the data if there is any, otherwise, placeholder ("x") if n < len(self[m]): elements.append(str(self[m][n])) else: elements.append('_') f.write(_fun.join(elements, delimiter)+"\n") f.close()
def load_file(self, path="ask", first_data_line="auto", filters="*.*", text="Select a file, FACEPANTS.", default_directory=None, header_only=False): """ This will load a file, storing the header info in self.headers, and the data in self.columns If first_data_line="auto", then the first data line is assumed to be the first line where all the elements are numbers. If you specify a first_data_line (index, starting at 0), the columns need not be numbers. Everything above will be considered header information and below will be data columns. In both cases, the line used to label the columns will always be the last header line with the same (or more) number of elements as the first data line. """ if default_directory==None: default_directory = self.directory # this loads the file, getting the header and the column values, if self.debug: print "resetting all the file-specific stuff, path =", path self.clear_columns() self.clear_headers() self.xdata = None self.ydata = None self.eydata = None if path=="ask": path = _dialogs.SingleFile(filters=self.file_extension, default_directory=self.directory, text=text) self.path = path if path==None: print "Aborted." return False # open said file for reading, read in all the lines and close t0 = time.time() if self.debug: print time.time()-t0, "seconds: starting read_lines()" self.lines = _fun.read_lines(path) if self.debug: print time.time()-t0, "seconds: done." # break up the path into parts and take the last bit (and take a stab at the legend string) self.legend_string = path.split(_os.path.sep)[-1] if self.legend_string[0] == '_': self.legend_string = '|' + self.legend_string # read in the header information if self.debug: print time.time()-t0, "seconds: start reading headers" ckeys_line = -2 for n in range(len(self.lines)): # split the line by the delimiter s = self.lines[n].strip().split(self.delimiter) # remove a trailing whitespace entry. if len(s) and s[-1].strip() == '': s.pop(-1) # first check and see if this is a data line (all elements are numbers) if first_data_line=="auto" and _fun.elements_are_numbers(s): # we've reached the first data line first_data_line = n if self.debug: print "first data line =", n # quit the header loop break; # first thing to try is simply evaluating the remaining string try: remainder = list(s) hkey = remainder.pop(0) remainder = _fun.join(remainder).strip() self.insert_header(hkey, eval(remainder)) # if that didn't work, try all the other complicated/flexible stuff except: # if this isn't an empty line and has strings for elements, assume it's a column key line for now # (we keep overwriting this until we get to the first data line) if len(s) > 0: # overwrite the ckeys, and note the line number self.ckeys = list(s) # this makes a new instance of the list so it doesn't lose the first element! ckeys_line = n # if it's length 1, it's just some word. Store a dummy string in there. if len(s) == 1: s.append('') # Also assume it is a header line. Here should be at least two elements in a header element if len(s) == 2: # If there are exactly two elemenents, just store the header constant try: self.headers[s[0]] = float(s[1]) # this one is a number except: try: self.headers[s[0]] = complex(s[1].replace('(','').replace(')','')) # it's a complex number except: self.headers[s[0]] = s[1] # this one is a string # store the key in a variable like the other cases l = s[0] else: # if there are more than 2 elements, then this is an array or a phrase # if all the elements after the first are numbers, this is an array row if _fun.elements_are_numbers(s, 1): # just add this to the headers as an array for n in range(1,len(s)): s[n] = float(s[n]) # pop off the first element, this is the string used to access the array l = s.pop(0) self.headers[l] = s # in either case, we now have a header key in the variable l. # now add it to the ordered list, but only if it doesn't exist if _fun.index(l, self.hkeys) < 0: self.hkeys.append(l) else: print "Duplicate header:", l if self.debug: print "header '"+l+"' = "+str(self.headers[l])[0:20]+" ..." # Make sure first_data_line isn't None (which happens if there's no data) if first_data_line == "auto": print "Could not find a line of pure data!" return # at this point we've found the first_data_line, and ckeys_line is correct or -2 # count the number of data columns column_count = len(self.lines[first_data_line].strip().split(self.delimiter)) # check to see if ckeys line is first_data_line-1, and that it is equal in length to the # number of data columns. If it isn't, it's a false ckeys line if ckeys_line == first_data_line-1 and len(self.ckeys) >= column_count: # it is valid. # if we have too many column keys, mention it if len(self.ckeys) > column_count: print "Note: more ckeys than columns (stripping extras)" # remove this line from the header try: self.pop_header(self.ckeys[0]) except: print "Couldn't pop column labels from header. Weird." else: # it is an invalid ckeys line. Generate our own! self.ckeys = [] for m in range(0, column_count): self.ckeys.append("column_"+str(m)) # for good measure, make sure to trim down the ckeys array to the size of the data columns for n in range(column_count, len(self.ckeys)): self.ckeys.pop(-1) # now we have a valid set of column ckeys one way or another, and we know first_data_line. if header_only: return # initialize the columns arrays # I did benchmarks and there's not much improvement by using numpy-arrays here. for label in self.ckeys: self.columns[label] = [] # start grabbing the data if self.debug: print time.time()-t0, "seconds: starting to read data" TimerStart() for n in range(first_data_line, len(self.lines)): # split the line up s = self.lines[n].strip().split(self.delimiter) # now start filling the column, ignoring the empty or bad data lines for m in range(len(s)): try: self.columns[self.ckeys[m]].append(float(s[m])) except: try: self.columns[self.ckeys[m]].append(complex(s[m][1:len(s[m])-1])) except: pass if self.debug: print time.time()-t0, "seconds: yeah." # now loop over the columns and make them all hard-core numpy columns! TimerStart() for k in self.ckeys: self.columns[k] = array(self.columns[k]) if self.debug: print time.time()-t0, "seconds: totally." # now, as an added bonus, rename some of the obnoxious headers for k in self.obnoxious_ckeys: if self.columns.has_key(k): if self.debug: print "renaming column",k,self.obnoxious_ckeys[k] self.columns[self.obnoxious_ckeys[k]] = self.columns[k]
def load_file(self, path="ask", first_data_line="auto", filters="*.*", text="Select a file, FACEPANTS.", default_directory=None, header_only=False): """ This will load a file, storing the header info in self.headers, and the data in self.columns If first_data_line="auto", then the first data line is assumed to be the first line where all the elements are numbers. If you specify a first_data_line (index, starting at 0), the columns need not be numbers. Everything above will be considered header information and below will be data columns. In both cases, the line used to label the columns will always be the last header line with the same (or more) number of elements as the first data line. """ if default_directory == None: default_directory = self.directory # this loads the file, getting the header and the column values, if self.debug: print "resetting all the file-specific stuff, path =", path self.clear_columns() self.clear_headers() self.xdata = None self.ydata = None self.eydata = None if path == "ask": path = _dialogs.SingleFile(filters=self.file_extension, default_directory=self.directory, text=text) self.path = path if path == None: print "Aborted." return False # open said file for reading, read in all the lines and close t0 = time.time() if self.debug: print time.time() - t0, "seconds: starting read_lines()" self.lines = _fun.read_lines(path) if self.debug: print time.time() - t0, "seconds: done." # break up the path into parts and take the last bit (and take a stab at the legend string) self.legend_string = path.split(_os.path.sep)[-1] if self.legend_string[0] == '_': self.legend_string = '|' + self.legend_string # read in the header information if self.debug: print time.time() - t0, "seconds: start reading headers" ckeys_line = -2 for n in range(len(self.lines)): # split the line by the delimiter s = self.lines[n].strip().split(self.delimiter) # remove a trailing whitespace entry. if len(s) and s[-1].strip() == '': s.pop(-1) # first check and see if this is a data line (all elements are numbers) if first_data_line == "auto" and _fun.elements_are_numbers(s): # we've reached the first data line first_data_line = n if self.debug: print "first data line =", n # quit the header loop break # first thing to try is simply evaluating the remaining string try: remainder = list(s) hkey = remainder.pop(0) remainder = _fun.join(remainder).strip() self.insert_header(hkey, eval(remainder)) # if that didn't work, try all the other complicated/flexible stuff except: # if this isn't an empty line and has strings for elements, assume it's a column key line for now # (we keep overwriting this until we get to the first data line) if len(s) > 0: # overwrite the ckeys, and note the line number self.ckeys = list( s ) # this makes a new instance of the list so it doesn't lose the first element! ckeys_line = n # if it's length 1, it's just some word. Store a dummy string in there. if len(s) == 1: s.append('') # Also assume it is a header line. Here should be at least two elements in a header element if len(s) == 2: # If there are exactly two elemenents, just store the header constant try: self.headers[s[0]] = float( s[1]) # this one is a number except: try: self.headers[s[0]] = complex( s[1].replace('(', '').replace( ')', '')) # it's a complex number except: self.headers[s[0]] = s[ 1] # this one is a string # store the key in a variable like the other cases l = s[0] else: # if there are more than 2 elements, then this is an array or a phrase # if all the elements after the first are numbers, this is an array row if _fun.elements_are_numbers(s, 1): # just add this to the headers as an array for n in range(1, len(s)): s[n] = float(s[n]) # pop off the first element, this is the string used to access the array l = s.pop(0) self.headers[l] = s # in either case, we now have a header key in the variable l. # now add it to the ordered list, but only if it doesn't exist if _fun.index(l, self.hkeys) < 0: self.hkeys.append(l) else: print "Duplicate header:", l if self.debug: print "header '" + l + "' = " + str( self.headers[l])[0:20] + " ..." # Make sure first_data_line isn't None (which happens if there's no data) if first_data_line == "auto": print "Could not find a line of pure data!" return # at this point we've found the first_data_line, and ckeys_line is correct or -2 # count the number of data columns column_count = len(self.lines[first_data_line].strip().split( self.delimiter)) # check to see if ckeys line is first_data_line-1, and that it is equal in length to the # number of data columns. If it isn't, it's a false ckeys line if ckeys_line == first_data_line - 1 and len( self.ckeys) >= column_count: # it is valid. # if we have too many column keys, mention it if len(self.ckeys) > column_count: print "Note: more ckeys than columns (stripping extras)" # remove this line from the header try: self.pop_header(self.ckeys[0]) except: print "Couldn't pop column labels from header. Weird." else: # it is an invalid ckeys line. Generate our own! self.ckeys = [] for m in range(0, column_count): self.ckeys.append("column_" + str(m)) # for good measure, make sure to trim down the ckeys array to the size of the data columns for n in range(column_count, len(self.ckeys)): self.ckeys.pop(-1) # now we have a valid set of column ckeys one way or another, and we know first_data_line. if header_only: return # initialize the columns arrays # I did benchmarks and there's not much improvement by using numpy-arrays here. for label in self.ckeys: self.columns[label] = [] # start grabbing the data if self.debug: print time.time() - t0, "seconds: starting to read data" TimerStart() for n in range(first_data_line, len(self.lines)): # split the line up s = self.lines[n].strip().split(self.delimiter) # now start filling the column, ignoring the empty or bad data lines for m in range(len(s)): try: self.columns[self.ckeys[m]].append(float(s[m])) except: try: self.columns[self.ckeys[m]].append( complex(s[m][1:len(s[m]) - 1])) except: pass if self.debug: print time.time() - t0, "seconds: yeah." # now loop over the columns and make them all hard-core numpy columns! TimerStart() for k in self.ckeys: self.columns[k] = array(self.columns[k]) if self.debug: print time.time() - t0, "seconds: totally." # now, as an added bonus, rename some of the obnoxious headers for k in self.obnoxious_ckeys: if self.columns.has_key(k): if self.debug: print "renaming column", k, self.obnoxious_ckeys[k] self.columns[self.obnoxious_ckeys[k]] = self.columns[k]
def __init__(self, f='a+b*x+c*x**2', p='a=1.5, b, c=1.5', bg=None, a=None, globs={}): """ This class takes the function string you specify and generates a model based on it. f can be either a string or a function f(x,a,b,..) that you have defined. p is a comma-delimited string bg is a background function. a is a comma-delimited string of additional args to send to the function. globs is a list of globals should you wish to have these visible to f. If the function is a string it will be evaluated knowing about all the globals specified by the globs argument. If it is a function, it can have as many arguments as you like, so long as the x data is the first argument, and each of the subsequent argument slots has a corresponding element in the list p. If you want to do something a little more fancy with a guessing algorithm, it's relatively straightforward to write one of the model classes similar to the examples given in spinmob.models If you want, you can specify a list of functions, a string of parameters, a matching list of background functions, and a matching list of additional arguments to fit more than one dataset simultaneously. """ # make sure we have lists if not _s.fun.is_iterable(f) : f = [f] if not _s.fun.is_iterable(bg): bg = [bg] # make sure the background has as many elements as the function list if not len(f)==len(bg): x = bg[0] bg = list(f) for n in range(len(bg)): bg[n]=x # start by parsing the p string. This is the same for both f's p_split = p.split(',') # Loop over the parameters, get their names and possible default values self.pnames = [] self.defaults = [] for parameter in p_split: parameter_split = parameter.split('=') self.pnames.append(parameter_split[0].strip()) if len(parameter_split)==2: self.defaults.append(float(parameter_split[1])) else: self.defaults.append(1.0) # set up the guess self.p0 = _n.array(self.defaults) # store the globals self.globs = dict(globs) self.f = [] self.bg = [] self.function_string = [] self.background_string = [] self.additional_args = [] # loop over the supplied list of functions for n in range(len(f)): # now do different things depending on the type of function if type(f[n])==str: # get the function strings self.function_string.append(f[n]) if bg[n]==None: self.background_string.append(f[n]) else: self.background_string.append(bg[n]) # override the function and background args = 'x,'+_fun.join(self.pnames,',') if a==None or a[n]==None: self.additional_args.append(None) else: args = args + "," + str(a[n]) self.additional_args.append(eval('['+str(a[n])+']', self.globs)) self.f.append( eval('lambda ' + args + ': ' + self.function_string[n], self.globs)) self.bg.append(eval('lambda ' + args + ': ' + self.background_string[n], self.globs)) else: if bg[n]==None: bg[n] = f[n] self.function_string.append( f[n].__name__ +"(x, "+p+")") self.background_string.append(bg[n].__name__ +"(x, "+p+")") # override the function and background self.f.append(f[n]) self.bg.append(bg[n])
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()