Package qubx :: Module data_fit
[hide private]
[frames] | no frames]

Source Code for Module qubx.data_fit

  1  """Curve fitting tool acting on data window. 
  2   
  3  Copyright 2008-2014 Research Foundation State University of New York  
  4  This file is part of QUB Express.                                           
  5   
  6  QUB Express is free software; you can redistribute it and/or modify           
  7  it under the terms of the GNU General Public License as published by  
  8  the Free Software Foundation, either version 3 of the License, or     
  9  (at your option) any later version.                                   
 10   
 11  QUB Express is distributed in the hope that it will be useful,                
 12  but WITHOUT ANY WARRANTY; without even the implied warranty of        
 13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         
 14  GNU General Public License for more details.                          
 15   
 16  You should have received a copy of the GNU General Public License,    
 17  named LICENSE.txt, in the QUB Express program directory.  If not, see         
 18  <http://www.gnu.org/licenses/>.                                       
 19   
 20  """ 
 21   
 22  from __future__ import with_statement 
 23   
 24  import os 
 25  import sys 
 26  import traceback 
 27  import numpy 
 28  import gobject 
 29  import cairo 
 30  import gtk 
 31  import qubx.fast.data 
 32  import qubx.GTK 
 33  import qubx.pyenv 
 34  import qubx.fast.data 
 35  import qubx.fit_space 
 36  import qubx.faces 
 37  import qubx.notebook 
 38  import qubx.notebookGTK 
 39  import qubx.settings 
 40  import qubx.task 
 41  import qubx.dataGTK 
 42   
 43  from gtk import gdk, keysyms 
 44  from itertools import izip, count, chain 
 45  from qubx.util_types import * 
 46  from qubx.GTK import build_menuitem 
 47  from qubx.accept import acceptFloatGreaterThan 
 48  from qubx.toolspace import ColorInfo 
 49  from qubx.tree import node_data_or_set_def 
 50   
 51  MAX_FIT_POINTS = 1000000 
 52   
 53  COLOR_DATAFIT_NUMBER = ('data_fit.number', (1, 1, 1, 1)) 
 54  ColorInfo[COLOR_DATAFIT_NUMBER[0]].label = 'Data curvefit numbers' 
 55  COLOR_DATAFIT_BUTTON = ('data_fit.button', (.9, .35, .35, 1)) 
 56  ColorInfo[COLOR_DATAFIT_BUTTON[0]].label = 'Data curvefit buttons' 
 57   
