Ejemplo n.º 1
0
    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()
Ejemplo n.º 2
0
    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()
Ejemplo n.º 3
0
    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]
Ejemplo n.º 4
0
    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]
Ejemplo n.º 5
0
    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])
Ejemplo n.º 6
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()