pandas_GUI.fit_Pandas_GUI
1import sys 2 3import numpy as np 4 5def calcresid(result): 6 ''' 7 lmfit has empty values for residuals where the weighting is infinite or not defined. 8 This calculates all the residuals based on the actual data and fit results. 9 10 :param ModelResult result: An lmfit ModelResult. 11 12 :return np.array residuals: The residuals. 13 ''' 14 import numpy as np 15 import lmfit as lmfit 16 resid = [] 17 for i in range(0, len(results.data)): 18 resid.append(results.data[i] - results.best_fit[i]) 19 return np.array(resid) 20 21def fit_pandas_GUI(df_info=None, show_text_col = False, **kwargs): 22 """ 23 If passed no parameters this will look for all the dataframes in the user 24 namespace and make them available for plotting. Once a 25 dataframe is chosen only the numerical columns from that dataframe will 26 be available for inclusion in the plotting expression. 27 28 This GUI produces code to use the lmfit package to fit data and the plotly 29 interactive plotting package to display the results. 30 31 If you wish to allow only certain dataframes or have them show up as 32 user friendly names in the menus provide that information in the first 33 paramater df_info. 34 35 To allow inclusion of text columns pass True for show_text_col. 36 37 :param bool show_text_col: (default = False). When True columns 38 containing text will be shown. 39 40 :param list df_info: List of Lists [[object,globalname, 41 userfriendly]],..] 42 * object -- pandas.DataFrame 43 * globalname -- string name of the object in the user global name space. 44 * userfriendly -- string name to display for user selection. 45 46 :keyword string figname: string used to override default python name for 47 figure. 48 49 :keyword string fitname: string used to override default python name for 50 fit. 51 52 :keyword bool findframes: default = True. If set to false and dataframes 53 are passed in dfs_info, will not search for dataframes in the user 54 namespace. 55 """ 56 from ipywidgets import Layout, Box, HBox, VBox, GridBox, Tab, \ 57 Accordion, Dropdown, Label, Text, Button, Checkbox, FloatText, \ 58 RadioButtons, BoundedIntText, Output 59 from ipywidgets import HTML as richLabel 60 from ipywidgets import HTMLMath as texLabel 61 from IPython.display import display, HTML 62 from IPython.display import Javascript as JS 63 from IPython import get_ipython 64 from JPSLUtils.utils import new_cell_immediately_below,\ 65 select_cell_immediately_below, move_cursor_in_current_cell, \ 66 insert_text_into_next_cell, insert_text_at_beginning_of_current_cell, \ 67 insert_newline_at_end_of_current_cell, select_containing_cell, \ 68 delete_selected_cell, iconselector, notice_group, \ 69 replace_text_of_current_cell, pseudoLatexToLatex 70 from lmfit import models 71 72 from .utils import find_pandas_dataframe_names, build_run_snip_widget 73 from IPython import get_ipython 74 global_dict = get_ipython().user_ns 75 JPSLUtils = global_dict["JPSLUtils"] 76 if JPSLUtils.notebookenv != 'colab': 77 import plotly.graph_objects as go 78 dfs_info = [] 79 if isinstance(df_info,list): 80 for k in df_info: 81 dfs_info.append(k) 82 findframes = kwargs.pop('findframes',True) 83 if findframes: 84 for k in find_pandas_dataframe_names(): 85 dfs_info.append([global_dict[k],k,k]) 86 friendly_to_globalname = {k[2]:k[1] for k in dfs_info} 87 friendly_to_object = {k[2]:k[0] for k in dfs_info} 88 89 fitname = kwargs.pop('fitname',None) 90 from .utils import find_fit_names 91 fitlst = find_fit_names() 92 if fitname in fitlst: 93 raise UserWarning (str(fitname) + ' already exists. Choose a ' 94 'different name for the fit.') 95 if fitname == None: 96 fitname = 'Fit_'+str(len(fitlst)+1) 97 98 figname = kwargs.pop('figname',None) 99 from .utils import find_figure_names 100 figlst = find_figure_names() 101 if figname in figlst: 102 raise UserWarning (str(figname) + ' already exists. Choose a ' 103 'different name for the figure.') 104 if figname == None: 105 figname = str(fitname) + '_Figure' 106 107 fitmodels = ['LinearModel','PolynomialModel','ExponentialModel', 108 'GaussianModel','SineModel'] 109 fitmodeleqns = { 110 'LinearModel':r'$fit = \color{red}{a}x+\color{red}{b}$, where $\color{' 111 r'red}{a}$ = slope, $\color{red}{b}$ = intercept', 112 'PolynomialModel': r'$fit = \sum_{n=0}^{\le7}{\color{red}{c_n}x^n} = ' 113 r'\color{red}{c_0} + \color{red}{c_1}x + \color{red}{' 114 r'c_2}x^2 + ...$', 115 'ExponentialModel': r'$fit = \color{red}{A} \exp \left( \frac{-x} ' \ 116 r'{\color{red}{\tau}}\right)$, where $\color{red}{A}$ ' 117 r'= amplitude, $ \color{red}{\tau}$ = decay', 118 'GaussianModel': r'$fit = \frac{\color{red}{A}}{\color{red}{\sigma} ' \ 119 r'\sqrt{2 \pi}} \exp \left( \frac{-(x-\color{red}' \ 120 r'{\mu})^2}{2 \color{red}{\sigma}^2} \right)$, where ' \ 121 r'$\color{red}{A}$ = amplitude, $\color{red}{\sigma}$ = sigma, ' 122 r'$\color{red}{\mu}$ = center', 123 'SineModel': r'$fit = \color{red}{A} \sin \left ( \color{red}{f}x + ' 124 r'\color{red}{\phi} \right)$, ' 125 r'where $\color{red}{A}$ = ' \ 126 r'amplitude, $\color{red}{f}$ = frequency, '\ 127 r'$\color{red}{\phi}$ = shift' 128 } 129 130 def polymodelresultstr(resultname): 131 template = '' \ 132 'fitstr = r\'$fit = \'\n' \ 133 'termcount = int(0)\n' \ 134 'for k in %result.params.keys():\n' \ 135 ' pwr = int(str(k)[int(-1):])\n' \ 136 ' if %result.params[k].vary:\n' \ 137 ' if termcount > int(0):\n' \ 138 ' fitstr += \' + \'\n' \ 139 ' fitstr += r\'({%COLOR{red}{\'+rue.latex_rndwitherr(' \ 140 '%result.params[k].value,\n' \ 141 ' %result.params[k].stderr, \n' \ 142 ' errdig=int(1), \n' \ 143 ' lowmag=-int(3))+\'}})\'\n' \ 144 ' if pwr == int(1):\n' \ 145 ' fitstr += \'x\'\n' \ 146 ' if pwr > int(1):\n' \ 147 ' fitstr += \'x^\'+str(pwr)\n' \ 148 ' termcount+=int(1)\n' \ 149 ' else:\n' \ 150 ' if %result.params[k].value!=0.0:\n' \ 151 ' if termcount > int(0):\n' \ 152 ' fitstr += \'+\'\n' \ 153 ' fitstr += r\'({%COLOR{blue}{\'+str(' \ 154 '%result.params[k].value)+\'}})\'\n' \ 155 ' termcount +=int(1)\n' \ 156 ' if pwr == int(1):\n' \ 157 ' fitstr += \'x\'\n' \ 158 ' if pwr > int(1):\n' \ 159 ' fitstr += \'x^\'+str(pwr)\n' \ 160 'fitstr+=\'$\'\n' \ 161 'captionstr=r\'<p>Use the command <code>%result</code> as the ' \ 162 'last line of a code cell for more details.</p>\'\n' \ 163 'display(Math(fitstr))\n' \ 164 'display(HTML(captionstr))' 165 return template.replace('%result', str(resultname)) 166 167 def linmodelresultstr(resultname): 168 template = '' \ 169 'slopestr = ''\'\'\n' \ 170 'interceptstr = ''\'\'\n' \ 171 'for k in %results.params.keys():\n' \ 172 ' if %results.params[k].vary:\n' \ 173 ' paramstr = r\'({%COLOR{red}{\'+rue.latex_rndwitherr(' \ 174 '%results.params[k].value,\n' \ 175 ' %results.params[k].stderr,\n' \ 176 ' errdig=int(1),\n' \ 177 ' lowmag=-int(3))+\'}})\'\n' \ 178 ' else:\n' \ 179 ' paramstr = r\'{%COLOR{blue}{\'+str(%results.params[' \ 180 'k].value,' \ 181 '\n' \ 182 ' )+\'}}\'\n' \ 183 ' if k == \'slope\':\n' \ 184 ' slopestr = paramstr\n' \ 185 ' if k == \'intercept\' and %results.params[k].value != 0:\n' \ 186 ' interceptstr = \' + \' + paramstr\n' \ 187 'fitstr = r\'$fit = \'+slopestr + \'x\' + interceptstr + \'$\'\n' \ 188 'captionstr = r\'<p>Use the command <code>%results</code> as the ' \ 189 'last line of a code cell for more details.</p>\'\n' \ 190 'display(Math(fitstr))\n' \ 191 'display(HTML(captionstr))' 192 return template.replace('%results', resultname) 193 194 def expmodelresultstr(resultname): 195 template = '' \ 196 'ampstr = ''\'\'\n' \ 197 'decaystr = ''\'\'\n' \ 198 'for k in %results.params.keys():\n' \ 199 ' if %results.params[k].vary:\n' \ 200 ' paramstr = r\'({%COLOR{red}{\'+rue.latex_rndwitherr(' \ 201 '%results.params[k].value,\n' \ 202 ' %results.params[k].stderr,\n' \ 203 ' errdig=int(1),\n' \ 204 ' lowmag=-int(3))+\'}})\'\n' \ 205 ' else:\n' \ 206 ' paramstr = r\'{%COLOR{blue}{\'+str(%results.params[' \ 207 'k].value, \n' \ 208 ' )+\'}}\'\n' \ 209 ' if k == \'amplitude\':\n' \ 210 ' ampstr = paramstr\n' \ 211 ' if k == \'decay\':\n' \ 212 ' decaystr = paramstr\n' \ 213 'fitstr = r\'$$fit = \'+ampstr+r\'%EXP %LEFT( %FRAC{-x}' \ 214 '{\'+decaystr+r\'}%RIGHT)$$\'\n' \ 215 'captionstr = r\'<p>Use the command <code>%results</code> as the ' \ 216 'last line of a code cell for more details.</p>\'\n' \ 217 'display(Math(fitstr))\n' \ 218 'display(HTML(captionstr))' 219 return template.replace('%results',resultname) 220 221 def gausmodelresultstr(resultname): 222 template = '' \ 223 'ampstr = ''\'\'\n' \ 224 'centstr = ''\'\'\n' \ 225 'sigmastr = ''\'\'\n' \ 226 'for k in %results.params.keys():\n' \ 227 ' if %results.params[k].vary:\n' \ 228 ' paramstr = r\'({%COLOR{red}{\'+rue.latex_rndwitherr(' \ 229 '%results.params[k].value,\n' \ 230 ' %results.params[k].stderr,\n' \ 231 ' errdig=int(1),\n' \ 232 ' lowmag=-int(3))+\'}})\'\n' \ 233 ' else:\n' \ 234 ' paramstr = r\'{%COLOR{blue}{\'+str(%results.params[' \ 235 'k].value, \n' \ 236 ' )+\'}}\'\n' \ 237 ' if k == \'amplitude\':\n' \ 238 ' ampstr = paramstr\n' \ 239 ' if k == \'center\':\n' \ 240 ' centstr = paramstr\n' \ 241 ' if k == \'sigma\':\n' \ 242 ' sigmastr = paramstr\n' \ 243 'fitstr = r\'$$fit = %FRAC{\'+ampstr+\'}{' \ 244 '\'+sigmastr+r\'%SQRT{2%PI}}%EXP %LEFT( %FRAC{' \ 245 '-%LEFT[x-\'+centstr+r\'%RIGHT]^2}' \ 246 '{2\'+sigmastr+r\'^2}%RIGHT)$$\'\n' \ 247 'captionstr = r\'<p>Use the command <code>%results</code> as the ' \ 248 'last line of a code cell for more details.</p>\'\n' \ 249 'display(Math(fitstr))\n' \ 250 'display(HTML(captionstr))' 251 return template.replace('%results',resultname) 252 253 def sinmodelresultstr(resultname): 254 template = '' \ 255 'ampstr = \'\'\n' \ 256 'freqstr = \'\'\n' \ 257 'shiftstr = \'\'\n' \ 258 'for k in %results.params.keys():\n' \ 259 ' if %results.params[k].vary:\n' \ 260 ' if %results.params[k].stderr:\n' \ 261 ' paramstr = r\'({%COLOR{red}{\'+rue.latex_rndwitherr(' \ 262 '%results.params[k].value,\n' \ 263 ' %results.params[k].stderr,\n' \ 264 ' errdig=int(1),\n' \ 265 ' lowmag=-int(3))+\'}})\'\n' \ 266 ' else:\n' \ 267 ' paramstr = r\'{%COLOR{red}{\'+' \ 268 'str(%results.params[k].value)+\'}}\'\n' \ 269 ' else:\n' \ 270 ' paramstr = r\'{%COLOR{blue}{\'+str(%results.params[k].value' \ 271 ')+\'}}\'\n' \ 272 ' if k == \'amplitude\':\n' \ 273 ' ampstr = paramstr\n' \ 274 ' if k == \'frequency\':\n' \ 275 ' freqstr = paramstr\n' \ 276 ' if k == \'shift\' and %results.params[k].value != 0.0:\n' \ 277 ' shiftstr = \' + \' + paramstr\n' \ 278 'fitstr = r\'$fit = \'+ampstr + \'sin[\' + freqstr + \'x\' + shiftstr + \']$\'\n' \ 279 'captionstr = r\'<p>Use the command <code>%results</code> as the ' \ 280 'last line of a code cell for more details.</p>\'\n' \ 281 'display(Math(fitstr))\n' \ 282 'display(HTML(captionstr))' 283 return template.replace('%results', resultname) 284 285 fitresultstrs = { 286 'LinearModel': linmodelresultstr, 287 'PolynomialModel': polymodelresultstr, 288 'ExponentialModel': expmodelresultstr, 289 'GaussianModel': gausmodelresultstr, 290 'SineModel': sinmodelresultstr 291 } 292 293 importstr = '# CODE BLOCK generated using fit_pandas_GUI().\n# See '\ 294 'https://jupyterphysscilab.github.io/jupyter_Pandas_GUI.\n' \ 295 '# Integers wrapped in `int()` to avoid having them cast \n' \ 296 '# as other types by interactive preparsers. \n' \ 297 '# Imports (no effect if already imported)\n' \ 298 'import numpy as np\n' \ 299 'import lmfit as lmfit\n' \ 300 'import round_using_error as rue\n' \ 301 'import copy as copy\n' \ 302 'from plotly import graph_objects as go\n' \ 303 'from IPython.display import HTML, Math\n\n' 304 step1str = '' 305 step2str = '' 306 step3str = '' 307 step4str = '' 308 step5str = '' 309 step6str = '' 310 range_chosen = False 311 #### Define GUI Elements #### 312 # Those followed by a * are required. 313 output = Output() 314 with output: 315 display(HTML( 316 "<h3 id ='pandasfitGUI' style='text-align:center;'>Pandas Fit " 317 "Composer</h3> <div style='text-align:center;'>" 318 "<span style='color:green;'>Steps with a * are required.</span> The " 319 "code that will generate the fit is being " 320 "built in the textbox or cell immediately below.</div><div " 321 "style='text-align:center;'>This composer uses a subset of " 322 "<a href ='https://lmfit.github.io/lmfit-py/'> the lmfit package</a>" 323 " and <a href ='https://plotly.com/python/line-and-scatter/#'> " 324 "the plotly scatter plot</a> capabilities.</div>")) 325 326 longdesc = {'description_width':'initial'} 327 328 # Notices for the Final Check Tab. 329 makeplot_notices = notice_group(['Need data to fit', 330 'Need a fit model', 331 'Axes must have labels.'], 332 'Notices:','','red') 333 makeplot_notices.set_active([0,1,2]) 334 335 # 1. Pick and variables to fit and fit model 336 # a. Select Y vs. X (DataFrame, X and Y, which must be from single 337 # frame. 338 # Notices for the Pick Trace(s) tab. 339 notice_list = [ 340 'Data set (DataFrame) required.', 341 'X- and Y-values required.', 342 ] 343 trace_notices = notice_group(notice_list, 'Notices:','','red') 344 trace_notices.set_active([0,1]) 345 step1instr = richLabel(value = '<ol><li>Select a DataFrame (Data ' 346 'set);</li>' 347 '<li>Select the column containing the X ' 348 'values;</li>' 349 '<li>Select the column containing the Y ' 350 'values (what is being fit);</li>' 351 '<li>Provide a name for the trace if you do' 352 ' not like the default. This text will be ' 353 'used for the legend;</li></ol>' 354 ) 355 step1instracc = Accordion(children = [step1instr]) 356 step1instracc.set_title(0,'Instructions') 357 step1instracc.selected_index = None 358 359 # DataFrame selection 360 tempopts = [] 361 tempopts.append('Choose data set.') 362 for k in dfs_info: 363 tempopts.append(k[2]) 364 whichframe = Dropdown(options=tempopts, 365 description='DataFrame: ',) 366 367 def update_columns(change): 368 if change['new'] == 'Choose data set.': 369 Xcoord.disabled = True 370 Ycoord.disabled = True 371 trace_notices.activate_notice(0) 372 trace_notices.activate_notice(1) 373 trace_notices.activate_notice(2) 374 add_trace_notices.value = trace_notices.notice_html() 375 return 376 df = friendly_to_object[change['new']] 377 tempcols = df.columns.values 378 tempopt = ['Choose column for coordinate.'] 379 for k in tempcols: 380 if show_text_col: 381 tempopt.append(k) 382 else: 383 if df[k].dtype != 'O': 384 tempopt.append(k) 385 Xcoord.options = tempopt 386 Xcoord.value = tempopt[0] 387 Ycoord.options = tempopt 388 Ycoord.value = tempopt[0] 389 Xcoord.disabled = False 390 Ycoord.disabled = False 391 trace_notices.activate_notice(1) 392 trace_notices.deactivate_notice(0) 393 add_trace_notices.value =trace_notices.notice_html() 394 pass 395 whichframe.observe(update_columns, names='value') 396 397 # Data selection 398 Xcoord = Dropdown(options=['Choose X-coordinate.'], 399 description='X: ', 400 disabled = True) 401 Ycoord = Dropdown(options=['Choose Y-coordinate.'], 402 description='Y: ', 403 disabled = True) 404 def trace_name_update(change): 405 if change['new'] != 'Choose column for coordinate.': 406 trace_name.value = Ycoord.value 407 if Xcoord.value != 'Choose column for coordinate.' and Ycoord.value \ 408 != 'Choose column for coordinate.': 409 yerrtype.disabled = False 410 trace_name.disabled = False 411 trace_notices.deactivate_notice(1) 412 add_trace_notices.value = trace_notices.notice_html() 413 else: 414 yerrtype.disabled = True 415 trace_name.disabled = True 416 trace_notices.activate_notice(1) 417 add_trace_notices.value = trace_notices.notice_html() 418 pass 419 Xcoord.observe(trace_name_update,names='value') 420 Ycoord.observe(trace_name_update,names='value') 421 422 # Trace name 423 trace_name = Text(placeholder = 'Trace name for legend', 424 description = 'Trace name: ', 425 disabled = True) 426 427 trace_notices.set_active([0,1]) 428 add_trace_notices = richLabel(value = trace_notices.notice_html()) 429 step1tracebox = VBox(children=[whichframe,Xcoord,Ycoord,trace_name]) 430 step1actionbox = VBox(children=[add_trace_notices]) 431 step1hbox = HBox(children=[step1tracebox,step1actionbox]) 432 step1 = VBox(children=[step1instracc, step1hbox]) 433 434 # 2. Set data uncertainty 435 step2instr = richLabel(value = 'If you know the uncertainty in your data ' 436 'values (Y-values)you should ' 437 'specify it, as the uncertainty impacts ' 438 'the final uncertainty in the fit ' 439 'parameters. ' 440 'If you do not know the uncertainty of ' 441 'your data leave the "Error Type" as ' 442 '"none". In this case all the data values ' 443 'will be equally weighted during the fit. ' 444 'Alternatives are: a constant uncertainty ' 445 'that is the same for every data point; a ' 446 'percentage of each value; data (a ' 447 'column) specifying the uncertainty for ' 448 'every data point.') 449 450 yerrtype = Dropdown(options = ['none','percent','constant','data'], 451 description = 'Error Type: ', 452 disabled = True) 453 454 def error_settings_OK(): 455 check = True 456 if (yerrtype.value == 'data') and (yerrdata.value == 'Choose error ' 457 'column.'): 458 check = False 459 return check 460 461 def yerr_change(change): 462 df = friendly_to_object[whichframe.value] 463 if change['new'] == 'percent' or change['new'] == 'constant': 464 yerrvalue.disabled = False 465 yerrdata.disabled = True 466 if change['new'] == 'data': 467 yerrvalue.disabled = True 468 tempopts = ['Choose error column.'] 469 tempcols = df.columns.values 470 for k in tempcols: 471 if df[k].dtype != 'O': 472 tempopts.append(k) 473 yerrdata.options=tempopts 474 yerrdata.disabled = False 475 if change['new'] == 'none': 476 yerrvalue.disabled = True 477 yerrdata.disabled = True 478 add_trace_notices.value = trace_notices.notice_html() 479 pass 480 481 yerrtype.observe(yerr_change, names = 'value') 482 483 yerrvalue = FloatText(description = '% or constant: ', disabled = True, 484 style=longdesc, value=1.0) 485 yerrdata = Dropdown(options = ['Choose error column.'], 486 description = 'Error values: ', 487 disabled = True) 488 489 def errdata_change(change): 490 if error_settings_OK(): 491 #trace_notices.deactivate_notice(2) 492 pass 493 else: 494 #trace_notices.activate_notice(2) 495 pass 496 add_trace_notices.value = trace_notices.notice_html() 497 pass 498 499 yerrdata.observe(errdata_change, names = 'value') 500 yerrrow1 = HBox(children=[yerrtype,yerrvalue]) 501 yerror = VBox(children=[yerrrow1,yerrdata]) 502 step2instracc = Accordion(children=[step2instr]) 503 step2instracc.selected_index = None 504 step2 = VBox(children=[step2instr,yerror]) 505 506 # 3. Set fit parameters 507 step3instr = richLabel(value = '<ol><li>Choose the fit type (' 508 'functional form). <span ' 509 'style="color:red">Red symbols are ' 510 'the fit parameters.</span></li>' 511 '<li>You may use the default settings for ' 512 'the ' 513 'initial guesses and parameter ranges or ' 514 'you may set them.</li>' 515 '<li>To fix a value at the ' 516 'initial guess select the "fix" checkbox. ' 517 'You must provide an initial guess if you ' 518 'fix a parameter.</li></ol>') 519 step3instracc = Accordion(children = [step3instr]) 520 step3instracc.set_title(0,'Instructions') 521 step3instracc.selected_index = None 522 # get selected fit model and update parameters list. 523 modeldrop = Dropdown(options=fitmodels) 524 modeleqn = texLabel(value = fitmodeleqns[modeldrop.value]) 525 def getcurrmodel_param(modelname, params_set): 526 ''' 527 Using the model name return ipywidgets for setting the fit 528 parameters and constraints, populated with the default values. 529 :param string modelname: The string name for the lmfit model. 530 :param VBox params_set: The VBox containing the HBoxes for parameter 531 guesses and constraints. 532 :return VBox: params_set with fields reset and those available visible. 533 ''' 534 currmodel = getattr(models,modelname)() 535 currmodel_param = [] 536 labeltext = '' 537 fix = False 538 value = 0 539 min = -sys.float_info.max 540 max = sys.float_info.max 541 expr = None # Not used, maybe for arbitrary functions. 542 for i in range(0,8): 543 fix = False 544 if i < len(currmodel.param_names): 545 labeltext = str(currmodel.param_names[i]) 546 hints = currmodel.param_hints.get(labeltext,None) 547 if isinstance(hints,dict): 548 fix = not(hints.get('vary',True)) 549 value = hints.get('value', 0) 550 min = hints.get('min', -sys.float_info.max) 551 max = hints.get('max', sys.float_info.max) 552 expr = hints.get('expr',None) 553 params_set.children[i].layout.display='' 554 if modelname == 'ExponentialModel': 555 df = friendly_to_object[whichframe.value] 556 xvals = df[Xcoord.value] 557 yvals = df[Ycoord.value] 558 if labeltext == 'amplitude': 559 value = np.mean(yvals) 560 if labeltext == 'decay': 561 value = (np.max(xvals) - np.min(xvals))/3.0 562 if modelname == 'GaussianModel': 563 df = friendly_to_object[whichframe.value] 564 xvals = df[Xcoord.value] 565 yvals = df[Ycoord.value] 566 if labeltext == 'amplitude': 567 value = np.max(yvals) 568 if labeltext == 'sigma': 569 value = 1.0 570 if labeltext == 'center': 571 maxidx = np.argmax(yvals) 572 value = xvals[maxidx] 573 if modelname == 'SineModel': 574 df = friendly_to_object[whichframe.value] 575 xvals = df[Xcoord.value] 576 yvals = df[Ycoord.value] 577 if labeltext == 'amplitude': 578 value = np.max(yvals) 579 if labeltext == 'frequency': 580 # Looking for the most prominent frequency component 581 # to fit. 582 temprange = np.max(xvals) - np.min(xvals) 583 npts = len(xvals) 584 tempfft = np.fft.fft(yvals)[:int(npts/2)] 585 maxloc = np.argmax(np.absolute(tempfft)) 586 value = maxloc*temprange/npts 587 else: 588 labeltext = str(i) 589 params_set.children[i].layout.display='none' 590 params_set.children[i].children[0].value = labeltext+':' 591 params_set.children[i].children[1].children[0].value = fix 592 params_set.children[i].children[1].children[1].value = value 593 params_set.children[i].children[1].children[2].value = min 594 params_set.children[i].children[1].children[3].value = max 595 pass 596 597 def make_param_set(): 598 ''' 599 Creates a VBox with 7 parameters each having fields in an HBox: 600 1. fixcheck (checkbox for fixing the value) 601 2. valuefield (floatText for setting the value) 602 3. minfield (floatText for setting the minimum allowed value) 603 4. maxfield (floatText for setting the maximum allowed value) 604 By default the all VBox components have their `layout.display=none`. 605 :return: VBox 606 ''' 607 import sys 608 currmodel_param=[] 609 for i in range (0,8): 610 fixcheck = Checkbox(value=False, 611 description='Fix (hold)', 612 disabled=False, 613 style=longdesc) 614 valuefield = FloatText(value=0, 615 description='Value: ', 616 disabled=False, 617 style=longdesc) 618 minfield = FloatText(value=-sys.float_info.max, 619 description='Min: ', 620 disabled=False, 621 style=longdesc) 622 maxfield = FloatText(value=sys.float_info.max, 623 description='Max: ', 624 disabled=False, 625 style=longdesc) 626 paramlabel = Label(value = str(i)+':',style=longdesc) 627 parambox = VBox(children=[paramlabel, HBox(children=[fixcheck,valuefield,minfield, 628 maxfield])]) 629 parambox.layout.display = 'none' 630 currmodel_param.append(parambox) 631 params_set = VBox(currmodel_param) 632 return params_set 633 634 def modeldrop_change(change): 635 modeleqn.value=fitmodeleqns[modeldrop.value] 636 getcurrmodel_param(modeldrop.value,params_set) 637 pass 638 modeldrop.observe(modeldrop_change, names = 'value') 639 params_set = make_param_set() 640 getcurrmodel_param(modeldrop.value, params_set) 641 step3 = VBox(children=[step3instracc, HBox(children=[modeldrop,modeleqn]),params_set]) 642 643 # 5.Title, Axes, Format ... 644 step5instr = richLabel(value = '<ul><li><span ' 645 'style="font-weight:bold;">You ' 646 'must set the axes labels to something ' 647 'appropriate.</span> For example if the X - values ' 648 'represent time in seconds "Time (s)" is a good ' 649 'choice. Likewise, choose an appropriate label ' 650 'for the Y - axis.</li>' 651 '<li>If the Aspect Ratio is set to `auto` the ' 652 'figure will fill the default output region. ' 653 'Other choices will allow you to pick the Plot ' 654 'Size. `Large` will use about 2/3 of an HD ' 655 '(1920X1080) screen.</li></ul>') 656 plot_title = Text(value = figname, 657 description = 'Plot title: ', 658 layout = Layout(width='80%')) 659 X_label = Text(placeholder = 'Provide an X-axis label (usually has units)', 660 description = 'X-axis label: ', 661 style = longdesc, 662 layout=Layout(width='45%')) 663 Y_label = Text(placeholder = 'Provide a Y-axis label (usually has units)', 664 description = 'Y-axis label: ', 665 style = longdesc, 666 layout=Layout(width='45%')) 667 def mirror_axes_change(change): 668 if change['new']: 669 mirror_ticks.disabled= False 670 else: 671 mirror_ticks.disabled= True 672 mirror_ticks.value = False 673 pass 674 675 mirror_axes = Checkbox(value = False, 676 description = 'Display Mirror Axes', 677 style = longdesc) 678 mirror_axes.observe(mirror_axes_change, names = 'value') 679 mirror_ticks = Checkbox(value = False, 680 description = 'Mirror Tick Marks', 681 disabled = True) 682 683 def aspect_change(change): 684 if change['new'] != 'auto': 685 plot_size.disabled=False 686 else: 687 plot_size.disabled=True 688 pass 689 690 plot_aspect = Dropdown(options = ['auto', '16:9', '5:3', '7:5', '4:3', 691 '10:8', '1:1'], 692 value = 'auto', 693 description = 'Aspect Ratio: ', 694 style = longdesc) 695 plot_aspect.observe(aspect_change, names = 'value') 696 plot_size = Dropdown(options = ['tiny', 'small', 'medium', 'large', 697 'huge'], 698 value = 'large', 699 description = 'Plot Size: ', 700 style = longdesc, 701 disabled = True) 702 plot_template = Dropdown(options=['none','simple_white', 'ggplot2', 703 'seaborn', 704 'plotly', 'plotly_white', 'plotly_dark', 705 'presentation', 'xgridoff', 'ygridoff', 706 'gridon', 'simple_white+presentation', 707 'simple_white+gridon', 708 'simple_white+presentation+gridon'], 709 value='simple_white', 710 description = 'Plot Styling: ', 711 style = longdesc) 712 step5hbox1 = HBox(children=[X_label, Y_label]) 713 step5hbox2 = HBox(children=[mirror_axes,mirror_ticks, plot_template, 714 plot_aspect, plot_size]) 715 step5 = VBox(children=[step5instr, plot_title, step5hbox1, step5hbox2]) 716 717 # 4. Pick Fit Range(s) 718 step4instr = richLabel(value ='This step is optional. ' 719 'If you define no range(s) all data ' 720 'points will be used in the fit. <ul>' 721 '<li> Click on points to select the ' 722 'beginning and ending of each range of ' 723 'data to include in the fit.</li>' 724 '<li> Click again on ' 725 'a point to deselect it.</li>' 726 '<li> Nearest neighbor pairs of points ' 727 'starting with the lowest point index ' 728 'number are used to define each range. If ' 729 'you select an odd number of points, ' 730 'the last point will be ignored.</li>' 731 '<li> Check the `Extend fitted function ' 732 'plot` box if you want to display ' 733 'calculations of the fitted function and ' 734 'residuals in regions that were not fit ' 735 'to.</li></ul>') 736 extend_fit = Checkbox(value=False, 737 description='Extend fitted function plot', 738 style=longdesc) 739 range_plot = None 740 if JPSLUtils.notebookenv == 'colab': 741 range_plot = richLabel(value = '<span style="color:blue;">' \ 742 'Unfortunately, defining a range by clicking ' \ 743 'on the graph is not yet supported in Google' \ 744 'Colab.</span>') 745 else: 746 range_plot = go.FigureWidget(layout_template='simple_white') 747 range_plot_line_color = 'blue' 748 range_plot_hilight = 'cyan' 749 range_plot_marker_size = 6 750 range_plot_hilight_size = 20 751 ranges=[] 752 753 def update_range_point(trace, points, selector): 754 # size and color must be done separately as they may not be updated 755 # in sync. 756 try: 757 from collections.abc import Iterable 758 except ImportError(e): 759 from collections import Iterable 760 if not isinstance(trace['marker']['size'],Iterable): 761 s = [range_plot_marker_size]*len(trace['x']) 762 else: 763 s = list(trace['marker']['size']) 764 if (not isinstance(trace['marker']['color'],Iterable)) or isinstance( 765 trace['marker']['color'],str): 766 c = [range_plot_line_color]*len(trace['x']) 767 else: 768 c = list(trace['marker']['color']) 769 for i in points.point_inds: 770 if c[i]==range_plot_line_color: 771 c[i] = range_plot_hilight 772 s[i] = range_plot_hilight_size 773 else: 774 c[i] = range_plot_line_color 775 s[i] = range_plot_marker_size 776 with range_plot.batch_update(): 777 trace.marker.color = c 778 trace.marker.size = s 779 pass 780 step4instacc = Accordion(children =[step4instr]) 781 step4instacc.set_title(0,'Instructions (optional step)') 782 step4instacc.selected_index = None 783 step4 = VBox(children=[step4instacc,extend_fit,range_plot]) 784 785 # 6. Final Check* 786 step6instr = richLabel(value = 'Things to check before running the fit:' \ 787 '<ul><li>Fix any problems listed in ' \ 788 '"Notices".</li>' \ 789 '<li>Check for any unpaired parentheses, ' \ 790 'brackets or braces.</li>' \ 791 '<li>Check that all single and double ' \ 792 'quotes are paired.</li>' \ 793 '<li>If you did any manual editing ' \ 794 'double-check for typos.</li>') 795 step6noticebox = richLabel(value = makeplot_notices.notice_html()) 796 797 def dofit_click(change): 798 if JPSLUtils.notebookenv == 'NBClassic': 799 # Commented out do nothing because of timing issues 800 #text = '\n# Force save widget states so that graph will still be\n' 801 #text += '# available when notebook next opened in trusted state.\n' 802 #text += 'import time\ntime.sleep(5)' 803 select_containing_cell('pandasfitGUI') 804 select_cell_immediately_below() 805 #insert_newline_at_end_of_current_cell(text) 806 #jscode = 'Jupyter.actions.call("widgets:save-with-widgets");' 807 #text = 'JPSLUtils.OTJS(\''+jscode+'\')' 808 #insert_newline_at_end_of_current_cell(text) 809 # run the cell to build the plot 810 JPSLUtils.OTJS('Jupyter.notebook.get_selected_cell().execute()') 811 # remove the GUI cell 812 select_containing_cell('pandasfitGUI') 813 delete_selected_cell() 814 pass 815 816 dofitbut_lay = Layout(visibility = "hidden") 817 if JPSLUtils.notebookenv == 'NBClassic': 818 dofitbut_lay = Layout(visibility="visible") 819 dofitbut = Button(description = 'Do Fit', 820 disabled = True, 821 layout = dofitbut_lay) 822 dofitbut.on_click(dofit_click) 823 step6vbox = VBox(children=[dofitbut,step6noticebox]) 824 step6 = HBox(children=[step6instr,step6vbox]) 825 826 827 steps = Tab(children=[step1, step2, step3, step4, step5, step6]) 828 steps.set_title(0,'1. Pick Data*') 829 steps.set_title(1,'2. Data Uncertainty*') 830 steps.set_title(2,'3. Set up Model*') 831 steps.set_title(3,'4. Pick Fit Range(s)') 832 steps.set_title(4, '5. Axes & Format*') 833 steps.set_title(5, '6. Final Check*') 834 835 def tab_changed(change): 836 nonlocal importstr, step1str, step2str, step3str, step4str, step5str, \ 837 range_chosen 838 dfname = friendly_to_globalname[whichframe.value] 839 if change['old'] == 0: 840 # Update step 1 string 841 step1str = '# Define data and trace name\n' 842 step1str += 'Xvals = '+dfname+'[\"' 843 step1str += str(Xcoord.value)+'\"]\n' 844 step1str += 'Yvals = ' + dfname +'[\"' 845 step1str += str(Ycoord.value)+'\"]\n' 846 step1str += 'tracename = \"'+str(trace_name.value)+'\"\n\n' 847 pass 848 if change['old'] == 1: 849 # TODO need do something in case tab is changed before a click 850 # occurs outside a box that was just change. blur things will 851 # require ipywidgets v8+ 852 # update step 2 string 853 step2str = '# Define error (uncertainty)\n' 854 if yerrtype.value == 'none': 855 step2str += 'Yerr = ' + dfname + '[\"' 856 step2str += str(Ycoord.value) + '\"]*0.0 + 1.0\n\n' 857 if yerrtype.value=='constant': 858 step2str += 'Yerr = ' + dfname +'[\"' 859 step2str += str(Ycoord.value)+'\"]*0.0 + ' + str( 860 yerrvalue.value) + '\n\n' 861 if yerrtype.value == 'percent': 862 step2str += 'Yerr = np.fabs('+ dfname +'[\"' 863 step2str += str(Ycoord.value)+'\"])*0.01*' + str( 864 yerrvalue.value) + '\n\n' 865 if yerrtype.value == 'data': 866 step2str += 'Yerr = ' + dfname +'[\"' 867 step2str += str(yerrdata.value)+'\"]\n\n' 868 pass 869 if change['old']== 2: 870 # update step 3 string 871 step3str = '# Define the fit model, initial guesses, ' \ 872 'and constraints\n' 873 step3str += 'fitmod = lmfit.models.'+str(modeldrop.value)+'()\n' 874 currmodel = getattr(models, str(modeldrop.value))() 875 for k in params_set.children: 876 param_name = str(k.children[0].value.split(':')[0]) 877 if param_name in currmodel.param_names: 878 step3str += 'fitmod.set_param_hint(\"'+param_name+'\",' 879 step3str += ' vary = '+str(not(k.children[1].children[ 880 0].value)) 881 temp_val = k.children[1].children[1].value 882 def tst_temp_val(temp_val): 883 if (temp_val != np.nan) and (temp_val != np.inf) and\ 884 (temp_val != -np.inf) and (temp_val != \ 885 -sys.float_info.max) and (temp_val != \ 886 sys.float_info.max): 887 return True 888 else: 889 return False 890 if tst_temp_val(temp_val): 891 step3str += ', value = ' + str(temp_val) 892 temp_val = k.children[1].children[2].value 893 if tst_temp_val(temp_val): 894 step3str += ', min = ' + str(temp_val) 895 temp_val = k.children[1].children[3].value 896 if tst_temp_val(temp_val): 897 step3str += ', max = ' + str(temp_val) 898 step3str += ')\n' 899 step3str +='\n' 900 pass 901 if change['new']>=4: 902 ranges = [] 903 if JPSLUtils.notebookenv != 'colab': 904 # update ranges 905 range_start = True 906 new_range = [] 907 if len(range_plot.data)>0: 908 for i in range(len(range_plot.data[0].marker.color)): 909 if range_plot.data[0].marker.color[i] == range_plot_hilight: 910 new_range.append(i) 911 if not range_start: 912 ranges.append(new_range) 913 new_range = [] 914 range_start = not range_start 915 # update step 4 string 916 covscalestr = 'False' 917 if yerrtype.value == 'none': 918 covscalestr = 'True' 919 if len(ranges) > 0: 920 range_chosen = True 921 step4str = '# Define fit ranges\n' 922 step4str += 'Yfiterr = copy.deepcopy(Yerr) # ranges not to ' \ 923 'fit = np.inf\n' 924 step4str += 'Xfitdata = copy.deepcopy(Xvals) # ranges where ' \ 925 'fit not displayed = np.nan\n' 926 for i in range(len(ranges)): 927 if i == 0 and ranges[0][0]>0: 928 step4str += 'Yfiterr[int(0):int('+str(ranges[0][0])+ \ 929 ')] = np.inf\n' 930 step4str += 'Xfitdata[int(0):int('+str(ranges[0][0])+\ 931 ')] = np.nan\n' 932 if (i + 1) < len(ranges): 933 step4str += 'Yfiterr[int('+str(ranges[i][1]+1)+\ 934 '):int('+str(ranges[i+1][0])+')] = np.inf\n' 935 step4str += 'Xfitdata[int('+str(ranges[i][1]+1)+ \ 936 '):int('+str(ranges[i+1][0])+')] = np.nan\n' 937 if i+1 == len(ranges): 938 step4str += 'Yfiterr[int('+str(ranges[i][1]+1)+\ 939 '):int('+str(len(range_plot.data[0].marker. 940 color))+')] = np.inf\n' 941 step4str += 'Xfitdata[int('+str(ranges[i][1]+1)+\ 942 '):int('+str(len(range_plot.data[0].marker. 943 color))+')] = np.nan\n' 944 step4str += '\n' 945 step4str += '# Do fit\n' 946 step4str += str(fitname)+' = fitmod.fit(Yvals, x=Xvals, ' \ 947 'weights = 1/Yfiterr, scale_covar = '+covscalestr+', ' \ 948 'nan_policy = \"omit\")\n\n' 949 else: 950 range_chosen = False 951 step4str = '# Do fit\n' 952 step4str += str(fitname)+' = fitmod.fit(Yvals, x=Xvals, ' \ 953 'weights = 1/Yerr, scale_covar = '+covscalestr+', ' \ 954 'nan_policy = \"omit\")\n\n' 955 step4str += '# Calculate residuals (data - fit) because lmfit\n' 956 step4str += '# does not calculate for all points under all ' \ 957 'conditions\n' 958 step4str += 'resid = []\n' 959 step4str += ('# explicit int(0) below avoids collisions with some ' 960 'preparsers.\n') 961 step4str += 'for i in range(int(0),len('+str(fitname)+'.data)):\n' 962 step4str += ' resid.append('+str(fitname)+'.data[' \ 963 'i]-'+str(fitname)+'.best_fit[i])\n\n' 964 pass 965 if change['old'] == 4: 966 # update step 5 string 967 step5str = '' 968 if range_chosen: 969 xstr = 'Xfitdata' 970 if not(extend_fit.value): 971 step5str += '# Delete residuals in ranges not fit\n' 972 step5str += '# and fit values that are not ' \ 973 'displayed.\n' 974 step5str += 'for i in range(len(resid)):\n' 975 step5str += ' if np.isnan(Xfitdata[i]):\n' 976 step5str += ' resid[i] = None\n' 977 step5str += ' '+str(fitname)+'.best_fit[i] = ' \ 978 'None\n\n' 979 else: 980 xstr = 'Xvals' 981 errbarstr = '' 982 if yerrtype.value!='none': 983 errbarstr = ', error_y_type=\"data\", error_y_array=Yerr' 984 xresidstr = xstr 985 if extend_fit.value: 986 xresidstr = 'Xvals' 987 mirrorstr = '' 988 if mirror_axes.value: 989 mirrorstr = ', mirror = True' 990 if mirror_ticks.value: 991 mirrorstr = ', mirror = \"ticks\"' 992 # the plot 993 step5str += '# Plot Results\n' 994 step5str += ('# explicit int(..) below avoids collisions with ' 995 'some preparsers.\n') 996 step5str += str(figname) + ' = go.FigureWidget(' \ 997 'layout_template=\"'+str( 998 plot_template.value)+'\")\n' 999 step5str += str(figname)+ '.update_layout(title = \"'+ \ 1000 str(plot_title.value)+'\",' 1001 text = '' 1002 if plot_aspect.value == 'auto': 1003 text += 'autosize=True)\n' 1004 else: 1005 if plot_size.value == 'tiny': 1006 plot_width = 300 1007 elif plot_size.value == 'small': 1008 plot_width = 450 1009 elif plot_size.value == 'medium': 1010 plot_width = 800 1011 elif plot_size.value == 'large': 1012 plot_width = 1200 1013 elif plot_size.value == 'huge': 1014 plot_width = 2400 1015 if plot_aspect.value == '16:9': 1016 plot_height = int(9 * plot_width / 16) 1017 elif plot_aspect.value == '5:3': 1018 plot_height = int(3 * plot_width / 5) 1019 elif plot_aspect.value == '7:5': 1020 plot_height = int(5 * plot_width / 7) 1021 elif plot_aspect.value == '4:3': 1022 plot_height = int(3 * plot_width / 4) 1023 elif plot_aspect.value == '10:8': 1024 plot_height = int(8 * plot_width / 10) 1025 elif plot_aspect.value == '1:1': 1026 plot_height = plot_width 1027 text += 'autosize=False, width = int(' 1028 text += str(plot_width) + '), height=int(' 1029 text += str(plot_height) + '))\n' 1030 step5str += text 1031 step5str += str(figname) + '.set_subplots(rows=int(2), cols=int(1), ' \ 1032 'row_heights=[0.2,0.8], ' \ 1033 'shared_xaxes=True)\n' 1034 step5str += 'scat = go.Scatter(y=resid,x='+xresidstr+', ' \ 1035 'mode=\"markers\",' \ 1036 'name = \"residuals\"'+errbarstr+')\n' 1037 step5str += str(figname) + '.update_yaxes(title = ' \ 1038 '\"Residuals\", ' \ 1039 'row=int(1), col=int(1), zeroline=True, zerolinecolor = ' \ 1040 '\"lightgrey\"'+str(mirrorstr)+')\n' 1041 if mirror_axes.value: 1042 step5str += str(figname) + '.update_xaxes(' \ 1043 'row=int(1), col=int(1)'+str(mirrorstr)+')\n' 1044 step5str += str(figname) + '.add_trace(scat,col=int(1),row=int(1))\n' 1045 step5str += 'scat = go.Scatter(x=Xvals, y=Yvals, ' \ 1046 'mode=\"markers\", name=tracename'+errbarstr+')\n' 1047 step5str += str(figname) + '.add_trace(scat, col=int(1), ' \ 1048 'row=int(2))\n' 1049 step5str += str(figname) + '.update_yaxes(title = ' \ 1050 '\"'+Y_label.value+'\", ' \ 1051 'row=int(2), col=int(1)'+str(mirrorstr)+')\n' 1052 step5str += str(figname) + '.update_xaxes(title = ' \ 1053 '\"'+X_label.value+'\", ' \ 1054 'row=int(2), col=int(1)'+str(mirrorstr)+')\n' 1055 if extend_fit.value: 1056 step5str += 'scat = go.Scatter(y='+str( 1057 fitname)+'.best_fit, x=Xvals, mode=\"lines\", '\ 1058 'line_color = \"black\", ' \ 1059 'name=\"extrapolated\",' \ 1060 'line_dash=\"dash\")\n' 1061 step5str += str(figname) + '.add_trace(scat, col=int(1), ' \ 1062 'row=int(2))\n' 1063 step5str += 'scat = go.Scatter(y='+str(fitname)+'.best_fit,' \ 1064 'x='+xstr+', mode=\"lines\", ' \ 1065 'name=\"fit\", line_color = ' \ 1066 '\"black\", line_dash=\"solid\")\n' 1067 step5str += str(figname) + '.add_trace(scat,col=int(1),row=int(2))\n' 1068 step5str += str(figname) + '.show(config = ' \ 1069 '{\'toImageButtonOptions\': {' \ 1070 '\'format\': \'svg\'}})\n\n' 1071 pass 1072 if change['new'] == 3 and JPSLUtils.notebookenv != 'colab': 1073 df = friendly_to_object[whichframe.value] 1074 rangex = df[Xcoord.value] 1075 rangey = df[Ycoord.value] 1076 c =[] 1077 s = [] 1078 if len(range_plot.data)> 0 and len(range_plot.data[ 1079 0].marker.color) == len(range_plot.data[0]['x']): 1080 c = list(range_plot.data[0].marker.color) 1081 s = list(range_plot.data[0].marker.size) 1082 range_plot.data=[] 1083 range_plot.add_scatter(x=rangex,y=rangey, mode = 'markers', 1084 line_color = range_plot_line_color, 1085 marker_size = range_plot_marker_size) 1086 if len(range_plot.data[0]['x']) == len(c): 1087 with range_plot.batch_update(): 1088 range_plot.data[0].marker.color = c 1089 range_plot.data[0].marker.size = s 1090 range_plot.data[0].on_click(update_range_point) 1091 if change['new'] ==5: 1092 if X_label.value == '' or Y_label.value == '': 1093 makeplot_notices.activate_notice(2) 1094 dofitbut.disabled = True 1095 dofitbut.button_style = '' 1096 else: 1097 makeplot_notices.deactivate_notice(2) 1098 if Xcoord.value == 'Choose X-coordinate.' or \ 1099 Ycoord.value == 'Choose X-coordinate.': 1100 makeplot_notices.activate_notice(0) 1101 dofitbut.disabled = True 1102 dofitbut.button_style = '' 1103 else: 1104 makeplot_notices.deactivate_notice(0) 1105 if modeldrop.value == '': 1106 makeplot_notices.activate_notice(1) 1107 dofitbut.disabled = True 1108 dofitbut.button_style = '' 1109 else: 1110 makeplot_notices.deactivate_notice(1) 1111 step6noticebox.value = makeplot_notices.notice_html() 1112 if len(makeplot_notices.get_active()) == 0: 1113 dofitbut.disabled = False 1114 dofitbut.button_style = 'success' 1115 # the best fit equation 1116 step6str = '# Display best fit equation\n' 1117 step6str += fitresultstrs[modeldrop.value](fitname) 1118 if JPSLUtils.notebookenv == 'NBClassic': 1119 JPSLUtils.select_containing_cell('pandasfitGUI') 1120 JPSLUtils.replace_text_of_next_cell(importstr + step1str + \ 1121 step2str + step3str + \ 1122 step4str + step5str + step6str) 1123 else: 1124 codearea.sniptext.value = importstr + step1str + step2str + \ 1125 step3str + step4str + step5str + \ 1126 pseudoLatexToLatex(step6str) 1127 pass 1128 1129 steps.observe(tab_changed, names = 'selected_index') 1130 with output: 1131 display(steps) 1132 if JPSLUtils.notebookenv == 'NBClassic': 1133 display(output) 1134 select_containing_cell('pandasfitGUI') 1135 new_cell_immediately_below() 1136 else: 1137 codearea = build_run_snip_widget('', output) 1138 with output: 1139 display(codearea) 1140 display(output) 1141 pass
6def calcresid(result): 7 ''' 8 lmfit has empty values for residuals where the weighting is infinite or not defined. 9 This calculates all the residuals based on the actual data and fit results. 10 11 :param ModelResult result: An lmfit ModelResult. 12 13 :return np.array residuals: The residuals. 14 ''' 15 import numpy as np 16 import lmfit as lmfit 17 resid = [] 18 for i in range(0, len(results.data)): 19 resid.append(results.data[i] - results.best_fit[i]) 20 return np.array(resid)
lmfit has empty values for residuals where the weighting is infinite or not defined. This calculates all the residuals based on the actual data and fit results.
Parameters
- ModelResult result: An lmfit ModelResult.
Returns
The residuals.
22def fit_pandas_GUI(df_info=None, show_text_col = False, **kwargs): 23 """ 24 If passed no parameters this will look for all the dataframes in the user 25 namespace and make them available for plotting. Once a 26 dataframe is chosen only the numerical columns from that dataframe will 27 be available for inclusion in the plotting expression. 28 29 This GUI produces code to use the lmfit package to fit data and the plotly 30 interactive plotting package to display the results. 31 32 If you wish to allow only certain dataframes or have them show up as 33 user friendly names in the menus provide that information in the first 34 paramater df_info. 35 36 To allow inclusion of text columns pass True for show_text_col. 37 38 :param bool show_text_col: (default = False). When True columns 39 containing text will be shown. 40 41 :param list df_info: List of Lists [[object,globalname, 42 userfriendly]],..] 43 * object -- pandas.DataFrame 44 * globalname -- string name of the object in the user global name space. 45 * userfriendly -- string name to display for user selection. 46 47 :keyword string figname: string used to override default python name for 48 figure. 49 50 :keyword string fitname: string used to override default python name for 51 fit. 52 53 :keyword bool findframes: default = True. If set to false and dataframes 54 are passed in dfs_info, will not search for dataframes in the user 55 namespace. 56 """ 57 from ipywidgets import Layout, Box, HBox, VBox, GridBox, Tab, \ 58 Accordion, Dropdown, Label, Text, Button, Checkbox, FloatText, \ 59 RadioButtons, BoundedIntText, Output 60 from ipywidgets import HTML as richLabel 61 from ipywidgets import HTMLMath as texLabel 62 from IPython.display import display, HTML 63 from IPython.display import Javascript as JS 64 from IPython import get_ipython 65 from JPSLUtils.utils import new_cell_immediately_below,\ 66 select_cell_immediately_below, move_cursor_in_current_cell, \ 67 insert_text_into_next_cell, insert_text_at_beginning_of_current_cell, \ 68 insert_newline_at_end_of_current_cell, select_containing_cell, \ 69 delete_selected_cell, iconselector, notice_group, \ 70 replace_text_of_current_cell, pseudoLatexToLatex 71 from lmfit import models 72 73 from .utils import find_pandas_dataframe_names, build_run_snip_widget 74 from IPython import get_ipython 75 global_dict = get_ipython().user_ns 76 JPSLUtils = global_dict["JPSLUtils"] 77 if JPSLUtils.notebookenv != 'colab': 78 import plotly.graph_objects as go 79 dfs_info = [] 80 if isinstance(df_info,list): 81 for k in df_info: 82 dfs_info.append(k) 83 findframes = kwargs.pop('findframes',True) 84 if findframes: 85 for k in find_pandas_dataframe_names(): 86 dfs_info.append([global_dict[k],k,k]) 87 friendly_to_globalname = {k[2]:k[1] for k in dfs_info} 88 friendly_to_object = {k[2]:k[0] for k in dfs_info} 89 90 fitname = kwargs.pop('fitname',None) 91 from .utils import find_fit_names 92 fitlst = find_fit_names() 93 if fitname in fitlst: 94 raise UserWarning (str(fitname) + ' already exists. Choose a ' 95 'different name for the fit.') 96 if fitname == None: 97 fitname = 'Fit_'+str(len(fitlst)+1) 98 99 figname = kwargs.pop('figname',None) 100 from .utils import find_figure_names 101 figlst = find_figure_names() 102 if figname in figlst: 103 raise UserWarning (str(figname) + ' already exists. Choose a ' 104 'different name for the figure.') 105 if figname == None: 106 figname = str(fitname) + '_Figure' 107 108 fitmodels = ['LinearModel','PolynomialModel','ExponentialModel', 109 'GaussianModel','SineModel'] 110 fitmodeleqns = { 111 'LinearModel':r'$fit = \color{red}{a}x+\color{red}{b}$, where $\color{' 112 r'red}{a}$ = slope, $\color{red}{b}$ = intercept', 113 'PolynomialModel': r'$fit = \sum_{n=0}^{\le7}{\color{red}{c_n}x^n} = ' 114 r'\color{red}{c_0} + \color{red}{c_1}x + \color{red}{' 115 r'c_2}x^2 + ...$', 116 'ExponentialModel': r'$fit = \color{red}{A} \exp \left( \frac{-x} ' \ 117 r'{\color{red}{\tau}}\right)$, where $\color{red}{A}$ ' 118 r'= amplitude, $ \color{red}{\tau}$ = decay', 119 'GaussianModel': r'$fit = \frac{\color{red}{A}}{\color{red}{\sigma} ' \ 120 r'\sqrt{2 \pi}} \exp \left( \frac{-(x-\color{red}' \ 121 r'{\mu})^2}{2 \color{red}{\sigma}^2} \right)$, where ' \ 122 r'$\color{red}{A}$ = amplitude, $\color{red}{\sigma}$ = sigma, ' 123 r'$\color{red}{\mu}$ = center', 124 'SineModel': r'$fit = \color{red}{A} \sin \left ( \color{red}{f}x + ' 125 r'\color{red}{\phi} \right)$, ' 126 r'where $\color{red}{A}$ = ' \ 127 r'amplitude, $\color{red}{f}$ = frequency, '\ 128 r'$\color{red}{\phi}$ = shift' 129 } 130 131 def polymodelresultstr(resultname): 132 template = '' \ 133 'fitstr = r\'$fit = \'\n' \ 134 'termcount = int(0)\n' \ 135 'for k in %result.params.keys():\n' \ 136 ' pwr = int(str(k)[int(-1):])\n' \ 137 ' if %result.params[k].vary:\n' \ 138 ' if termcount > int(0):\n' \ 139 ' fitstr += \' + \'\n' \ 140 ' fitstr += r\'({%COLOR{red}{\'+rue.latex_rndwitherr(' \ 141 '%result.params[k].value,\n' \ 142 ' %result.params[k].stderr, \n' \ 143 ' errdig=int(1), \n' \ 144 ' lowmag=-int(3))+\'}})\'\n' \ 145 ' if pwr == int(1):\n' \ 146 ' fitstr += \'x\'\n' \ 147 ' if pwr > int(1):\n' \ 148 ' fitstr += \'x^\'+str(pwr)\n' \ 149 ' termcount+=int(1)\n' \ 150 ' else:\n' \ 151 ' if %result.params[k].value!=0.0:\n' \ 152 ' if termcount > int(0):\n' \ 153 ' fitstr += \'+\'\n' \ 154 ' fitstr += r\'({%COLOR{blue}{\'+str(' \ 155 '%result.params[k].value)+\'}})\'\n' \ 156 ' termcount +=int(1)\n' \ 157 ' if pwr == int(1):\n' \ 158 ' fitstr += \'x\'\n' \ 159 ' if pwr > int(1):\n' \ 160 ' fitstr += \'x^\'+str(pwr)\n' \ 161 'fitstr+=\'$\'\n' \ 162 'captionstr=r\'<p>Use the command <code>%result</code> as the ' \ 163 'last line of a code cell for more details.</p>\'\n' \ 164 'display(Math(fitstr))\n' \ 165 'display(HTML(captionstr))' 166 return template.replace('%result', str(resultname)) 167 168 def linmodelresultstr(resultname): 169 template = '' \ 170 'slopestr = ''\'\'\n' \ 171 'interceptstr = ''\'\'\n' \ 172 'for k in %results.params.keys():\n' \ 173 ' if %results.params[k].vary:\n' \ 174 ' paramstr = r\'({%COLOR{red}{\'+rue.latex_rndwitherr(' \ 175 '%results.params[k].value,\n' \ 176 ' %results.params[k].stderr,\n' \ 177 ' errdig=int(1),\n' \ 178 ' lowmag=-int(3))+\'}})\'\n' \ 179 ' else:\n' \ 180 ' paramstr = r\'{%COLOR{blue}{\'+str(%results.params[' \ 181 'k].value,' \ 182 '\n' \ 183 ' )+\'}}\'\n' \ 184 ' if k == \'slope\':\n' \ 185 ' slopestr = paramstr\n' \ 186 ' if k == \'intercept\' and %results.params[k].value != 0:\n' \ 187 ' interceptstr = \' + \' + paramstr\n' \ 188 'fitstr = r\'$fit = \'+slopestr + \'x\' + interceptstr + \'$\'\n' \ 189 'captionstr = r\'<p>Use the command <code>%results</code> as the ' \ 190 'last line of a code cell for more details.</p>\'\n' \ 191 'display(Math(fitstr))\n' \ 192 'display(HTML(captionstr))' 193 return template.replace('%results', resultname) 194 195 def expmodelresultstr(resultname): 196 template = '' \ 197 'ampstr = ''\'\'\n' \ 198 'decaystr = ''\'\'\n' \ 199 'for k in %results.params.keys():\n' \ 200 ' if %results.params[k].vary:\n' \ 201 ' paramstr = r\'({%COLOR{red}{\'+rue.latex_rndwitherr(' \ 202 '%results.params[k].value,\n' \ 203 ' %results.params[k].stderr,\n' \ 204 ' errdig=int(1),\n' \ 205 ' lowmag=-int(3))+\'}})\'\n' \ 206 ' else:\n' \ 207 ' paramstr = r\'{%COLOR{blue}{\'+str(%results.params[' \ 208 'k].value, \n' \ 209 ' )+\'}}\'\n' \ 210 ' if k == \'amplitude\':\n' \ 211 ' ampstr = paramstr\n' \ 212 ' if k == \'decay\':\n' \ 213 ' decaystr = paramstr\n' \ 214 'fitstr = r\'$$fit = \'+ampstr+r\'%EXP %LEFT( %FRAC{-x}' \ 215 '{\'+decaystr+r\'}%RIGHT)$$\'\n' \ 216 'captionstr = r\'<p>Use the command <code>%results</code> as the ' \ 217 'last line of a code cell for more details.</p>\'\n' \ 218 'display(Math(fitstr))\n' \ 219 'display(HTML(captionstr))' 220 return template.replace('%results',resultname) 221 222 def gausmodelresultstr(resultname): 223 template = '' \ 224 'ampstr = ''\'\'\n' \ 225 'centstr = ''\'\'\n' \ 226 'sigmastr = ''\'\'\n' \ 227 'for k in %results.params.keys():\n' \ 228 ' if %results.params[k].vary:\n' \ 229 ' paramstr = r\'({%COLOR{red}{\'+rue.latex_rndwitherr(' \ 230 '%results.params[k].value,\n' \ 231 ' %results.params[k].stderr,\n' \ 232 ' errdig=int(1),\n' \ 233 ' lowmag=-int(3))+\'}})\'\n' \ 234 ' else:\n' \ 235 ' paramstr = r\'{%COLOR{blue}{\'+str(%results.params[' \ 236 'k].value, \n' \ 237 ' )+\'}}\'\n' \ 238 ' if k == \'amplitude\':\n' \ 239 ' ampstr = paramstr\n' \ 240 ' if k == \'center\':\n' \ 241 ' centstr = paramstr\n' \ 242 ' if k == \'sigma\':\n' \ 243 ' sigmastr = paramstr\n' \ 244 'fitstr = r\'$$fit = %FRAC{\'+ampstr+\'}{' \ 245 '\'+sigmastr+r\'%SQRT{2%PI}}%EXP %LEFT( %FRAC{' \ 246 '-%LEFT[x-\'+centstr+r\'%RIGHT]^2}' \ 247 '{2\'+sigmastr+r\'^2}%RIGHT)$$\'\n' \ 248 'captionstr = r\'<p>Use the command <code>%results</code> as the ' \ 249 'last line of a code cell for more details.</p>\'\n' \ 250 'display(Math(fitstr))\n' \ 251 'display(HTML(captionstr))' 252 return template.replace('%results',resultname) 253 254 def sinmodelresultstr(resultname): 255 template = '' \ 256 'ampstr = \'\'\n' \ 257 'freqstr = \'\'\n' \ 258 'shiftstr = \'\'\n' \ 259 'for k in %results.params.keys():\n' \ 260 ' if %results.params[k].vary:\n' \ 261 ' if %results.params[k].stderr:\n' \ 262 ' paramstr = r\'({%COLOR{red}{\'+rue.latex_rndwitherr(' \ 263 '%results.params[k].value,\n' \ 264 ' %results.params[k].stderr,\n' \ 265 ' errdig=int(1),\n' \ 266 ' lowmag=-int(3))+\'}})\'\n' \ 267 ' else:\n' \ 268 ' paramstr = r\'{%COLOR{red}{\'+' \ 269 'str(%results.params[k].value)+\'}}\'\n' \ 270 ' else:\n' \ 271 ' paramstr = r\'{%COLOR{blue}{\'+str(%results.params[k].value' \ 272 ')+\'}}\'\n' \ 273 ' if k == \'amplitude\':\n' \ 274 ' ampstr = paramstr\n' \ 275 ' if k == \'frequency\':\n' \ 276 ' freqstr = paramstr\n' \ 277 ' if k == \'shift\' and %results.params[k].value != 0.0:\n' \ 278 ' shiftstr = \' + \' + paramstr\n' \ 279 'fitstr = r\'$fit = \'+ampstr + \'sin[\' + freqstr + \'x\' + shiftstr + \']$\'\n' \ 280 'captionstr = r\'<p>Use the command <code>%results</code> as the ' \ 281 'last line of a code cell for more details.</p>\'\n' \ 282 'display(Math(fitstr))\n' \ 283 'display(HTML(captionstr))' 284 return template.replace('%results', resultname) 285 286 fitresultstrs = { 287 'LinearModel': linmodelresultstr, 288 'PolynomialModel': polymodelresultstr, 289 'ExponentialModel': expmodelresultstr, 290 'GaussianModel': gausmodelresultstr, 291 'SineModel': sinmodelresultstr 292 } 293 294 importstr = '# CODE BLOCK generated using fit_pandas_GUI().\n# See '\ 295 'https://jupyterphysscilab.github.io/jupyter_Pandas_GUI.\n' \ 296 '# Integers wrapped in `int()` to avoid having them cast \n' \ 297 '# as other types by interactive preparsers. \n' \ 298 '# Imports (no effect if already imported)\n' \ 299 'import numpy as np\n' \ 300 'import lmfit as lmfit\n' \ 301 'import round_using_error as rue\n' \ 302 'import copy as copy\n' \ 303 'from plotly import graph_objects as go\n' \ 304 'from IPython.display import HTML, Math\n\n' 305 step1str = '' 306 step2str = '' 307 step3str = '' 308 step4str = '' 309 step5str = '' 310 step6str = '' 311 range_chosen = False 312 #### Define GUI Elements #### 313 # Those followed by a * are required. 314 output = Output() 315 with output: 316 display(HTML( 317 "<h3 id ='pandasfitGUI' style='text-align:center;'>Pandas Fit " 318 "Composer</h3> <div style='text-align:center;'>" 319 "<span style='color:green;'>Steps with a * are required.</span> The " 320 "code that will generate the fit is being " 321 "built in the textbox or cell immediately below.</div><div " 322 "style='text-align:center;'>This composer uses a subset of " 323 "<a href ='https://lmfit.github.io/lmfit-py/'> the lmfit package</a>" 324 " and <a href ='https://plotly.com/python/line-and-scatter/#'> " 325 "the plotly scatter plot</a> capabilities.</div>")) 326 327 longdesc = {'description_width':'initial'} 328 329 # Notices for the Final Check Tab. 330 makeplot_notices = notice_group(['Need data to fit', 331 'Need a fit model', 332 'Axes must have labels.'], 333 'Notices:','','red') 334 makeplot_notices.set_active([0,1,2]) 335 336 # 1. Pick and variables to fit and fit model 337 # a. Select Y vs. X (DataFrame, X and Y, which must be from single 338 # frame. 339 # Notices for the Pick Trace(s) tab. 340 notice_list = [ 341 'Data set (DataFrame) required.', 342 'X- and Y-values required.', 343 ] 344 trace_notices = notice_group(notice_list, 'Notices:','','red') 345 trace_notices.set_active([0,1]) 346 step1instr = richLabel(value = '<ol><li>Select a DataFrame (Data ' 347 'set);</li>' 348 '<li>Select the column containing the X ' 349 'values;</li>' 350 '<li>Select the column containing the Y ' 351 'values (what is being fit);</li>' 352 '<li>Provide a name for the trace if you do' 353 ' not like the default. This text will be ' 354 'used for the legend;</li></ol>' 355 ) 356 step1instracc = Accordion(children = [step1instr]) 357 step1instracc.set_title(0,'Instructions') 358 step1instracc.selected_index = None 359 360 # DataFrame selection 361 tempopts = [] 362 tempopts.append('Choose data set.') 363 for k in dfs_info: 364 tempopts.append(k[2]) 365 whichframe = Dropdown(options=tempopts, 366 description='DataFrame: ',) 367 368 def update_columns(change): 369 if change['new'] == 'Choose data set.': 370 Xcoord.disabled = True 371 Ycoord.disabled = True 372 trace_notices.activate_notice(0) 373 trace_notices.activate_notice(1) 374 trace_notices.activate_notice(2) 375 add_trace_notices.value = trace_notices.notice_html() 376 return 377 df = friendly_to_object[change['new']] 378 tempcols = df.columns.values 379 tempopt = ['Choose column for coordinate.'] 380 for k in tempcols: 381 if show_text_col: 382 tempopt.append(k) 383 else: 384 if df[k].dtype != 'O': 385 tempopt.append(k) 386 Xcoord.options = tempopt 387 Xcoord.value = tempopt[0] 388 Ycoord.options = tempopt 389 Ycoord.value = tempopt[0] 390 Xcoord.disabled = False 391 Ycoord.disabled = False 392 trace_notices.activate_notice(1) 393 trace_notices.deactivate_notice(0) 394 add_trace_notices.value =trace_notices.notice_html() 395 pass 396 whichframe.observe(update_columns, names='value') 397 398 # Data selection 399 Xcoord = Dropdown(options=['Choose X-coordinate.'], 400 description='X: ', 401 disabled = True) 402 Ycoord = Dropdown(options=['Choose Y-coordinate.'], 403 description='Y: ', 404 disabled = True) 405 def trace_name_update(change): 406 if change['new'] != 'Choose column for coordinate.': 407 trace_name.value = Ycoord.value 408 if Xcoord.value != 'Choose column for coordinate.' and Ycoord.value \ 409 != 'Choose column for coordinate.': 410 yerrtype.disabled = False 411 trace_name.disabled = False 412 trace_notices.deactivate_notice(1) 413 add_trace_notices.value = trace_notices.notice_html() 414 else: 415 yerrtype.disabled = True 416 trace_name.disabled = True 417 trace_notices.activate_notice(1) 418 add_trace_notices.value = trace_notices.notice_html() 419 pass 420 Xcoord.observe(trace_name_update,names='value') 421 Ycoord.observe(trace_name_update,names='value') 422 423 # Trace name 424 trace_name = Text(placeholder = 'Trace name for legend', 425 description = 'Trace name: ', 426 disabled = True) 427 428 trace_notices.set_active([0,1]) 429 add_trace_notices = richLabel(value = trace_notices.notice_html()) 430 step1tracebox = VBox(children=[whichframe,Xcoord,Ycoord,trace_name]) 431 step1actionbox = VBox(children=[add_trace_notices]) 432 step1hbox = HBox(children=[step1tracebox,step1actionbox]) 433 step1 = VBox(children=[step1instracc, step1hbox]) 434 435 # 2. Set data uncertainty 436 step2instr = richLabel(value = 'If you know the uncertainty in your data ' 437 'values (Y-values)you should ' 438 'specify it, as the uncertainty impacts ' 439 'the final uncertainty in the fit ' 440 'parameters. ' 441 'If you do not know the uncertainty of ' 442 'your data leave the "Error Type" as ' 443 '"none". In this case all the data values ' 444 'will be equally weighted during the fit. ' 445 'Alternatives are: a constant uncertainty ' 446 'that is the same for every data point; a ' 447 'percentage of each value; data (a ' 448 'column) specifying the uncertainty for ' 449 'every data point.') 450 451 yerrtype = Dropdown(options = ['none','percent','constant','data'], 452 description = 'Error Type: ', 453 disabled = True) 454 455 def error_settings_OK(): 456 check = True 457 if (yerrtype.value == 'data') and (yerrdata.value == 'Choose error ' 458 'column.'): 459 check = False 460 return check 461 462 def yerr_change(change): 463 df = friendly_to_object[whichframe.value] 464 if change['new'] == 'percent' or change['new'] == 'constant': 465 yerrvalue.disabled = False 466 yerrdata.disabled = True 467 if change['new'] == 'data': 468 yerrvalue.disabled = True 469 tempopts = ['Choose error column.'] 470 tempcols = df.columns.values 471 for k in tempcols: 472 if df[k].dtype != 'O': 473 tempopts.append(k) 474 yerrdata.options=tempopts 475 yerrdata.disabled = False 476 if change['new'] == 'none': 477 yerrvalue.disabled = True 478 yerrdata.disabled = True 479 add_trace_notices.value = trace_notices.notice_html() 480 pass 481 482 yerrtype.observe(yerr_change, names = 'value') 483 484 yerrvalue = FloatText(description = '% or constant: ', disabled = True, 485 style=longdesc, value=1.0) 486 yerrdata = Dropdown(options = ['Choose error column.'], 487 description = 'Error values: ', 488 disabled = True) 489 490 def errdata_change(change): 491 if error_settings_OK(): 492 #trace_notices.deactivate_notice(2) 493 pass 494 else: 495 #trace_notices.activate_notice(2) 496 pass 497 add_trace_notices.value = trace_notices.notice_html() 498 pass 499 500 yerrdata.observe(errdata_change, names = 'value') 501 yerrrow1 = HBox(children=[yerrtype,yerrvalue]) 502 yerror = VBox(children=[yerrrow1,yerrdata]) 503 step2instracc = Accordion(children=[step2instr]) 504 step2instracc.selected_index = None 505 step2 = VBox(children=[step2instr,yerror]) 506 507 # 3. Set fit parameters 508 step3instr = richLabel(value = '<ol><li>Choose the fit type (' 509 'functional form). <span ' 510 'style="color:red">Red symbols are ' 511 'the fit parameters.</span></li>' 512 '<li>You may use the default settings for ' 513 'the ' 514 'initial guesses and parameter ranges or ' 515 'you may set them.</li>' 516 '<li>To fix a value at the ' 517 'initial guess select the "fix" checkbox. ' 518 'You must provide an initial guess if you ' 519 'fix a parameter.</li></ol>') 520 step3instracc = Accordion(children = [step3instr]) 521 step3instracc.set_title(0,'Instructions') 522 step3instracc.selected_index = None 523 # get selected fit model and update parameters list. 524 modeldrop = Dropdown(options=fitmodels) 525 modeleqn = texLabel(value = fitmodeleqns[modeldrop.value]) 526 def getcurrmodel_param(modelname, params_set): 527 ''' 528 Using the model name return ipywidgets for setting the fit 529 parameters and constraints, populated with the default values. 530 :param string modelname: The string name for the lmfit model. 531 :param VBox params_set: The VBox containing the HBoxes for parameter 532 guesses and constraints. 533 :return VBox: params_set with fields reset and those available visible. 534 ''' 535 currmodel = getattr(models,modelname)() 536 currmodel_param = [] 537 labeltext = '' 538 fix = False 539 value = 0 540 min = -sys.float_info.max 541 max = sys.float_info.max 542 expr = None # Not used, maybe for arbitrary functions. 543 for i in range(0,8): 544 fix = False 545 if i < len(currmodel.param_names): 546 labeltext = str(currmodel.param_names[i]) 547 hints = currmodel.param_hints.get(labeltext,None) 548 if isinstance(hints,dict): 549 fix = not(hints.get('vary',True)) 550 value = hints.get('value', 0) 551 min = hints.get('min', -sys.float_info.max) 552 max = hints.get('max', sys.float_info.max) 553 expr = hints.get('expr',None) 554 params_set.children[i].layout.display='' 555 if modelname == 'ExponentialModel': 556 df = friendly_to_object[whichframe.value] 557 xvals = df[Xcoord.value] 558 yvals = df[Ycoord.value] 559 if labeltext == 'amplitude': 560 value = np.mean(yvals) 561 if labeltext == 'decay': 562 value = (np.max(xvals) - np.min(xvals))/3.0 563 if modelname == 'GaussianModel': 564 df = friendly_to_object[whichframe.value] 565 xvals = df[Xcoord.value] 566 yvals = df[Ycoord.value] 567 if labeltext == 'amplitude': 568 value = np.max(yvals) 569 if labeltext == 'sigma': 570 value = 1.0 571 if labeltext == 'center': 572 maxidx = np.argmax(yvals) 573 value = xvals[maxidx] 574 if modelname == 'SineModel': 575 df = friendly_to_object[whichframe.value] 576 xvals = df[Xcoord.value] 577 yvals = df[Ycoord.value] 578 if labeltext == 'amplitude': 579 value = np.max(yvals) 580 if labeltext == 'frequency': 581 # Looking for the most prominent frequency component 582 # to fit. 583 temprange = np.max(xvals) - np.min(xvals) 584 npts = len(xvals) 585 tempfft = np.fft.fft(yvals)[:int(npts/2)] 586 maxloc = np.argmax(np.absolute(tempfft)) 587 value = maxloc*temprange/npts 588 else: 589 labeltext = str(i) 590 params_set.children[i].layout.display='none' 591 params_set.children[i].children[0].value = labeltext+':' 592 params_set.children[i].children[1].children[0].value = fix 593 params_set.children[i].children[1].children[1].value = value 594 params_set.children[i].children[1].children[2].value = min 595 params_set.children[i].children[1].children[3].value = max 596 pass 597 598 def make_param_set(): 599 ''' 600 Creates a VBox with 7 parameters each having fields in an HBox: 601 1. fixcheck (checkbox for fixing the value) 602 2. valuefield (floatText for setting the value) 603 3. minfield (floatText for setting the minimum allowed value) 604 4. maxfield (floatText for setting the maximum allowed value) 605 By default the all VBox components have their `layout.display=none`. 606 :return: VBox 607 ''' 608 import sys 609 currmodel_param=[] 610 for i in range (0,8): 611 fixcheck = Checkbox(value=False, 612 description='Fix (hold)', 613 disabled=False, 614 style=longdesc) 615 valuefield = FloatText(value=0, 616 description='Value: ', 617 disabled=False, 618 style=longdesc) 619 minfield = FloatText(value=-sys.float_info.max, 620 description='Min: ', 621 disabled=False, 622 style=longdesc) 623 maxfield = FloatText(value=sys.float_info.max, 624 description='Max: ', 625 disabled=False, 626 style=longdesc) 627 paramlabel = Label(value = str(i)+':',style=longdesc) 628 parambox = VBox(children=[paramlabel, HBox(children=[fixcheck,valuefield,minfield, 629 maxfield])]) 630 parambox.layout.display = 'none' 631 currmodel_param.append(parambox) 632 params_set = VBox(currmodel_param) 633 return params_set 634 635 def modeldrop_change(change): 636 modeleqn.value=fitmodeleqns[modeldrop.value] 637 getcurrmodel_param(modeldrop.value,params_set) 638 pass 639 modeldrop.observe(modeldrop_change, names = 'value') 640 params_set = make_param_set() 641 getcurrmodel_param(modeldrop.value, params_set) 642 step3 = VBox(children=[step3instracc, HBox(children=[modeldrop,modeleqn]),params_set]) 643 644 # 5.Title, Axes, Format ... 645 step5instr = richLabel(value = '<ul><li><span ' 646 'style="font-weight:bold;">You ' 647 'must set the axes labels to something ' 648 'appropriate.</span> For example if the X - values ' 649 'represent time in seconds "Time (s)" is a good ' 650 'choice. Likewise, choose an appropriate label ' 651 'for the Y - axis.</li>' 652 '<li>If the Aspect Ratio is set to `auto` the ' 653 'figure will fill the default output region. ' 654 'Other choices will allow you to pick the Plot ' 655 'Size. `Large` will use about 2/3 of an HD ' 656 '(1920X1080) screen.</li></ul>') 657 plot_title = Text(value = figname, 658 description = 'Plot title: ', 659 layout = Layout(width='80%')) 660 X_label = Text(placeholder = 'Provide an X-axis label (usually has units)', 661 description = 'X-axis label: ', 662 style = longdesc, 663 layout=Layout(width='45%')) 664 Y_label = Text(placeholder = 'Provide a Y-axis label (usually has units)', 665 description = 'Y-axis label: ', 666 style = longdesc, 667 layout=Layout(width='45%')) 668 def mirror_axes_change(change): 669 if change['new']: 670 mirror_ticks.disabled= False 671 else: 672 mirror_ticks.disabled= True 673 mirror_ticks.value = False 674 pass 675 676 mirror_axes = Checkbox(value = False, 677 description = 'Display Mirror Axes', 678 style = longdesc) 679 mirror_axes.observe(mirror_axes_change, names = 'value') 680 mirror_ticks = Checkbox(value = False, 681 description = 'Mirror Tick Marks', 682 disabled = True) 683 684 def aspect_change(change): 685 if change['new'] != 'auto': 686 plot_size.disabled=False 687 else: 688 plot_size.disabled=True 689 pass 690 691 plot_aspect = Dropdown(options = ['auto', '16:9', '5:3', '7:5', '4:3', 692 '10:8', '1:1'], 693 value = 'auto', 694 description = 'Aspect Ratio: ', 695 style = longdesc) 696 plot_aspect.observe(aspect_change, names = 'value') 697 plot_size = Dropdown(options = ['tiny', 'small', 'medium', 'large', 698 'huge'], 699 value = 'large', 700 description = 'Plot Size: ', 701 style = longdesc, 702 disabled = True) 703 plot_template = Dropdown(options=['none','simple_white', 'ggplot2', 704 'seaborn', 705 'plotly', 'plotly_white', 'plotly_dark', 706 'presentation', 'xgridoff', 'ygridoff', 707 'gridon', 'simple_white+presentation', 708 'simple_white+gridon', 709 'simple_white+presentation+gridon'], 710 value='simple_white', 711 description = 'Plot Styling: ', 712 style = longdesc) 713 step5hbox1 = HBox(children=[X_label, Y_label]) 714 step5hbox2 = HBox(children=[mirror_axes,mirror_ticks, plot_template, 715 plot_aspect, plot_size]) 716 step5 = VBox(children=[step5instr, plot_title, step5hbox1, step5hbox2]) 717 718 # 4. Pick Fit Range(s) 719 step4instr = richLabel(value ='This step is optional. ' 720 'If you define no range(s) all data ' 721 'points will be used in the fit. <ul>' 722 '<li> Click on points to select the ' 723 'beginning and ending of each range of ' 724 'data to include in the fit.</li>' 725 '<li> Click again on ' 726 'a point to deselect it.</li>' 727 '<li> Nearest neighbor pairs of points ' 728 'starting with the lowest point index ' 729 'number are used to define each range. If ' 730 'you select an odd number of points, ' 731 'the last point will be ignored.</li>' 732 '<li> Check the `Extend fitted function ' 733 'plot` box if you want to display ' 734 'calculations of the fitted function and ' 735 'residuals in regions that were not fit ' 736 'to.</li></ul>') 737 extend_fit = Checkbox(value=False, 738 description='Extend fitted function plot', 739 style=longdesc) 740 range_plot = None 741 if JPSLUtils.notebookenv == 'colab': 742 range_plot = richLabel(value = '<span style="color:blue;">' \ 743 'Unfortunately, defining a range by clicking ' \ 744 'on the graph is not yet supported in Google' \ 745 'Colab.</span>') 746 else: 747 range_plot = go.FigureWidget(layout_template='simple_white') 748 range_plot_line_color = 'blue' 749 range_plot_hilight = 'cyan' 750 range_plot_marker_size = 6 751 range_plot_hilight_size = 20 752 ranges=[] 753 754 def update_range_point(trace, points, selector): 755 # size and color must be done separately as they may not be updated 756 # in sync. 757 try: 758 from collections.abc import Iterable 759 except ImportError(e): 760 from collections import Iterable 761 if not isinstance(trace['marker']['size'],Iterable): 762 s = [range_plot_marker_size]*len(trace['x']) 763 else: 764 s = list(trace['marker']['size']) 765 if (not isinstance(trace['marker']['color'],Iterable)) or isinstance( 766 trace['marker']['color'],str): 767 c = [range_plot_line_color]*len(trace['x']) 768 else: 769 c = list(trace['marker']['color']) 770 for i in points.point_inds: 771 if c[i]==range_plot_line_color: 772 c[i] = range_plot_hilight 773 s[i] = range_plot_hilight_size 774 else: 775 c[i] = range_plot_line_color 776 s[i] = range_plot_marker_size 777 with range_plot.batch_update(): 778 trace.marker.color = c 779 trace.marker.size = s 780 pass 781 step4instacc = Accordion(children =[step4instr]) 782 step4instacc.set_title(0,'Instructions (optional step)') 783 step4instacc.selected_index = None 784 step4 = VBox(children=[step4instacc,extend_fit,range_plot]) 785 786 # 6. Final Check* 787 step6instr = richLabel(value = 'Things to check before running the fit:' \ 788 '<ul><li>Fix any problems listed in ' \ 789 '"Notices".</li>' \ 790 '<li>Check for any unpaired parentheses, ' \ 791 'brackets or braces.</li>' \ 792 '<li>Check that all single and double ' \ 793 'quotes are paired.</li>' \ 794 '<li>If you did any manual editing ' \ 795 'double-check for typos.</li>') 796 step6noticebox = richLabel(value = makeplot_notices.notice_html()) 797 798 def dofit_click(change): 799 if JPSLUtils.notebookenv == 'NBClassic': 800 # Commented out do nothing because of timing issues 801 #text = '\n# Force save widget states so that graph will still be\n' 802 #text += '# available when notebook next opened in trusted state.\n' 803 #text += 'import time\ntime.sleep(5)' 804 select_containing_cell('pandasfitGUI') 805 select_cell_immediately_below() 806 #insert_newline_at_end_of_current_cell(text) 807 #jscode = 'Jupyter.actions.call("widgets:save-with-widgets");' 808 #text = 'JPSLUtils.OTJS(\''+jscode+'\')' 809 #insert_newline_at_end_of_current_cell(text) 810 # run the cell to build the plot 811 JPSLUtils.OTJS('Jupyter.notebook.get_selected_cell().execute()') 812 # remove the GUI cell 813 select_containing_cell('pandasfitGUI') 814 delete_selected_cell() 815 pass 816 817 dofitbut_lay = Layout(visibility = "hidden") 818 if JPSLUtils.notebookenv == 'NBClassic': 819 dofitbut_lay = Layout(visibility="visible") 820 dofitbut = Button(description = 'Do Fit', 821 disabled = True, 822 layout = dofitbut_lay) 823 dofitbut.on_click(dofit_click) 824 step6vbox = VBox(children=[dofitbut,step6noticebox]) 825 step6 = HBox(children=[step6instr,step6vbox]) 826 827 828 steps = Tab(children=[step1, step2, step3, step4, step5, step6]) 829 steps.set_title(0,'1. Pick Data*') 830 steps.set_title(1,'2. Data Uncertainty*') 831 steps.set_title(2,'3. Set up Model*') 832 steps.set_title(3,'4. Pick Fit Range(s)') 833 steps.set_title(4, '5. Axes & Format*') 834 steps.set_title(5, '6. Final Check*') 835 836 def tab_changed(change): 837 nonlocal importstr, step1str, step2str, step3str, step4str, step5str, \ 838 range_chosen 839 dfname = friendly_to_globalname[whichframe.value] 840 if change['old'] == 0: 841 # Update step 1 string 842 step1str = '# Define data and trace name\n' 843 step1str += 'Xvals = '+dfname+'[\"' 844 step1str += str(Xcoord.value)+'\"]\n' 845 step1str += 'Yvals = ' + dfname +'[\"' 846 step1str += str(Ycoord.value)+'\"]\n' 847 step1str += 'tracename = \"'+str(trace_name.value)+'\"\n\n' 848 pass 849 if change['old'] == 1: 850 # TODO need do something in case tab is changed before a click 851 # occurs outside a box that was just change. blur things will 852 # require ipywidgets v8+ 853 # update step 2 string 854 step2str = '# Define error (uncertainty)\n' 855 if yerrtype.value == 'none': 856 step2str += 'Yerr = ' + dfname + '[\"' 857 step2str += str(Ycoord.value) + '\"]*0.0 + 1.0\n\n' 858 if yerrtype.value=='constant': 859 step2str += 'Yerr = ' + dfname +'[\"' 860 step2str += str(Ycoord.value)+'\"]*0.0 + ' + str( 861 yerrvalue.value) + '\n\n' 862 if yerrtype.value == 'percent': 863 step2str += 'Yerr = np.fabs('+ dfname +'[\"' 864 step2str += str(Ycoord.value)+'\"])*0.01*' + str( 865 yerrvalue.value) + '\n\n' 866 if yerrtype.value == 'data': 867 step2str += 'Yerr = ' + dfname +'[\"' 868 step2str += str(yerrdata.value)+'\"]\n\n' 869 pass 870 if change['old']== 2: 871 # update step 3 string 872 step3str = '# Define the fit model, initial guesses, ' \ 873 'and constraints\n' 874 step3str += 'fitmod = lmfit.models.'+str(modeldrop.value)+'()\n' 875 currmodel = getattr(models, str(modeldrop.value))() 876 for k in params_set.children: 877 param_name = str(k.children[0].value.split(':')[0]) 878 if param_name in currmodel.param_names: 879 step3str += 'fitmod.set_param_hint(\"'+param_name+'\",' 880 step3str += ' vary = '+str(not(k.children[1].children[ 881 0].value)) 882 temp_val = k.children[1].children[1].value 883 def tst_temp_val(temp_val): 884 if (temp_val != np.nan) and (temp_val != np.inf) and\ 885 (temp_val != -np.inf) and (temp_val != \ 886 -sys.float_info.max) and (temp_val != \ 887 sys.float_info.max): 888 return True 889 else: 890 return False 891 if tst_temp_val(temp_val): 892 step3str += ', value = ' + str(temp_val) 893 temp_val = k.children[1].children[2].value 894 if tst_temp_val(temp_val): 895 step3str += ', min = ' + str(temp_val) 896 temp_val = k.children[1].children[3].value 897 if tst_temp_val(temp_val): 898 step3str += ', max = ' + str(temp_val) 899 step3str += ')\n' 900 step3str +='\n' 901 pass 902 if change['new']>=4: 903 ranges = [] 904 if JPSLUtils.notebookenv != 'colab': 905 # update ranges 906 range_start = True 907 new_range = [] 908 if len(range_plot.data)>0: 909 for i in range(len(range_plot.data[0].marker.color)): 910 if range_plot.data[0].marker.color[i] == range_plot_hilight: 911 new_range.append(i) 912 if not range_start: 913 ranges.append(new_range) 914 new_range = [] 915 range_start = not range_start 916 # update step 4 string 917 covscalestr = 'False' 918 if yerrtype.value == 'none': 919 covscalestr = 'True' 920 if len(ranges) > 0: 921 range_chosen = True 922 step4str = '# Define fit ranges\n' 923 step4str += 'Yfiterr = copy.deepcopy(Yerr) # ranges not to ' \ 924 'fit = np.inf\n' 925 step4str += 'Xfitdata = copy.deepcopy(Xvals) # ranges where ' \ 926 'fit not displayed = np.nan\n' 927 for i in range(len(ranges)): 928 if i == 0 and ranges[0][0]>0: 929 step4str += 'Yfiterr[int(0):int('+str(ranges[0][0])+ \ 930 ')] = np.inf\n' 931 step4str += 'Xfitdata[int(0):int('+str(ranges[0][0])+\ 932 ')] = np.nan\n' 933 if (i + 1) < len(ranges): 934 step4str += 'Yfiterr[int('+str(ranges[i][1]+1)+\ 935 '):int('+str(ranges[i+1][0])+')] = np.inf\n' 936 step4str += 'Xfitdata[int('+str(ranges[i][1]+1)+ \ 937 '):int('+str(ranges[i+1][0])+')] = np.nan\n' 938 if i+1 == len(ranges): 939 step4str += 'Yfiterr[int('+str(ranges[i][1]+1)+\ 940 '):int('+str(len(range_plot.data[0].marker. 941 color))+')] = np.inf\n' 942 step4str += 'Xfitdata[int('+str(ranges[i][1]+1)+\ 943 '):int('+str(len(range_plot.data[0].marker. 944 color))+')] = np.nan\n' 945 step4str += '\n' 946 step4str += '# Do fit\n' 947 step4str += str(fitname)+' = fitmod.fit(Yvals, x=Xvals, ' \ 948 'weights = 1/Yfiterr, scale_covar = '+covscalestr+', ' \ 949 'nan_policy = \"omit\")\n\n' 950 else: 951 range_chosen = False 952 step4str = '# Do fit\n' 953 step4str += str(fitname)+' = fitmod.fit(Yvals, x=Xvals, ' \ 954 'weights = 1/Yerr, scale_covar = '+covscalestr+', ' \ 955 'nan_policy = \"omit\")\n\n' 956 step4str += '# Calculate residuals (data - fit) because lmfit\n' 957 step4str += '# does not calculate for all points under all ' \ 958 'conditions\n' 959 step4str += 'resid = []\n' 960 step4str += ('# explicit int(0) below avoids collisions with some ' 961 'preparsers.\n') 962 step4str += 'for i in range(int(0),len('+str(fitname)+'.data)):\n' 963 step4str += ' resid.append('+str(fitname)+'.data[' \ 964 'i]-'+str(fitname)+'.best_fit[i])\n\n' 965 pass 966 if change['old'] == 4: 967 # update step 5 string 968 step5str = '' 969 if range_chosen: 970 xstr = 'Xfitdata' 971 if not(extend_fit.value): 972 step5str += '# Delete residuals in ranges not fit\n' 973 step5str += '# and fit values that are not ' \ 974 'displayed.\n' 975 step5str += 'for i in range(len(resid)):\n' 976 step5str += ' if np.isnan(Xfitdata[i]):\n' 977 step5str += ' resid[i] = None\n' 978 step5str += ' '+str(fitname)+'.best_fit[i] = ' \ 979 'None\n\n' 980 else: 981 xstr = 'Xvals' 982 errbarstr = '' 983 if yerrtype.value!='none': 984 errbarstr = ', error_y_type=\"data\", error_y_array=Yerr' 985 xresidstr = xstr 986 if extend_fit.value: 987 xresidstr = 'Xvals' 988 mirrorstr = '' 989 if mirror_axes.value: 990 mirrorstr = ', mirror = True' 991 if mirror_ticks.value: 992 mirrorstr = ', mirror = \"ticks\"' 993 # the plot 994 step5str += '# Plot Results\n' 995 step5str += ('# explicit int(..) below avoids collisions with ' 996 'some preparsers.\n') 997 step5str += str(figname) + ' = go.FigureWidget(' \ 998 'layout_template=\"'+str( 999 plot_template.value)+'\")\n' 1000 step5str += str(figname)+ '.update_layout(title = \"'+ \ 1001 str(plot_title.value)+'\",' 1002 text = '' 1003 if plot_aspect.value == 'auto': 1004 text += 'autosize=True)\n' 1005 else: 1006 if plot_size.value == 'tiny': 1007 plot_width = 300 1008 elif plot_size.value == 'small': 1009 plot_width = 450 1010 elif plot_size.value == 'medium': 1011 plot_width = 800 1012 elif plot_size.value == 'large': 1013 plot_width = 1200 1014 elif plot_size.value == 'huge': 1015 plot_width = 2400 1016 if plot_aspect.value == '16:9': 1017 plot_height = int(9 * plot_width / 16) 1018 elif plot_aspect.value == '5:3': 1019 plot_height = int(3 * plot_width / 5) 1020 elif plot_aspect.value == '7:5': 1021 plot_height = int(5 * plot_width / 7) 1022 elif plot_aspect.value == '4:3': 1023 plot_height = int(3 * plot_width / 4) 1024 elif plot_aspect.value == '10:8': 1025 plot_height = int(8 * plot_width / 10) 1026 elif plot_aspect.value == '1:1': 1027 plot_height = plot_width 1028 text += 'autosize=False, width = int(' 1029 text += str(plot_width) + '), height=int(' 1030 text += str(plot_height) + '))\n' 1031 step5str += text 1032 step5str += str(figname) + '.set_subplots(rows=int(2), cols=int(1), ' \ 1033 'row_heights=[0.2,0.8], ' \ 1034 'shared_xaxes=True)\n' 1035 step5str += 'scat = go.Scatter(y=resid,x='+xresidstr+', ' \ 1036 'mode=\"markers\",' \ 1037 'name = \"residuals\"'+errbarstr+')\n' 1038 step5str += str(figname) + '.update_yaxes(title = ' \ 1039 '\"Residuals\", ' \ 1040 'row=int(1), col=int(1), zeroline=True, zerolinecolor = ' \ 1041 '\"lightgrey\"'+str(mirrorstr)+')\n' 1042 if mirror_axes.value: 1043 step5str += str(figname) + '.update_xaxes(' \ 1044 'row=int(1), col=int(1)'+str(mirrorstr)+')\n' 1045 step5str += str(figname) + '.add_trace(scat,col=int(1),row=int(1))\n' 1046 step5str += 'scat = go.Scatter(x=Xvals, y=Yvals, ' \ 1047 'mode=\"markers\", name=tracename'+errbarstr+')\n' 1048 step5str += str(figname) + '.add_trace(scat, col=int(1), ' \ 1049 'row=int(2))\n' 1050 step5str += str(figname) + '.update_yaxes(title = ' \ 1051 '\"'+Y_label.value+'\", ' \ 1052 'row=int(2), col=int(1)'+str(mirrorstr)+')\n' 1053 step5str += str(figname) + '.update_xaxes(title = ' \ 1054 '\"'+X_label.value+'\", ' \ 1055 'row=int(2), col=int(1)'+str(mirrorstr)+')\n' 1056 if extend_fit.value: 1057 step5str += 'scat = go.Scatter(y='+str( 1058 fitname)+'.best_fit, x=Xvals, mode=\"lines\", '\ 1059 'line_color = \"black\", ' \ 1060 'name=\"extrapolated\",' \ 1061 'line_dash=\"dash\")\n' 1062 step5str += str(figname) + '.add_trace(scat, col=int(1), ' \ 1063 'row=int(2))\n' 1064 step5str += 'scat = go.Scatter(y='+str(fitname)+'.best_fit,' \ 1065 'x='+xstr+', mode=\"lines\", ' \ 1066 'name=\"fit\", line_color = ' \ 1067 '\"black\", line_dash=\"solid\")\n' 1068 step5str += str(figname) + '.add_trace(scat,col=int(1),row=int(2))\n' 1069 step5str += str(figname) + '.show(config = ' \ 1070 '{\'toImageButtonOptions\': {' \ 1071 '\'format\': \'svg\'}})\n\n' 1072 pass 1073 if change['new'] == 3 and JPSLUtils.notebookenv != 'colab': 1074 df = friendly_to_object[whichframe.value] 1075 rangex = df[Xcoord.value] 1076 rangey = df[Ycoord.value] 1077 c =[] 1078 s = [] 1079 if len(range_plot.data)> 0 and len(range_plot.data[ 1080 0].marker.color) == len(range_plot.data[0]['x']): 1081 c = list(range_plot.data[0].marker.color) 1082 s = list(range_plot.data[0].marker.size) 1083 range_plot.data=[] 1084 range_plot.add_scatter(x=rangex,y=rangey, mode = 'markers', 1085 line_color = range_plot_line_color, 1086 marker_size = range_plot_marker_size) 1087 if len(range_plot.data[0]['x']) == len(c): 1088 with range_plot.batch_update(): 1089 range_plot.data[0].marker.color = c 1090 range_plot.data[0].marker.size = s 1091 range_plot.data[0].on_click(update_range_point) 1092 if change['new'] ==5: 1093 if X_label.value == '' or Y_label.value == '': 1094 makeplot_notices.activate_notice(2) 1095 dofitbut.disabled = True 1096 dofitbut.button_style = '' 1097 else: 1098 makeplot_notices.deactivate_notice(2) 1099 if Xcoord.value == 'Choose X-coordinate.' or \ 1100 Ycoord.value == 'Choose X-coordinate.': 1101 makeplot_notices.activate_notice(0) 1102 dofitbut.disabled = True 1103 dofitbut.button_style = '' 1104 else: 1105 makeplot_notices.deactivate_notice(0) 1106 if modeldrop.value == '': 1107 makeplot_notices.activate_notice(1) 1108 dofitbut.disabled = True 1109 dofitbut.button_style = '' 1110 else: 1111 makeplot_notices.deactivate_notice(1) 1112 step6noticebox.value = makeplot_notices.notice_html() 1113 if len(makeplot_notices.get_active()) == 0: 1114 dofitbut.disabled = False 1115 dofitbut.button_style = 'success' 1116 # the best fit equation 1117 step6str = '# Display best fit equation\n' 1118 step6str += fitresultstrs[modeldrop.value](fitname) 1119 if JPSLUtils.notebookenv == 'NBClassic': 1120 JPSLUtils.select_containing_cell('pandasfitGUI') 1121 JPSLUtils.replace_text_of_next_cell(importstr + step1str + \ 1122 step2str + step3str + \ 1123 step4str + step5str + step6str) 1124 else: 1125 codearea.sniptext.value = importstr + step1str + step2str + \ 1126 step3str + step4str + step5str + \ 1127 pseudoLatexToLatex(step6str) 1128 pass 1129 1130 steps.observe(tab_changed, names = 'selected_index') 1131 with output: 1132 display(steps) 1133 if JPSLUtils.notebookenv == 'NBClassic': 1134 display(output) 1135 select_containing_cell('pandasfitGUI') 1136 new_cell_immediately_below() 1137 else: 1138 codearea = build_run_snip_widget('', output) 1139 with output: 1140 display(codearea) 1141 display(output) 1142 pass
If passed no parameters this will look for all the dataframes in the user namespace and make them available for plotting. Once a dataframe is chosen only the numerical columns from that dataframe will be available for inclusion in the plotting expression.
This GUI produces code to use the lmfit package to fit data and the plotly interactive plotting package to display the results.
If you wish to allow only certain dataframes or have them show up as user friendly names in the menus provide that information in the first paramater df_info.
To allow inclusion of text columns pass True for show_text_col.
Parameters
bool show_text_col: (default = False). When True columns containing text will be shown.
list df_info: List of Lists [[object,globalname, userfriendly]],..]
- object -- pandas.DataFrame
- globalname -- string name of the object in the user global name space.
- userfriendly -- string name to display for user selection.
:keyword string figname: string used to override default python name for figure.
:keyword string fitname: string used to override default python name for fit.
:keyword bool findframes: default = True. If set to false and dataframes are passed in dfs_info, will not search for dataframes in the user namespace.