58 -class DataFitControls(qubx.fit_space.FitControls):
59 """L{qubx.toolspace.LayerSet} to add least-squares curve fitting to the L{qubx.dataGTK.QubDataView_Hi}. 60 61 """
62 - def __init__(self, label='CurveFit', global_name=""):
63 qubx.fit_space.FitControls.__init__(self, label=label, cLayer=qubx.toolspace.LAYER_BG, cText=qubx.toolspace.LAYER_FG, 64 cNumber=COLOR_DATAFIT_NUMBER, cButton=COLOR_DATAFIT_BUTTON, global_name=global_name) 65 self.__ref = Reffer() 66 cat = qubx.settings.SettingsMgr[label] 67 self.profile = cat.active 68 cat.OnSet = self.__ref(self.__onUpdateProfile) 69 self.__relX = node_data_or_set_def(self.profile, 'RelX', 1) 70 self.__resample = bool(node_data_or_set_def(self.profile, 'Resample', 0)) 71 self.__resampleStd = node_data_or_set_def(self.profile, 'ResampleStd', 0.1) 72 self.__nb_next_stats = False 73 self.__source_segs = self.__source_bounds = self.__source_view = None 74 self.__list = self.__list_ix = None 75 self.__list_priority = False 76 self.__resume_list_fit = False 77 self.QubX = qubx.pyenv.env.globals['QubX'] 78 self.QubX.OnQuit += self.__ref(lambda: self.dispose()) 79 self.datasrc = FitDataSource(self.QubX) 80 self.datasrc.use_list = False # bool(node_data_or_set_def(self.profile, 'use_segments', self.datasrc.use_list)) 81 self.datasrc.use_together = bool(node_data_or_set_def(self.profile, 'use_together', self.datasrc.use_together)) 82 self.datasrc.OnChangeFile += self.__ref(self.__onChangeDataFile) 83 self.datasrc.OnChange += self.__ref(self.__onChangeData) 84 self.datasrc.OnListSelect += self.__ref(self.__onListSelect) 85 self.robot.OnExpr += self.__ref(self.__onExpr) 86 self.robot.OnParam += self.__ref(self.__onParam) 87 self.robot.OnStats += self.__ref(self.__onStats) 88 self.robot.OnException += self.__ref(self.__onRobotException) 89 self.robot.OnInterrupt += self.__ref(self.__onRobotInterrupt) 90 self.robot.OnIteration += self.__ref(self.__onIteration) 91 self.robot.OnStartFit += self.__ref(self.__onStartFit) 92 self.robot.OnEndFit += self.__ref(self.__onEndFit) 93 self.OnClickFit += self.__ref(self.__onClickFit) 94 self.layNotebook = qubx.toolspace.Layer(x=28, y=1+qubx.fit_space.LINE_EMS, w=2, h=2, cBG=qubx.toolspace.COLOR_CLEAR) 95 self.subNotebook = qubx.notebookGTK.SubLayer_Notebook(x=0, y=0, w=2, h=2) 96 self.layNotebook.add_sublayer(self.subNotebook) 97 self.layers.append(self.layNotebook) 98 self.nbParams = qubx.notebook.NbDynText('Parameters', 'QubX.Data.controls_fit.nbParams', self.nb_get_param_text) 99 self.nbParams.std_err_est = None 100 self.subNotebook.items.append(self.nbParams) 101 self.nbPicture = qubx.notebook.NbItems('Data image', 'QubX.Data.controls_fit.nbPicture') 102 self.subNotebook.items.append(self.nbPicture) 103 self.nbPictureNO = qubx.notebook.NbItems('Data image, no overlays', 'QubX.Data.controls_fit.nbPictureNO') 104 self.subNotebook.items.append(self.nbPictureNO) 105 self.nbChart = qubx.notebook.NbItems('Data table', 'QubX.Data.controls_fit.nbChart') 106 self.subNotebook.items.append(self.nbChart) 107 qubx.notebook.Notebook.register_auto('DataFit.Parameters', 'Data Fit Parameters, on Fit', True) 108 qubx.notebook.Notebook.register_auto('DataFit.Results', 'Data Fit results, on Fit', True) 109 qubx.notebook.Notebook.register_auto('DataFit.Chart', 'Data chart, on Fit', False) 110 qubx.notebook.Notebook.register_auto('DataFit.Picture', 'Data image, on Fit', True) 111 qubx.notebook.Notebook.register_auto('DataFit.PictureNO', 'Data image, no overlays, on Fit', False) 112 113 self.layBack = qubx.toolspace.Layer(x=1, y=-4*qubx.fit_space.LINE_EMS, 114 w=4+qubx.fit_space.LINE_EMS, h=qubx.fit_space.LINE_EMS, 115 cBG=qubx.toolspace.COLOR_CLEAR) 116 self.subBack = qubx.toolspace.SubLayer_Arrow(color=self.cButton, angle=pi, 117 x=0, y=0, w=qubx.fit_space.LINE_EMS, h=qubx.fit_space.LINE_EMS, 118 action=self.__ref(self.__onClickBack)) 119 self.subBack.tooltip = 'Return to navigation controls' 120 self.layBack.add_sublayer(self.subBack) 121 self.layBack.add_sublayer(qubx.toolspace.SubLayer_Label('Back', 122 x=qubx.fit_space.LINE_EMS, y=0, w=4, h=qubx.fit_space.LINE_EMS, 123 action=self.__ref(self.__onClickBack))) 124 self.layers.append(self.layBack) 125 126 self.layData = qubx.toolspace.Layer(x=0, y=-2*qubx.fit_space.LINE_EMS-.5, 127 w=-.1, h=qubx.fit_space.LINE_EMS+.5, 128 cBG=self.cLayer) 129 self.layers.append(self.layData) 130 self.layData.add_sublayer(qubx.toolspace.SubLayer_Label('Data:', x=0, y=0.25, w=7, h=qubx.fit_space.LINE_EMS)) 131 self.subUseOnscreen = qubx.toolspace.SubLayer_Check(x=8, y=0.25, w=10, h=qubx.fit_space.LINE_EMS, 132 color=self.cButton, caption='onscreen') 133 self.subUseOnscreen.tooltip = "Fit onscreen portion of current segment" 134 self.subUseOnscreen.set_active(not self.datasrc.use_list) 135 self.subUseOnscreen.OnToggle += self.__ref(self.__onToggleUseOnscreen) 136 self.layData.add_sublayer(self.subUseOnscreen) 137 self.subUseList = qubx.toolspace.SubLayer_Check(x=18, y=0.25, w=10, h=qubx.fit_space.LINE_EMS, 138 color=self.cButton, caption='List') 139 self.subUseList.tooltip = "Fit all selections in List" 140 self.subUseList.set_active(self.datasrc.use_list) 141 self.subUseList.OnToggle += self.__ref(self.__onToggleUseList) 142 self.layData.add_sublayer(self.subUseList) 143 self.subUseTogether = qubx.toolspace.SubLayer_Check(x=26, y=0.25, w=10, h=qubx.fit_space.LINE_EMS, 144 color=self.cButton, caption='together') 145 self.subUseTogether.tooltip = "Fit all selections at once, or else one-by-one" 146 self.subUseTogether.set_active(self.datasrc.use_together) 147 self.subUseTogether.OnToggle += self.__ref(self.__onToggleUseTogether) 148 if self.datasrc.use_list: 149 self.layData.add_sublayer(self.subUseTogether) 150 151 # minimal data navigation: 152 self.subBackSeg = qubx.toolspace.SubLayer_Label('-', x=-24, w=1, h=qubx.fit_space.LINE_EMS, 153 action=self.__ref(self.__onClickBackSeg)) 154 self.subBackSeg.tooltip = 'Back one segment' 155 self.layData.add_sublayer(self.subBackSeg) 156 self.subBackScreen = qubx.toolspace.SubLayer_Label('<', x=-22.5, w=1, h=qubx.fit_space.LINE_EMS, 157 action=self.__ref(self.__onClickBackScreen)) 158 self.subBackScreen.tooltip = 'Back one screenful' 159 self.layData.add_sublayer(self.subBackScreen) 160 self.subFwdScreen = qubx.toolspace.SubLayer_Label('>', x=-19.5, w=1, h=qubx.fit_space.LINE_EMS, 161 action=self.__ref(self.__onClickFwdScreen)) 162 self.subFwdScreen.tooltip = 'Forward one screenful' 163 self.layData.add_sublayer(self.subFwdScreen) 164 self.subFwdSeg = qubx.toolspace.SubLayer_Label('+', x=-18, w=1, h=qubx.fit_space.LINE_EMS, 165 action=self.__ref(self.__onClickFwdSeg)) 166 self.subFwdSeg.tooltip = 'Forward one segment' 167 self.layData.add_sublayer(self.subFwdSeg) 168 self.subGrab = qubx.toolspace.SubLayer_Label('?', x=-16, w=1, h=qubx.fit_space.LINE_EMS, 169 action=self.__ref(self.__onClickGrab)) 170 self.subGrab.tooltip = 'Rescale data to screen' 171 self.layData.add_sublayer(self.subGrab) 172 173 # move the N display to our extra panel: 174 self.layBottom.remove_sublayer(self.subN) 175 self.subN.x = -9 176 self.layData.add_sublayer(self.subN) 177 178 # new stuff for bottom panel: 179 self.subRelX = qubx.toolspace.SubLayer_Check(x=5, y=0, w=23, h=qubx.fit_space.LINE_EMS, 180 color=self.cButton, 181 caption='x coordinate starts at 0') 182 self.subRelX.set_active(self.__relX) 183 self.subRelX.OnToggle += self.__ref(self.__onToggleRelX) 184 self.layBottom.add_sublayer(self.subRelX) 185 186 self.subResample = qubx.toolspace.SubLayer_Check(x=28, y=0, w=17, h=qubx.fit_space.LINE_EMS, 187 color=self.cButton, caption='Resample by delta y=') 188 self.subResample.set_active(self.__resample) 189 self.subResample.OnToggle += self.__ref(self.__onToggleResample) 190 self.layBottom.add_sublayer(self.subResample) 191 self.subDelta = qubx.toolspace.SubLayer_Label('%.3g'%self.__resampleStd, -1, 0, x=46, y=0, 192 w=8, h=qubx.fit_space.LINE_EMS, color=self.cText, 193 action=self.__ref(self.__onClickDelta)) 194 self.layBottom.add_sublayer(self.subDelta) 195 196 self.subRestoreFit = qubx.toolspace.SubLayer_Label('Restore last fit', 0, 0, x=-33, y=0, w=14, h=qubx.fit_space.LINE_EMS, color=self.cButton, 197 action=self.__ref(self.__onClickRestoreFit)) 198 self.__showing_restore = False 199 200 gobject.idle_add(self.__onChangeDataFile) 201 gobject.idle_add(self.__onChangeData) 202 self.__serial_data = 0 203 self.__serial_fit = 0 204 self.__delayed_curve = False 205 self.__xx = numpy.array([]) 206 self.__ff = numpy.array([]) 207 self.__ll = numpy.array([]) 208 self.__vvv = []
209 - def set_relX(self, x):
210 if x != self.__relX: 211 self.__relX = x 212 self.profile['RelX'].data = x 213 self.subRelX.set_active(x) 214 self.__onChangeData()
215 relX = property(lambda self: self.__relX, lambda self, x: self.set_relX(x))
216 - def set_resample(self, x):
217 if x != self.__resample: 218 self.__resample = x 219 self.profile['Resample'].data = x 220 self.subResample.set_active(x) 221 self.__onChangeData()
222 resample = property(lambda self: self.__resample, lambda self, x: self.set_resample(x))
223 - def set_resampleStd(self, x):
224 if x != self.__resampleStd: 225 self.__resampleStd = x 226 self.profile['ResampleStd'].data = x 227 self.subDelta.label = '%.3g' % x 228 self.__onChangeData()
229 resampleStd = property(lambda self: self.__resampleStd, lambda self, x: self.set_resampleStd(x))
230 - def set_use_list(self, x):
231 if x != self.datasrc.use_list: 232 self.datasrc.use_list = x 233 self.profile['use_segments'].data = x 234 self.subUseOnscreen.active = not x 235 self.subUseList.active = x 236 self.__show_hide_segcontrols(x)
237 use_list = property(lambda self: self.datasrc.use_list, lambda self, x: self.set_use_list(x))
238 - def __onToggleUseOnscreen(self, *args):
239 if self.global_name: 240 qubx.pyenv.env.OnScriptable('%s.use_list = %s' % (self.global_name, repr(not self.subUseOnscreen.active))) 241 self.use_list = not self.subUseOnscreen.active
242 - def __onToggleUseList(self, *args):
243 if self.global_name: 244 qubx.pyenv.env.OnScriptable('%s.use_list = %s' % (self.global_name, repr(self.subUseList.active))) 245 self.use_list = self.subUseList.active
246 - def __show_hide_segcontrols(self, use_segments):
247 if use_segments: 248 self.layData.add_sublayer(self.subUseTogether) 249 else: 250 self.layData.remove_sublayer(self.subUseTogether)
251 - def set_use_together(self, x):
252 if x != self.datasrc.use_together: 253 self.datasrc.use_together = x 254 self.profile['use_together'].data = x 255 self.subUseTogether.active = x
256 use_together = property(lambda self: self.datasrc.use_together, lambda self, x: self.set_use_together(x))
257 - def __onToggleUseTogether(self, *args):
258 if self.global_name: 259 qubx.pyenv.env.OnScriptable('%s.use_together = %s' % (self.global_name, repr(self.subUseTogether.active))) 260 self.use_together = self.subUseTogether.active
261 - def __onToggleRelX(self, *args):
262 if self.global_name: 263 qubx.pyenv.env.OnScriptable('%s.relX = %s' % (self.global_name, repr(self.subRelX.active))) 264 self.relX = self.subRelX.active
265 - def __onToggleResample(self, *args):
266 if self.global_name: 267 qubx.pyenv.env.OnScriptable('%s.resample = %s' % (self.global_name, repr(self.subResample.active))) 268 self.resample = self.subResample.active
269 - def __onClickBack(self, x, y, e):
270 self.__stopped = True # prevent "segments and not together" iteration 271 qubx.pyenv.env.OnScriptable('QubX.Data.view.request_fit_controls(False)') 272 gobject.idle_add(self.datasrc.data.request_fit_controls, False)
273 - def __onClickDelta(self, x, y, e):
274 dlg = qubx.GTK.NumEntryDialog('%s - Edit resampling delta'%qubx.pyenv.env.globals['QubX'].appname, None, 275 'Skip differences less than:', '%.7g'%self.__resampleStd, acceptFloatGreaterThan(0.0)) 276 if gtk.RESPONSE_ACCEPT == dlg.run(): 277 if self.global_name: 278 qubx.pyenv.env.OnScriptable('%s.resampleStd = %s' % (self.global_name, repr(dlg.value))) 279 self.resampleStd = dlg.value 280 dlg.destroy()
281 - def __onUpdateProfile(self, settings, updates):
282 if updates.find('RelX').data: 283 self.relX = updates['RelX'].data[0] 284 if updates.find('Resample').data: 285 self.resample = updates['Resample'].data[0] 286 if updates.find('ResampleStd').data: 287 self.resampleStd = updates['ResampleStd'].data[0] 288 if updates.find('use_list').data: 289 self.use_list = bool(updates['use_list'].data[0]) 290 if updates.find('use_together').data: 291 self.use_together = bool(updates['use_together'].data[0])
292 - def activate(self, space):
293 qubx.toolspace.LayerSet.activate(self, space) 294 if space: 295 self.__delayed_curve = True # wait until they mess with the curve or press Fit; give them a chance to select appropriate data (avoid accidental multi-million-point calcs) 296 self.__onChangeData()
297 - def __end_delayed_curve(self):
298 if self.__delayed_curve: 299 self.__delayed_curve = False 300 self.__onChangeData()
301 - def __update_restore_fit(self):
302 have_fit = not ((self.__list is None) or (self.__list_ix is None) or (self.__list.fits[self.__list_ix] is None)) 303 if self.__showing_restore and not have_fit: 304 self.layBottom.remove_sublayer(self.subRestoreFit) 305 self.__showing_restore = False 306 elif have_fit and not self.__showing_restore: 307 self.layBottom.add_sublayer(self.subRestoreFit) 308 self.__showing_restore = True
309 - def __onClickRestoreFit(self, x, y, e):
310 self.profileCat.setProperties(self.__list.fits[self.__list_ix])
311 - def __onClickBackSeg(self, x, y, e):
312 qubx.pyenv.env.OnScriptable('QubX.Data.view.time.Iseg -= 1') 313 self.QubX.Data.view.time.Iseg -= 1
314 - def __onClickBackScreen(self, x, y, e):
315 qubx.pyenv.env.OnScriptable('t = QubX.Data.view.time.timeRange; d = min(t.left, t.right-t.left); t.set_range(t.left-d, t.right-d)') 316 time = self.QubX.Data.view.time 317 delta = min(time.sel_left, time.sel_right - time.sel_left) 318 if delta: 319 time.set_sel(time.sel_left - delta, time.sel_right - delta)
320 - def __onClickFwdScreen(self, x, y, e):
321 qubx.pyenv.env.OnScriptable('t = QubX.Data.view.time.timeRange; d = min(t.bounds[1]-t.right, t.right-t.left); t.set_range(t.left+d, t.right+d)') 322 time = self.QubX.Data.view.time 323 delta = min(time.timeRange.bounds[1]-time.sel_right, time.sel_right - time.sel_left) 324 if delta: 325 time.set_sel(time.sel_left + delta, time.sel_right + delta)
326 - def __onClickFwdSeg(self, x, y, e):
327 qubx.pyenv.env.OnScriptable('QubX.Data.view.time.Iseg += 1') 328 self.QubX.Data.view.time.Iseg += 1
329 - def __onClickGrab(self, x, y, e):
330 self.QubX.Data.view.onClickGrab(self.QubX.Data.view.sig_layers[self.QubX.DataSource.signal+1])
331 - def __onRobotException(self, robot, typ, val, trace):
332 traceback.print_exception(typ, val, trace)
333 - def __onRobotInterrupt(self, robot, cancel):
334 self.__stopped = True # prevent "segments and not together" iteration
335 - def __onExpr(self, curve_name, expr, params, param_vals, lo, hi, can_fit):
336 self.__param_names = params 337 self.__param_vals = param_vals 338 self.__param_active = can_fit 339 self.__end_delayed_curve() 340 self.update_fit()
341 - def __onParam(self, index, name, value, lo, hi, can_fit):
342 self.__param_names[index] = name # paranoid? 343 self.__param_vals[index] = value 344 self.__param_active[index] = can_fit 345 self.__end_delayed_curve() 346 self.update_fit()
347 - def __onIteration(self, param_vals, iteration):
349 - def update_fit(self, param_vals=None):
350 self.__serial_fit += 1 351 if not (param_vals is None): 352 self.__param_vals = param_vals 353 gobject.idle_add(self.__update_fit, self.__param_vals, self.__serial_fit)
354 - def __update_fit(self, param_vals, serial):
355 if (not self.__source_segs) or (not self.QubX.Data.file) or (serial < self.__serial_fit): 356 return 357 for seg, bounds in izip(self.__source_segs, self.__source_bounds): 358 first, last = bounds 359 if isinstance(self.curve, qubx.fit.Curve) and self.curve.ode: 360 fit = self.robot.curve.last_fit # can't run 2 ode threads at once (old fortran not reentrant) 361 else: 362 fit = self.curve.eval(param_vals, self.__xx[first:last+1], [vv[first:last+1] for vv in self.__vvv]) 363 if not self.datasrc.use_list: 364 seg.file.fits[seg.signal].idl.clear(seg.index) 365 seg.file.fits[seg.signal].idl.set_fit(self.__ff[first:last+1], self.__ll[first:last+1], fit) 366 self.__source_segs[0].file.OnChangeFits(self.__source_segs[0].file, self.__source_segs[0].signal)
367 - def __onClickFit(self):
368 self.__end_delayed_curve() 369 if self.datasrc.use_list: 370 self.QubX.Data.file.fits[self.QubX.DataSource.signal].idl.clear() 371 if self.datasrc.use_list and not self.datasrc.use_together: 372 if not self.QubX.Data.file.list: return 373 self.__datafile = self.QubX.Data.file 374 self.__list = self.__datafile.list 375 self.__zoom_to_list(0) 376 self.robot.fit(True) # grab initial values on first or only segment
377 - def fit(self, wait=True, receiver=None):
378 self.__list_receiver = None 379 if wait and self.datasrc.use_list and (not self.datasrc.use_together) and self.QubX.Data.file.list.size: 380 qubx.pyenv.call_async_wait(self.__onInitFit) 381 else: 382 qubx.fit_space.FitControls.fit(self, wait=wait, receiver=receiver)
383 - def __onStartFit(self):
384 self.__stopped = False
385 - def __onEndFit(self):
386 self.__nb_next_stats = True 387 gobject.idle_add(self.update_fit)
388 - def __onInitFit(self, receiver): # list mode only, so self.fit() waits until all list items are done
389 self.__list_receiver = receiver 390 qubx.fit_space.FitControls.fit(self, wait=False)
391 - def __onListSelect(self, ix, field, sender=None):
392 self.__list_ix = ix 393 self.__list_priority = True # override data bound updates resulting from list selection
394 - def __zoom_to_list(self, ix):
395 self.__list_ix = ix 396 self.__list_priority = True # override data bound updates resulting from list selection 397 self.QubX.Data.view.show_list_item(ix) 398 self.__list.select(ix, sender=self)
399 #file = self.__datafile 400 #sel = self.__list[ix] 401 #segf = file.segmentation.segments[file.segmentation.index_at(sel.From)][0] 402 #self.QubX.Data.view.time.Iseg = segf 403 #self.QubX.Data.view.time.Gseg = 1 404 #self.QubX.Data.view.zoom_in(file.sampling *(sel.From - segf), file.sampling * (sel.To - segf))
405 - def fit_next_segment(self):
406 if self.__list_ix < (self.__list.size - 1): 407 self.__resume_list_fit = True 408 self.__zoom_to_list(self.__list_ix + 1) 409 elif self.__list_receiver: 410 gobject.idle_add(self.__list_receiver) # release waiting caller of fit() 411 self.__list_receiver = None
412 - def __onStats(self, correlation, is_pseudo, std_err_est, ssr, r2, runs_prob):
413 self.update_fit() 414 if self.__nb_next_stats: 415 self.__nb_next_stats = False 416 if not self.__stopped and self.datasrc.use_list and not self.datasrc.use_together: 417 gobject.idle_add(self.fit_next_segment) 418 n = len(self.__xx) 419 segm = ((self.datasrc.use_list and not self.datasrc.use_together) 420 and ("Selection %i\n" % self.__list_ix) 421 or "") 422 resamp = self.__resample and ("(resampled by delta = %.3g"%self.__resampleStd) or "" 423 qubx.notebook.Notebook.send(qubx.notebook.NbText("""CurveFit finished. 424 %(segm)s\tN: %(n)i %(resamp)s 425 \tSSR: %(ssr).6g 426 \tR^2: %(r2).6g 427 \tWald-Wolfowitz runs probability: %(runs_prob).6g 428 \tCorrelation: 429 %(correlation)s 430 """ % locals()), auto_id='DataFit.Results') 431 self.nbParams.std_err_est = std_err_est 432 qubx.notebook.Notebook.send(self.nbParams, auto_id='DataFit.Parameters') 433 qubx.notebook.Notebook.send(self.__source_view.hires.nbChart, auto_id='DataFit.Chart') 434 qubx.notebook.Notebook.send(self.__source_view.hires.nbPicture, auto_id='DataFit.Picture') 435 qubx.notebook.Notebook.send(self.__source_view.hires.nbPictureNO, auto_id='DataFit.PictureNO') 436 self.nbParams.std_err_est = None 437 if not (self.__list_ix is None): # self.datasrc.use_list and not self.datasrc.use_together: 438 # into List 439 tb = self.__list 440 si = self.__list_ix 441 tb.set(si, "fit N", n) 442 tb.set(si, "fit SSR", ssr) 443 tb.set(si, "fit SSR per N", n and (ssr/n) or 0.0) 444 tb.set(si, "fit R2", r2) 445 tb.set(si, "fit runs prob", runs_prob) 446 for i, name in enumerate(self.__param_names): 447 tb.set(si, "fit %s" % name, self.__param_vals[i]) 448 tb.set(si, "fit %s err" % name, std_err_est[i]) 449 # and into list.fits: 450 preset = qubx.tree.Node('Curve') 451 preset.appendClone(self.profile['Name']) 452 preset.appendClone(self.profile['Eqn']) 453 preset.appendClone(self.profile['Weight']) 454 preset.appendClone(self.profile['Params']) 455 # skip strategy; could be annoying 456 self.__list.fits[self.__list_ix] = preset
457 - def __onChangeDataFile(self, *args):
458 if self.QubX.Data.view: 459 self.nbPicture.items[:] = [self.QubX.Data.view.hires.nbPicture] 460 self.nbPictureNO.items[:] = [self.QubX.Data.view.hires.nbPictureNO] 461 self.nbChart.items[:] = [self.QubX.Data.view.hires.nbChart]
462 - def __onChangeData(self, *args):
463 if (not self.QubX.Data.view) or (not self.space): return 464 self.__fit_fails = 0 465 self.__serial_data += 1 466 # in case the robot is working: put off data change until it's ready: 467 if self.__delayed_curve: 468 if len(self.__xx): 469 self.robot.do(self.robot_null_data, self.__serial_data) 470 else: 471 self.robot.do(self.robot_read_data, self.__serial_data)
472 - def robot_read_data(self, serial):
473 if serial < self.__serial_data: return 474 with self.robot.main_hold: 475 self.__source_view = self.QubX.Data.view 476 if not self.__resume_list_fit: 477 self.__list = self.QubX.Data.file.list 478 if (self.__list_priority is True) and not (self.__list_ix is None): 479 f, l = self.__list[self.__list_ix, 'From'], self.__list[self.__list_ix, 'To'] 480 segs = self.__source_segs = self.QubX.Data.view.get_segmentation_indexed(f, l, self.QubX.DataSource.signal) 481 vsegs = [self.QubX.Data.view.get_segmentation_indexed(f, l, signal=i) for i in xrange(segs[0].file.signals.size)] 482 self.__list_priority = 'done' 483 elif self.__list_priority is False: 484 segs = self.__source_segs = self.datasrc.get_segmentation() 485 vsegs = [self.datasrc.get_segmentation(signal=i) for i in xrange(segs[0].file.signals.size)] 486 self.__list_ix = None 487 if len(segs) == 1: # is this a selection from the current List? 488 f, l = segs[0].f, segs[0].l 489 lst = segs[0].file.list 490 sels = lst.selections_in(f, l) 491 for sel in sels: 492 if (abs(sel[0] - f) < 3) and (abs(sel[1] - l) < 3): 493 self.__list = lst 494 self.__list_ix = sel[3] 495 break 496 else: 497 self.__list_priority = False 498 return 499 bounds = self.__source_bounds = [] 500 firsts = [] 501 lasts = [] 502 xxs = [] 503 vvvs = [] 504 total_n = 0 505 bounds_at = 0 506 for i in xrange(segs[0].file.signals.size): 507 vvvs.append([]) 508 for iseg, seg in enumerate(segs): 509 seg_n = 0 510 vseg = [vsegg[iseg] for vsegg in vsegs] 511 for c, chunk in enumerate(seg.chunks): 512 if chunk.included: 513 chunk.n = min(chunk.n, MAX_FIT_POINTS-total_n) 514 if chunk.n <= 0: 515 break 516 chunk.l = chunk.f + chunk.n - 1 517 xx = numpy.arange(chunk.f-seg.f, chunk.l-seg.f+1, dtype='float32') 518 xx *= seg.sampling 519 if not self.__relX: 520 xx += 1e-3 * (seg.start - seg.file.segments[0, 'Start']) 521 vvv = [vseg[i].chunks[c].get_samples().samples for i in xrange(len(vvvs))] 522 if self.__resample: 523 means, stds, ff, ll, closest = qubx.fast.data.adaptive_resample(vvv[seg.signal], self.__resampleStd) 524 xx = numpy.array(xx[closest], dtype='float32', copy=True) 525 vvv = [numpy.array(v[closest], dtype='float32', copy=True) for v in vvv] 526 vvv[seg.signal] = means 527 ff += chunk.f 528 ll += chunk.f 529 else: 530 ff = ll = numpy.arange(chunk.f, chunk.l+1, dtype='int32') 531 firsts.append(ff) 532 lasts.append(ll) 533 xxs.append(xx) 534 for i, v in enumerate(vvv): 535 vvvs[i].append(v) 536 total_n += len(xx) 537 seg_n += len(xx) 538 bounds.append( (bounds_at, bounds_at+seg_n-1) ) 539 bounds_at += seg_n 540 v_names = [segs[0].file.signals.get(i, 'Name') for i in xrange(segs[0].file.signals.size)] 541 self.__ff = numpy.hstack(firsts) 542 self.__ll = numpy.hstack(lasts) 543 self.__xx = numpy.hstack(xxs) 544 self.__vvv = [numpy.hstack(vvv) for vvv in vvvs] 545 self.robot.robot_set_data(self.__xx, self.__vvv[seg.signal], self.__vvv, v_names) 546 if self.__resume_list_fit: 547 self.__resume_list_fit = False 548 gobject.idle_add(self.robot.fit, False) 549 else: 550 gobject.idle_add(self.__update_restore_fit)
551 - def robot_null_data(self, serial):
552 if serial < self.__serial_data: return 553 self.__ff = numpy.array([]) 554 self.__ll = numpy.array([]) 555 self.__xx = numpy.array([]) 556 self.__vvv = [] 557 self.robot.robot_set_data(self.__xx, self.__xx, self.__vvv, [])
558 559
560 -class FitDataSource(object):
561 """Provides sampled and data to the CurveFit specification. 562 563 @ivar use_list: True to fit selections from the List 564 @ivar use_together: False to fit selections one by one 565 @ivar signal: zero-based index of active signal 566 @ivar OnChangeFile: L{WeakEvent}() 567 @ivar OnChange: L{WeakEvent}() on any change including file 568 """
569 - def __init__(self, qubx):
570 self.__ref = Reffer() 571 self.qubx = qubx 572 self.datas = self.qubx.Data 573 self.datas.OnSwitch += self.__ref(self.__onSwitchData) 574 self.__data = None 575 gobject.idle_add(self.__onSwitchData, self.datas, self.datas.file) 576 self.__use_list = self.__use_together = False 577 self.__signal = qubx.DataSource.signal 578 qubx.DataSource.OnChangeSignal += self.__ref(self.__onChangeSignal) 579 self.OnChange = WeakEvent() # () 580 self.OnChangeFile = WeakEvent() 581 self.OnListSelect = WeakEvent()
582 - def set_use_list(self, x):
583 if x == self.__use_list: return 584 self.__use_list = x 585 self.OnChange()
586 use_list = property(lambda self: self.__use_list, lambda self, x: self.set_use_list(x))
587 - def set_use_together(self, x):
588 if x == self.__use_together: return 589 self.__use_together = x 590 if self.__use_list: 591 self.OnChange()
592 use_together = property(lambda self: self.__use_together, lambda self, x: self.set_use_together(x))
593 - def __onSwitchData(self, datas, data):
594 self.data = datas.view
595 - def set_data(self, data):
596 if data == self.__data: return 597 if self.__data: 598 self.__data.time.OnChangeSel -= self.__ref(self.__onChangeSel) 599 self.__data.file.OnChangeSamples -= self.__ref(self.__onChangeSamples) 600 self.__data.file.lists.OnSelect -= self.OnListSelect 601 self.__data = data 602 if self.__data: 603 self.__data.time.OnChangeSel += self.__ref(self.__onChangeSel) 604 self.__data.file.OnChangeSamples += self.__ref(self.__onChangeSamples) 605 self.__data.file.lists.OnSelect += self.OnListSelect 606 self.OnChangeFile() 607 self.OnChange()
608 data = property(lambda self: self.__data, lambda self, x: self.set_data(x))
609 - def __onChangeSignal(self, signal):
610 self.__signal = signal 611 self.OnChange()
612 - def __onChangeSel(self, *args):
613 if self.__use_list and self.__use_together: return 614 self.OnChange()
615 - def __onChangeData(self, *args):
616 self.OnChange()
617 - def __onChangeSamples(self, *args):
618 self.OnChange()
619 - def get_segmentation(self, signal=None):
620 """Returns a list of L{SourceSeg} corresponding to use_* 621 """ 622 data = self.data 623 if not data: 624 return [] 625 sig = signal 626 if sig is None: 627 sig = self.__signal 628 if self.__use_list and self.__use_together: 629 return data.file.get_segmentation_list(signal=sig) 630 else: 631 return data.get_segmentation_hires(signal=sig)
632 - def gen_samples(self, chunks, main_hold=None, signal=None, maxlen=(1<<20), get_excluded=False):
633 """Yields a L{SourceChunk} with fields .samples and .sampling for each in chunks. 634 635 @param chunks: list of L{SourceChunk} 636 @param main_hold: L{qubx.task.GTK_Main_Hold} if in a worker thread, or None in the main thread 637 @param signal: signal index, or None to use the chunk.signal 638 @param maxlen: chunks longer than this will be yielded in pieces 639 @param get_excluded: if True, yields sampled chunks even when included==False (if False, yields orig chunk when excluded). 640 """ 641 return generate_chunk_samples(chunks, main_hold, signal, maxlen, get_excluded)
642