Hi everybody, I'm the author of SciGraphica (http://scigraphica.sourceforge.net), an application for scientific graphics and data analysis that uses Python and Numpy. Among other things, it features spreadheets that can contain Python data, like formulas. The Python stuff was mainly done by Conrad Steenberg, because I don't have much experience, specially embedding Python into C. However, I decided to get started this weekend because we needed badly a GUI for non-linear least squares fits. The result is gtkLeastSquares.py, which is attached below. It's going to be distributed with SG, but it works independently and needs gtk.py and Scientific. Basically it is a gtk GUI for Scientific.Functions.LeastSquares. I'm quite satisfied with the result, considering that I'm a newbie ;-) I'm writting because I'd like to have some feedback, and people interested in contributing. The code is still immature, and needs enhancements, like user defined functions and parameters (either editing the existing in the GUI, or adding new ones interactively). I still have to learn. It is very simple, and easy to use. You'll find the code below, with an example at the bottom of the file. I hope you like it. All comments, and suggestions are very welcome (as well as contributors ;-) Thank you, <ADRIAN> -------------- Cut here ---------------------- # # gtkLeastSquares.py : GUI for the module Scientific.Functions.LeastSquares # Implementation of the Levenberg-Marquardt algorithm for general # non-linear least-squares fits. # # Copyright (C) 2001 Adrian E. Feiguin <feiguin@ifir.edu.ar> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # from string import * from gtk import * from Numeric import * from Scientific.Functions.LeastSquares import * from Scientific.Functions.FirstDerivatives import * def fit_linear(parameters, values): a, b = parameters x = values return (a + b * x) def fit_cuadratic(parameters, values): a, b, c, x0 = parameters x = values return(a + b * (x - x0) + c * (x - x0)**2) return def fit_gauss(parameters, values): y0, x0, a, w = parameters x = values return(y0 + a * exp(-2*(x-x0)**2/w**2)) return def fit_lorentz(parameters, values): x0, y0, a, b = parameters x = values return(y0 + 2*a/pi * w / (4 * (x - x0)**2 + w**2)) return def fit_boltzman(parameters, values): x0, a1, a2, dx = parameters x = values return((a1 - a2)/(1 + exp((x - x0)/dx)) + a2) def fit_logistic(parameters, values): x0, a1, a2, p = parameters x = values return((a1 - a2)/(1 + (x/x0)**p) + a2) def fit_expdecay(parameters, values): x0, y0, a, t = parameters x = values return(y0 + a * exp(-(x - x0)/t)) def fit_expgrow(parameters, values): x0, y0, a, t = parameters x = values return(y0 + a * exp((x - x0)/t)) def fit_expassoc(parameters, values): y0, a1, t1, a2, t2 = parameters x = values return(y0 + a1 * (1 + exp(-x/t1)) + a2 * (1 + exp(-x/t2))) def fit_hyperbl(parameters, values): p1, p2 = parameters x = values return(p1 * x/ (p2 + x)) def fit_pulse(parameters, values): x0, y0, a, t1, t2 = parameters x = values return(y0 + a * (1 + exp(-(x - x0)/t1)) * exp(-(x - x0)/t2)) def fit_rational0(parameters, values): a, b, c = parameters x = values return((b + c*x)/(1 + a*x)) def fit_sine(parameters, values): x0, a, w = parameters x = values return(a * sin(pi*(x - x0)/w)) def fit_gaussamp(parameters, values): x0, y0, a, w = parameters x = values return(y0 + a * exp(-(x - x0)**2/(2*w**2))) def fit_allometric(parameters, values): a, b = parameters x = values return(a * x**b) fit_linear_dic = { "Doc" : "Linear Function", "Exp" : "y = a + b * x", "Par" : ("a", "b"), "NumPar" : 2, "IVar" : "x", "DVar" : "y", "Function" : fit_linear } fit_cuadratic_dic = { "Doc" : "Cuadratic Function", "Exp" : "y = a + b * (x - x0) + c * (x - x0)**2", "Par" : ("a", "b", "c", "x0"), "NumPar" : 4, "IVar" : "x", "DVar" : "y", "Function" : fit_cuadratic } fit_gauss_dic = { "Doc" : "Amplitude version of Gaussian Function", "Exp" : "y = y0 + a * exp(-2*(x-x0)**2/w**2)", "Par" : ("x0", "y0", "a", "w"), "NumPar" : 4, "IVar" : "x", "DVar" : "y", "Function" : fit_gauss } fit_lorentz_dic = { "Doc" : "Lorentzian Peak Function", "Exp" : "y = y0 + 2*a/pi * w / (4 * (x - x0)**2 + w**2)", "Par" : ("x0", "y0", "a", "w"), "NumPar" : 4, "IVar" : "x", "DVar" : "y", "Function" : fit_lorentz } fit_boltzman_dic = { "Doc" : "Boltzman Function: sigmoidal curve", "Exp" : "y = (a1 - a2)/(1 + exp((x - x0)/dx)) + a2", "Par" : ("x0", "a1", "a2", "dx"), "NumPar" : 4, "IVar" : "x", "DVar" : "y", "Function" : fit_boltzman } fit_logistic_dic = { "Doc" : "Logistic dose/response", "Exp" : "y = (a1 - a2)/(1 + (x/x0)**p) + a2", "Par" : ("x0", "a1", "a2", "p"), "NumPar" : 4, "IVar" : "x", "DVar" : "y", "Function" : fit_logistic } fit_expdecay_dic = { "Doc" : "Exponential Decay", "Exp" : "y = y0 + a * exp(-(x - x0)/t)", "Par" : ("x0", "y0", "a", "t"), "NumPar" : 4, "IVar" : "x", "DVar" : "y", "Function" : fit_expdecay } fit_expgrow_dic = { "Doc" : "Exponential Growth", "Exp" : "y = y0 + a * exp((x - x0)/t)", "Par" : ("x0", "y0", "a", "t"), "NumPar" : 4, "IVar" : "x", "DVar" : "y", "Function" : fit_expgrow } fit_expassoc_dic = { "Doc" : "Exponential Associate", "Exp" : "y = y0 + a1 * (1 + exp(-x/t1)) + a2 * (1 + exp(-x/t2))", "Par" : ("y0", "a1", "t1", "a2", "t2"), "NumPar" : 5, "IVar" : "x", "DVar" : "y", "Function" : fit_expassoc } fit_hyperbl_dic = { "Doc" : "Hyperbola Function", "Exp" : "y = p1 * x/ (p2 + x)", "Par" : ("p1", "p2"), "NumPar" : 2, "IVar" : "x", "DVar" : "y", "Function" : fit_hyperbl } fit_pulse_dic = { "Doc" : "Pulse Function", "Exp" : "y = y0 + a * (1 + exp(-(x - x0)/t1)) * exp(-(x - x0)/t2)", "Par" : ("y0", "a", "t1", "t2"), "NumPar" : 4, "IVar" : "x", "DVar" : "y", "Function" : fit_pulse } fit_rational0_dic = { "Doc" : "Rational Function , type 0", "Exp" : "y = (b + c*x)/(1 + a*x)", "Par" : ("a", "b", "c"), "NumPar" : 3, "IVar" : "x", "DVar" : "y", "Function" : fit_rational0 } fit_sine_dic = { "Doc" : "Sine Function", "Exp" : "y = a * sin(pi*(x - x0)/w)", "Par" : ("a", "x0", "w"), "NumPar" : 3, "IVar" : "x", "DVar" : "y", "Function" : fit_sine } fit_gaussamp_dic = { "Doc" : "Amplitude version of Gaussian Peak Function", "Exp" : "y = y0 + a * exp(-(x - x0)**2/(2*w**2))", "Par" : ("x0", "y0", "a", "w"), "NumPar" : 4, "IVar" : "x", "DVar" : "y", "Function" : fit_gaussamp } fit_allometric_dic = { "Doc" : "Classical Freundlich Model", "Exp" : "y = a * x**b", "Par" : ("a", "b"), "NumPar" : 4, "IVar" : "x", "DVar" : "y", "Function" : fit_allometric } functions = { "Linear" : fit_linear_dic, "Cuadratic" : fit_cuadratic_dic, "Gauss" : fit_gauss_dic, "Lorentz" : fit_lorentz_dic, "Boltzman" : fit_boltzman_dic, "Logistic" : fit_logistic_dic, "ExpDecay" : fit_expdecay_dic, "ExpGrow" : fit_expgrow_dic, "ExpAssoc" : fit_expassoc_dic, "Hyperbl" : fit_hyperbl_dic, "Pulse" : fit_pulse_dic, "Rational0" : fit_rational0_dic, "Sine" : fit_sine_dic, "GaussAmp" : fit_gaussamp_dic, "Allometric" : fit_allometric_dic } def fit(data): main_window = GtkWindow() main_window.set_title("Curve Fitting") main_window.set_border_width(5) main_window.connect("destroy", mainquit) main_window.connect("delete_event", mainquit) main_box = GtkVBox(FALSE, 5) main_window.add(main_box) main_frame = GtkFrame("Select Function") main_box.pack_start(main_frame) table = GtkTable(7, 4, FALSE) table.set_col_spacings(10) table.set_row_spacings(5) table.set_border_width(5) main_frame.add(table) swindow = GtkScrolledWindow() swindow.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC) swindow.set_usize(120, 100) table.attach(swindow, 0, 1, 0, 6) clist = GtkCList(1) swindow.add(clist) table.attach(GtkVSeparator(), 1, 2, 0, 6) text = map(lambda i: str(i), range(20)) k = functions.keys() k.sort() for i in k: text[0] = i clist.append(text) label = GtkLabel("Exp:") label.set_alignment(1., .5) table.attach(label, 2, 3, 0, 1) fentry = GtkEntry() # fentry.set_editable(FALSE) table.attach(fentry, 3, 4, 0, 1) label = GtkLabel("Number of Param:") label.set_alignment(1., .5) table.attach(label, 2, 3, 1, 2) nspin = GtkSpinButton(GtkAdjustment(0, 0, 8, 1, 8, 0), 0, 0) nspin.set_editable(FALSE) nspin.set_state(STATE_INSENSITIVE) table.attach(nspin, 3, 4, 1, 2) label = GtkLabel("Param:") label.set_alignment(1., .5) table.attach(label, 2, 3, 2, 3) pentry = GtkEntry() pentry.set_editable(FALSE) pentry.set_state(STATE_INSENSITIVE) table.attach(pentry, 3, 4, 2, 3) label = GtkLabel("Independent Var:") label.set_alignment(1., .5) table.attach(label, 2, 3, 3, 4) iventry = GtkEntry() iventry.set_editable(FALSE) iventry.set_state(STATE_INSENSITIVE) table.attach(iventry, 3, 4, 3, 4) label = GtkLabel("Dependent Var:") label.set_alignment(1., .5) table.attach(label, 2, 3, 4, 5) dventry = GtkEntry() dventry.set_editable(FALSE) dventry.set_state(STATE_INSENSITIVE) table.attach(dventry, 3, 4, 4, 5) action_area = GtkHButtonBox() action_area.set_layout(BUTTONBOX_END) action_area.set_spacing(5) main_box.pack_start(action_area) fit_button = GtkButton("Fit") action_area.pack_start(fit_button) close_button = GtkButton("Close") action_area.pack_start(close_button) lframe = GtkFrame() lframe.set_shadow_type(SHADOW_IN) main_box.pack_start(lframe) explabel = GtkLabel("Choose a Fitting Function") lframe.add(explabel) # CALLBACK FUNCTIONS def select_function(_clist, row, col, event, functions = functions, label = explabel, fentry = fentry, pentry = pentry, nspin = nspin, iventry = iventry, dventry = dventry): k = _clist.get_text(row, col) f = functions[k] label.set_text(f["Doc"]) fentry.set_text(f["Exp"]) nspin.set_value(f["NumPar"]) iventry.set_text(f["IVar"]) dventry.set_text(f["DVar"]) s = "" for i in f["Par"]: s = s + i + ", " pentry.set_text(s[:len(s)-2]) def open_fit_dialog(_button, functions = functions, clist = clist, data = data): a = clist.__getattr__("selection") k = clist.get_text(a[0], 0) f = functions[k] param = (1, 1) fit_dialog(f, data) # CONNECT OBJECTS clist.connect("select_row", select_function) fit_button.connect("clicked", open_fit_dialog) close_button.connect("clicked", main_window.destroy) clist.select_row(0, 0) main_window.show_all() mainloop() def fit_dialog(f, data): main_window = GtkWindow() main_window.set_title("Fit") main_window.set_border_width(5) main_window.connect("destroy", mainquit) main_window.connect("delete_event", mainquit) table = GtkTable(len(f["Par"])+3, 2, FALSE) table.set_col_spacings(10) table.set_row_spacings(5) main_window.add(table) table.attach(GtkLabel("Variable"), 0, 1, 0, 1) table.attach(GtkLabel("Value"), 1, 2, 0, 1) table.attach(GtkHSeparator(), 0, 2, 1, 2) r = 2 entries = [] for i in f["Par"]: # check = GtkCheckButton(i+":") # table.attach(check, 0, 1, r, r+1) table.attach(GtkLabel(i+":"), 0, 1, r, r+1) entry = GtkEntry() entries = entries + [entry] entry.set_text("0.0") table.attach(entry, 1, 2, r, r+1) r = r + 1 table.attach(GtkHSeparator(), 0, 2, r, r + 1) r = r + 1 table.attach(GtkLabel("Chi_Sqr:"), 0, 1, r, r + 1) err_entry = GtkEntry() table.attach(err_entry, 1, 2, r, r + 1) r = r + 1 table.attach(GtkHSeparator(), 0, 2, r, r + 1) def run_fit(_button, f = f, data = data, entries = entries, err_entry = err_entry): n = 0 p = () for i in entries: s = entries[n].get_text() p = p + (atof(s),) n = n + 1 fit, error = leastSquaresFit(f["Function"], p, data) n = 0 for i in entries: entries[n].set_text(str(fit[n])) n = n + 1 err_entry.set_text(str(error)) # print "Fitted parameters: ", fit # print "Fit error: ", error return action_area = GtkHButtonBox() action_area.set_layout(BUTTONBOX_SPREAD) action_area.set_spacing(5) run_button = GtkButton("Run") close_button = GtkButton("Close") action_area.pack_start(run_button) action_area.pack_start(close_button) table.attach(action_area, 0, 2, r + 1, r + 2) # CONNECT OBJECTS run_button.connect("clicked", run_fit) close_button.connect("clicked", main_window.destroy) main_window.show_all() mainloop() # Test for linear fit: # # from gtkLeastSquares import * # data = [ (0., 0.), (1., 1.1), (2., 1.98), (3., 3.05) ] # fit(data)
participants (1)
-
Adrian Feiguin