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

Source Code for Module qubx.select_charts

   1  """Charts and stats 
   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  import collections 
  23  import operator 
  24   
  25  import gobject 
  26  import gtk 
  27  from gtk import gdk 
  28   
  29  import qubx.faces 
  30  import qubx.fast.clickmap 
  31  import qubx.fast.kmeans 
  32  import qubx.fit 
  33  import qubx.fit_space 
  34  import qubx.modelGTK 
  35  import qubx.notebook 
  36  import qubx.notebookGTK 
  37  import qubx.pyenv 
  38  import qubx.settings 
  39  import qubx.settingsGTK 
  40  import qubx.table 
  41  import qubx.toolspace 
  42  import qubx.tree 
  43  from qubx.accept import * 
  44  from qubx.GTK import pack_item, pack_space, pack_hsep, pack_vsep, pack_label, pack_button, pack_check, pack_radio, pack_scrolled, build_menuitem 
  45  from qubx.toolspace import ColorInfo 
  46  from qubx.util_types import * 
  47   
  48  from itertools import * 
  49  from numpy import * 
  50  import __builtin__ 
  51   
  52  COLOR_ACTIONS = ('select.actions', (0,1,0,1)) 
  53  ColorInfo[COLOR_ACTIONS[0]].label = 'Charts menu' 
  54  COLOR_EMPTY_BG = ('select.empty.bg', (1,1,1,1)) 
  55  ColorInfo[COLOR_EMPTY_BG[0]].label = 'Charts placeholder background' 
  56  COLOR_EMPTY_TEXT = ('select.empty.text', (.1,0,0,1)) 
  57  ColorInfo[COLOR_EMPTY_TEXT[0]].label = 'Charts placeholder text' 
  58  COLOR_EMPTY_BTN = ('select.empty.btn', (.7,0,.1,1)) 
  59  ColorInfo[COLOR_EMPTY_BTN[0]].label = 'Charts placeholder button' 
  60  COLOR_CRITERIA_POPUP = ('select.criteria.popup', (0, 1, 0, .8)) 
  61  ColorInfo[COLOR_CRITERIA_POPUP[0]].label = 'Charts criteria popup' 
  62   
  63  CENTROID_ALPHA = 0.2 
  64  DRAG_ALPHA = 0.25 
  65  DRAG_BORDER_ALPHA = 0.5 
  66   
  67  MENU_TIMEOUT = 360 # ms 
  68  MENU_STRAYDIUS = 2 # pixels 
  69   
  70  RECENT_COUNT = 5 
  71   
72 -class SelectFace(qubx.faces.Face):
73 __explore_featured = ['presets', 'plots', 'plots_showing', 'x_means_plots', 'x_means', 'table_names', 'table_events', 'tb_by_name', 74 'missing', 'vals', 'mean', 'std', 'median', 'mode', 'v_fields', 'v_names', 'vvv', 'popupActions', 'mnuActions', 75 'palette', 'empty_msg', 'grid', 'actions', 'nbCharts', 'nbPictures', 'nbStats', 76 'prefer_table', 'add_plot', 'fields_in', 'colors_in', 'get_color', 'set_color', 'get_colors', 'set_colors', 77 'select_all', 'select_by_criteria', 'trim', 'cluster_k_means', 'cluster_x_means_k', 'cluster_x_means', 78 'nb_group_caption', 'nb_group_colors', 'nb_group_shape', 'nb_group_headers', 'nb_group_row', 79 'add_one_plot', 'add_two_plot', 'add_hist_plot', 'remove_plot', 'remove_all', 'get_x_means_plot', 80 'remove_x_means_plot', 'set_fit_ix', 'set_xmeans_fit_ix']
81 - def __init__(self, name='Charts', global_name='QubX.Figures.Charts'):
82 super(SelectFace, self).__init__(name, global_name) 83 self.__ref = Reffer() 84 self.presets = qubx.settings.SettingsMgr['Select'] 85 self.presets.OnSet = self.__ref(self.__onPreSet) 86 self.__profile = self.presets.active 87 self.__recent_criteria = self.__profile['RecentCriteria'] 88 self.__recent_weights = self.__profile['RecentWeights'] 89 self.__fit_ix = None 90 self.__xmeans_fit_ix = None 91 self.__actionlist = [] 92 self.plots = [] 93 self.plots_showing = {} 94 self.x_means_plots = [] 95 self.x_means = {} # [table_name] 96 self.table_names = [] 97 self.table_events = [] 98 self.tb_by_name = {} 99 self.__colors = collections.defaultdict(lambda: []) # table_name : [group_of_row(i) for i in range(table.size)] 100 self.__fields = collections.defaultdict(lambda: set()) # table_name : set of (field_name, is_log) of onscreen 101 self.__plots_using_field = collections.defaultdict(lambda: collections.defaultdict(lambda: [])) # table_name : field_name : [plot] 102 self.missing = memoize(self.__missing) 103 self.vals = collections.defaultdict(lambda: collections.defaultdict(lambda: self.missing)) # [table_name][field_name, is_log] = f(group=None) -> list of field values 104 self.mean = collections.defaultdict(lambda: collections.defaultdict(lambda: self.missing)) # [table_name][field_name, is_log] = f(group=None) 105 self.std = collections.defaultdict(lambda: collections.defaultdict(lambda: self.missing)) 106 self.median = collections.defaultdict(lambda: collections.defaultdict(lambda: self.missing)) 107 self.mode = collections.defaultdict(lambda: collections.defaultdict(lambda: self.missing)) 108 self.v_fields = {} # [table_name] -> memoized(lambda: [field_name] 109 self.v_names = {} # [table_name] -> memoized(lambda: [safe_field_name] 110 self.vvv = {} # [table_name] -> memoized(lambda: [field_series]) 111 112 self.panLeftRight = pack_item(gtk.HBox(), self, expand=True) 113 self.panLeft = pack_item(gtk.VBox(), self.panLeftRight) 114 self.popupActions = gtk.Menu() 115 self.mnuActions = pack_item(qubx.toolspace.Button_Popup(self.popupActions, color=COLOR_ACTIONS, tooltip='Click for Charts menu', 116 on_popup=self.__ref(self.__onPopupActions)), 117 self.panLeft) 118 self.palette = pack_item(qubx.toolspace.Palette(columns=2), self.panLeft, expand=True) 119 self.palette.set_tooltip_text('Pick a color, click points to make groups') 120 121 QubX = qubx.pyenv.env.globals['QubX'] 122 123 self.empty_msg = pack_item(qubx.toolspace.ToolSpace(), self.panLeftRight, expand=True) 124 self.empty_msg.OnDraw += self.__ref(lambda ctx, w, h: ctx.set_source_rgba(1,1,1,1) or ctx.paint()) 125 layer = qubx.toolspace.Layer(x=0, y=0, w=-.01, h=27, cBG=COLOR_EMPTY_BG) 126 layer.add_sublayer(qubx.toolspace.SubLayer_Label("To read a table from disk, or create a new one, use the Tables' file menu", 127 -1, 1, x=6, y=2, w=50, h=1.5, 128 color=COLOR_EMPTY_TEXT, hover_color=COLOR_EMPTY_TEXT)) 129 layer.add_sublayer(qubx.toolspace.SubLayer_Label("(blue folder icon in the left-hand sidebar, next to 'Tables').", 130 -1, 1, x=8, y=3.5, w=50, h=1.5, 131 color=COLOR_EMPTY_TEXT, hover_color=COLOR_EMPTY_TEXT)) 132 layer.add_sublayer(qubx.toolspace.SubLayer_Label("New Table...", 0, 1, x=15, y=6, w=12, h=1.5, color=COLOR_EMPTY_BTN, 133 border=1, action=self.__ref(lambda x,y,e: QubX.new_table()))) 134 layer.add_sublayer(qubx.toolspace.SubLayer_Label("Open Table...", 0, 1, x=30, y=6, w=12, h=1.5, color=COLOR_EMPTY_BTN, 135 border=1, action=self.__ref(lambda x,y,e: QubX.open_table()))) 136 layer.add_sublayer(qubx.toolspace.SubLayer_Label("To plot columns from the Tables panel, use the Charts menu", 137 -1, 1, x=6, y=9, w=50, h=1.5, 138 color=COLOR_EMPTY_TEXT, hover_color=COLOR_EMPTY_TEXT)) 139 layer.add_sublayer(qubx.toolspace.SubLayer_Label("(triangle at the upper-left of this panel).", 140 -1, 1, x=8, y=10.5, w=50, h=1.5, 141 color=COLOR_EMPTY_TEXT, hover_color=COLOR_EMPTY_TEXT)) 142 layer.add_sublayer(qubx.toolspace.SubLayer_Label("Show Plot...", 0, 1, x=30, y=13, w=12, h=1.5, color=COLOR_EMPTY_BTN, 143 border=1, action=self.__ref(lambda x,y,e: self.add_plot()))) 144 layer.add_sublayer(qubx.toolspace.SubLayer_Label("To fit large files of sampled data, open them in the Data panel", 145 -1, 1, x=6, y=16, w=50, h=1.5, 146 color=COLOR_EMPTY_TEXT, hover_color=COLOR_EMPTY_TEXT)) 147 layer.add_sublayer(qubx.toolspace.SubLayer_Label("(use its blue folder icon [File menu]).", 148 -1, 1, x=8, y=17.5, w=50, h=1.5, 149 color=COLOR_EMPTY_TEXT, hover_color=COLOR_EMPTY_TEXT)) 150 layer.add_sublayer(qubx.toolspace.SubLayer_Label("Show the Data panel...", 0, 1, x=24, y=20, w=18, h=1.5, color=COLOR_EMPTY_BTN, 151 border=1, action=self.__ref(lambda x,y,e: QubX.Data.request_show()))) 152 layer.add_sublayer(qubx.toolspace.SubLayer_Label("To curve-fit any figure, click its fitting icon:", 153 -1, 1, x=6, y=23, w=32, h=1.5, 154 color=COLOR_EMPTY_TEXT, hover_color=COLOR_EMPTY_TEXT)) 155 layer.add_sublayer(qubx.fit_space.SubLayer_FitIcon(x=44, y=22, w=3, h=3)) 156 self.empty_msg.add_layer(layer) 157 158 self.grid = pack_item(qubx.GTK.AspectGrid(), self.panLeftRight, expand=True, show=False) 159 160 Tables = QubX.Tables 161 Tables.OnAddTable += self.__ref(self.__onAddTable) 162 Tables.OnRemoveTable += self.__ref(self.__onRemoveTable) 163 for tablerec in Tables.tables: 164 self.__onAddTable(tablerec.table) 165 166 self.actions.append(('Plot...', self.add_plot)) 167 self.actions.append(('Select all', self.select_all)) 168 self.actions.append(('Unselect all (black)', lambda: self.select_all(0))) 169 self.actions.append(('Select by criteria...', self.select_by_criteria)) 170 self.actions.append(('Trim...', self.trim)) 171 self.actions.append(('K-Means clustering', self.cluster_k_means)) 172 self.actions.append(('X-Means clustering...', self.cluster_x_means)) 173 self.actions.append(('Edit bounds, all charts...', self.edit_bounds_all)) 174 175 self.nbCharts = qubx.notebook.NbItems("All charts", '%s.nbCharts'%self.global_name) 176 self.nbPictures = qubx.notebook.NbItems("All pictures", '%s.nbPictures'%self.global_name) 177 self.nbStats = qubx.notebook.NbTable("All group stats", '%s.nbStats'%self.global_name, 178 self.nb_group_caption, self.nb_group_shape, self.nb_group_headers, self.nb_group_row) 179 qubx.notebook.Notebook.register_auto('Select.XMeans.Chart', 'XMeans Chart, on XMeans') 180 qubx.notebook.Notebook.register_auto('Select.XMeans.Picture', 'XMeans Picture, on XMeans') 181 qubx.notebook.Notebook.register_auto('Select.XMeans.Charts', 'All Charts, on XMeans', True) 182 qubx.notebook.Notebook.register_auto('Select.XMeans.Pictures', 'All Pictures, on XMeans') 183 qubx.notebook.Notebook.register_auto('Select.XMeans.Stats', 'All group stats, on XMeans', True) 184 qubx.notebook.Notebook.register_auto('Select.Parameters', 'Chart fitting Parameters, on Fit', True) 185 qubx.notebook.Notebook.register_auto('Select.Results', 'Chart fitting Results, on Fit', True) 186 qubx.notebook.Notebook.register_auto('Select.Chart', 'Chart, on Fit') 187 qubx.notebook.Notebook.register_auto('Select.Picture', 'Chart Picture, on Fit', True) 188 189 setaside = self.__profile.clone() 190 # add_*_plot will add an entry in self.__profile, so remove the originals 191 self.__profile.remove(self.__profile['Plots']) 192 self.__onPreSet(self.presets, setaside) 193 194 # persistent: 195 if not self.__profile['BinCount'].data: 196 self.__profile['BinCount'].data = 12 197 if not self.__profile['TrialsPerK'].data: 198 self.__profile['TrialsPerK'].data = 300 199 # reset each session: 200 self.__trim_stds = 1.0 201 self.__criteria_expr = "" 202 self.__xmeans_klo = 1 203 self.__xmeans_khi = None 204 self.__xmeans_weight_expr = "" 205 self.__add_table_name = "Segments"
206 actions = property(lambda self: self.__actionlist, None, "to add an item to the Actions menu, insert a tuple ('label', lambda: action())")
207 - def __onPopupActions(self):
208 def build_item(caption, action=None): 209 if action: 210 return build_menuitem(caption, self.__ref(bind(action))) 211 else: 212 return build_menuitem(caption)
213 menu = self.popupActions 214 menu.foreach(lambda item: menu.remove(item)) 215 for lbl, func in self.actions: 216 menu.append(build_item(lbl, func)) 217 218 menu_presets = gtk.Menu() 219 for name in sorted(self.presets.listNames(), key=lambda x: x.lower()): 220 menu_presets.append(build_item(name, bind(self.__onClickUsePreset, name))) 221 menu_presets.append(build_item('Manage...', self.__onClickManagePresets)) 222 menu_presets.append(build_item('Add to menu...', self.__onClickAddPreset)) 223 item_presets = build_item('Chart sets') 224 item_presets.set_sensitive(True) 225 item_presets.set_submenu(menu_presets) 226 menu.append(item_presets) 227 return True
228 - def __onPreSet(self, settings, updates):
229 self.remove_all() 230 for node in qubx.tree.children(updates.find('Plots')): 231 if 'Label' in (str(node.find('Field').data), 232 str(node.find('FieldX').data), 233 str(node.find('FieldY').data)): 234 continue # the undead plot of Label lives on in someone's presets 235 try: 236 if node.name == 'OnePlot': 237 self.add_one_plot(str(node['Table'].data), 238 str(node['Field'].data), 239 bool(node['IsLog'].data[0]), 240 str(node['Ind'].data), 241 bool(node['IndIsLog'].data[0])) 242 elif node.name == 'TwoPlot': 243 self.add_two_plot(str(node['Table'].data), 244 str(node['FieldX'].data), 245 str(node['FieldY'].data), 246 bool(node['IsLogX'].data[0]), 247 bool(node['IsLogY'].data[0])) 248 elif node.name == 'HistPlot': 249 self.add_hist_plot(str(node['Table'].data), 250 str(node['Field'].data), 251 bool(node['IsLog'].data[0]), 252 node['BinCount'].data[0]) 253 except: 254 traceback.print_exc()
255 - def __onClickUsePreset(self, name):
256 qubx.pyenv.env.OnScriptable('%s.use_preset(%s)' % (self.global_name, repr(name))) 257 self.use_preset(name)
258 - def use_preset(self, name):
259 self.__onPreSet(self.presets, self.presets.read(name))
260 - def __onClickManagePresets(self):
261 dlg = qubx.settingsGTK.PresetsDialog(self.presets.name, None) 262 dlg.run() 263 dlg.destroy()
264 - def __onClickAddPreset(self):
265 name = None 266 dlg = qubx.GTK.NumEntryDialog('%s - Add chart-set to menu'%qubx.pyenv.env.globals['QubX'].appname, 267 None, 'Name:', 'Untitled', acceptString) 268 if gtk.RESPONSE_ACCEPT == dlg.run(): 269 name = dlg.value 270 dlg.destroy() 271 if not name: return 272 qubx.pyenv.env.OnScriptable('%s.presets.save(%s)' % (self.global_name, repr(name))) 273 self.presets.save(name)
274 - def prefer_table(self, table_name):
275 """Makes table_name the default choice for "add a chart", xmeans etc.""" 276 self.__add_table_name = table_name
277 - def add_plot(self, table_name=None):
278 QubX = qubx.pyenv.env.globals['QubX'] 279 if not (table_name is None): 280 tbn = table_name 281 elif QubX.Tables.find_table(self.__add_table_name): 282 tbn = self.__add_table_name 283 else: 284 tbn = 'Segments' 285 table = QubX.Tables.find_table(tbn) 286 dlg = gtk.Dialog("%s - Add a chart"%QubX.appname, qubx.GTK.get_active_window(), gtk.DIALOG_MODAL) 287 columns = pack_item(gtk.HBox(), dlg.vbox) 288 col = pack_item(gtk.VBox(), columns) 289 pack_label('Table:', col) 290 col = pack_item(gtk.VBox(), columns) 291 self.mnuTable = pack_item(qubx.GTK.StaticComboList(sorted([trec.table.label for trec in QubX.Tables.tables])), col) 292 self.mnuTable.active_text = tbn 293 self.chkTypePlot = pack_radio('Plot', col, on_toggle=self.__ref(self.__onToggleType)) 294 self.chkTypeHist = pack_radio('Histogram', col, group=self.chkTypePlot, on_toggle=self.__ref(self.__onToggleType)) 295 pack_space(columns, x=20) 296 col = pack_item(gtk.VBox(), columns) 297 line = pack_item(gtk.HBox(), col) 298 self.panVarX = pack_item(gtk.HBox(), line) 299 pack_label('X: ', self.panVarX) 300 self.chkLogX = pack_check('log10', self.panVarX) 301 self.mnuVarX = pack_item(qubx.GTK.DynamicComboBox(), self.panVarX) 302 self.mnuVarX.OnPopulate += self.__ref(self.__onPopulateVar) 303 self.mnuVarX.active_text = 'Index' 304 line = pack_item(gtk.HBox(), col) 305 self.lblYser = pack_label('Y: ', line) 306 self.chkLogY = pack_check('log10', line) 307 self.mnuVarY = pack_item(qubx.GTK.DynamicComboBox(), line) 308 self.mnuVarY.OnPopulate += self.__ref(self.__onPopulateVar) 309 self.mnuVarY.active_text = 'Pick the Y series' if (table is None) else table.fields[-1] 310 self.panHist = pack_item(gtk.HBox(), col, show=False) 311 pack_label('Bins:', self.panHist, expand=True) 312 self.txtHistBins = pack_item(qubx.GTK.NumEntry(self.__profile['BinCount'].data[0], acceptIntGreaterThan(2), width_chars=3), self.panHist) 313 dlg.add_button('Cancel', gtk.RESPONSE_REJECT) 314 dlg.add_button('Plot', gtk.RESPONSE_ACCEPT) 315 response = dlg.run() 316 dlg.destroy() 317 if response == gtk.RESPONSE_ACCEPT: 318 self.__profile['BinCount'].data[0] = self.txtHistBins.value 319 self.__add_table_name = self.mnuTable.active_text 320 self.__onClickPlot() 321 return True 322 else: 323 return False
324 - def __onToggleType(self, chk):
325 if not chk.get_active(): 326 return 327 if chk == self.chkTypePlot: 328 self.panVarX.show() 329 self.lblYser.show() 330 self.panHist.hide() 331 else: 332 self.panVarX.hide() 333 self.lblYser.hide() 334 self.panHist.show()
335 - def __onPopulateVar(self, add):
336 Tables = qubx.pyenv.env.globals['QubX'].Tables 337 table = Tables.find_table(self.mnuTable.active_text) 338 if table.size: 339 for field_name in sorted(table.fields, key=lambda x:x.lower()): 340 if operator.isNumberType(table[0,field_name]): 341 add(field_name)
342 - def __onClickPlot(self):
343 Tables = qubx.pyenv.env.globals['QubX'].Tables 344 table = Tables.find_table(self.mnuTable.active_text) 345 if not table: 346 qubx.GTK.ShowMessage("There is no longer a %s table. Please pick another." % self.mnuTable.active_text) 347 return 348 if self.chkTypePlot.get_active(): 349 for field_name in (self.mnuVarX.active_text, self.mnuVarY.active_text): 350 if not (field_name in table.fields): 351 qubx.GTK.ShowMessage("The %s table doesn't have a field %s. Please pick another." % (self.mnuTable.active_text, field_name)) 352 return 353 if self.mnuVarX.active_text in table.fields_independent: 354 qubx.pyenv.env.OnScriptable('%s.add_one_plot(%s, %s, %s, %s, %s); QubX.show_charts()' % 355 (self.global_name, 356 repr(table.label), repr(self.mnuVarY.active_text), repr(self.chkLogY.get_active()), 357 repr(self.mnuVarX.active_text), repr(self.chkLogX.get_active()))) 358 self.add_one_plot(table.label, self.mnuVarY.active_text, self.chkLogY.get_active(), 359 self.mnuVarX.active_text, self.chkLogX.get_active()) 360 else: 361 qubx.pyenv.env.OnScriptable('%s.add_two_plot(%s, %s, %s, %s, %s); QubX.show_charts()' % 362 (self.global_name, 363 repr(table.label), repr(self.mnuVarX.active_text), repr(self.mnuVarY.active_text), 364 repr(self.chkLogX.get_active()), repr(self.chkLogY.get_active()))) 365 self.add_two_plot(table.label, self.mnuVarX.active_text, self.mnuVarY.active_text, 366 self.chkLogX.get_active(), self.chkLogY.get_active()) 367 else: 368 if not (self.mnuVarY.active_text in table.fields): 369 qubx.GTK.ShowMessage("The %s table doesn't have a field %s. Please pick another." % (self.mnuTable.active_text, self.mnuVarY.active_text)) 370 return 371 qubx.pyenv.env.OnScriptable('%s.add_hist_plot(%s, %s, %s, %s); QubX.show_charts()' % 372 (self.global_name, 373 repr(table.label), repr(self.mnuVarY.active_text), repr(self.chkLogY.get_active()), 374 repr(self.txtHistBins.value))) 375 self.add_hist_plot(table.label, self.mnuVarY.active_text, self.chkLogY.get_active(), self.txtHistBins.value)
376 - def fields_in(self, table_name):
377 return list(self.__fields[table_name])
378 - def colors_in(self, table_name, include_zero=False):
379 try: 380 return self.tb_by_name[table_name].groups_occupied(include_zero) 381 except: 382 traceback.print_exc() 383 return []
384 - def get_color(self, table_name, ix):
385 return self.__colors[table_name][ix]
386 - def set_color(self, table_name, ix, color):
387 if not (table_name in self.tb_by_name): 388 return 389 colors = self.__colors[table_name] 390 if colors[ix] != color: 391 colors[ix] = color 392 self.tb_by_name[table_name].set(ix, 'Group', color)
393 - def get_colors(self, table_name):
394 return self.__colors[table_name]
395 - def set_colors(self, table_name, color_list, index_list=None):
396 if not (table_name in self.tb_by_name): 397 return 398 table = self.tb_by_name[table_name] 399 colors_cache = self.get_colors(table_name) 400 indices = index_list or count() 401 for i, c in izip(indices, color_list): 402 if c != colors_cache[i]: 403 colors_cache[i] = c 404 table.set(i, 'Group', c)
405 - def select_all(self, color=None):
406 qubx.pyenv.env.OnScriptable('%s.select_all(color=%s)' % (self.global_name, repr(color))) 407 c = color 408 if c is None: 409 c = self.palette.color 410 for table_name, table in self.tb_by_name.iteritems(): 411 self.set_colors(table_name, [c]*table.size)
412 - def select_by_criteria(self, table=None, expr=None):
413 if not self.tb_by_name: 414 return 415 ixs = None 416 if table and expr: 417 try: 418 ixs = rows_meeting_criteria(table, expr) 419 tbl = table 420 except: 421 traceback.print_exc() 422 else: 423 dlg = CriteriaDialog([self.tb_by_name[name] for name in sorted(self.tb_by_name.keys())], self.__recent_criteria) 424 self.__criteria_expr, ixs = dlg.run(self.__criteria_expr) 425 tbl = dlg.table 426 dlg.destroy() 427 if tbl.global_name: 428 qubx.pyenv.env.OnScriptable('%s.select_by_criteria(table=%s, expr=%s)' % 429 (self.global_name, tbl.global_name, repr(self.__criteria_expr))) 430 if ixs: 431 self.set_colors(tbl.label, [self.palette.color]*len(ixs), ixs)
432 - def __add_recent(self, tree, entry):
433 if not entry: 434 return 435 rec = tree.find(entry) 436 if not rec.isNull: 437 tree.remove(rec) 438 tree.insert(rec) 439 else: 440 tree.insert(qubx.tree.Node(entry)) 441 rec = tree.child 442 for i in xrange(RECENT_COUNT): 443 rec = rec.sibling 444 if rec.isNull: 445 break 446 while not rec.isNull: 447 next = rec.sibling 448 tree.remove(rec) 449 rec = next
450 - def trim(self, num_std=None):
451 stds = num_std 452 if stds is None: 453 QubX = qubx.pyenv.env.globals['QubX'] 454 dlg = qubx.GTK.NumEntryDialog('%s - Trim'%QubX.appname, qubx.GTK.get_active_window(), 455 "Unselect points outside this many std devs:", 456 self.__trim_stds, 457 acceptFloatGreaterThan(0.0), '%.5g') 458 response = dlg.run() 459 dlg.destroy() 460 if response == gtk.RESPONSE_ACCEPT: 461 stds = self.__trim_stds = dlg.value 462 else: 463 return 464 qubx.pyenv.env.OnScriptable('%s.trim(num_std=%s)' % (self.global_name, repr(stds))) 465 def trim_fields(table, fields): 466 colors = self.__colors[table.label][:] # work on a copy then set them all at once, to avoid interfering with stats 467 for field in fields: 468 for color in table.groups_occupied(): 469 ixs = table.rows_in_group(color) 470 vals = self.vals[table.label][field](color) 471 mean = self.mean[table.label][field](color) 472 std = self.std[table.label][field](color) 473 for i,v in izip(ixs, vals): 474 if abs(v - mean) > std: 475 colors[i] = 0 476 self.set_colors(table.label, colors)
477 if self.__fit_ix is None: 478 for table in self.tb_by_name.values(): 479 trim_fields(table, self.__fields[table.label]) 480 else: 481 plot = self.plots[self.__fit_ix] 482 trim_fields(plot.table, plot.fields)
483 - def cluster_k_means(self, table_name=None):
484 qubx.pyenv.env.OnScriptable('%s.cluster_k_means(table_name=%s)' % (self.global_name, repr(table_name))) 485 if (table_name is None) and not (self.__fit_ix is None): 486 table_names = [self.plots[self.__fit_ix].table_name] 487 fields = {table_names[0] : self.plots[self.__fit_ix].fields} 488 elif table_name: 489 table_names = [table_name] 490 fields = self.__fields 491 else: 492 table_names = self.tb_by_name.keys() 493 fields = self.__fields 494 for table_name in table_names: 495 table = self.tb_by_name[table_name] 496 if table_name in self.x_means: 497 wgt = self.x_means[table_name].weight_expr 498 else: 499 wgt = "" 500 colors = self.__colors[table_name][:] 501 color_set = set(colors) 502 k = len(color_set) 503 if 0 in color_set: 504 k -= 1 505 if k <= 0: 506 k = 1 507 #colors, ssd = cluster_k_means(colors, self.__evalXMeansWeight(wgt, table=table), 508 # [self.vals[table_name][field]() for field in fields[table_name]]) 509 try: 510 ww = self.__evalXMeansWeight(wgt, table=table) 511 except: 512 ww = array([1.0]*table.size, dtype='float32') 513 clusterer = qubx.fast.kmeans.Clusterer([vv for vv in [self.vals[table_name][field]() for field in fields[table_name]] if len(vv)], 514 ww) 515 ssd = clusterer.cluster(colors) 516 self.set_colors(table_name, colors) 517 LL, aicc = clusterer.ll_aicc(colors, k) 518 self.get_x_means_plot(table_name).set_ssd(k, ssd, LL, aicc, clusterer.Ndata, clusterer.Nseries)
519 - def cluster_x_means_k(self, table_name, k, trials_per_k=None, clusterer=None):
520 if not self.plots: 521 return 0.0 522 #colors, ssd = cluster_k_means_trials(self.__colors[table_name], 523 # self.__evalXMeansWeight(wgt), # table=table? 524 # [self.vals[table_name][field]() for field in fields[table_name]], 525 # k, tpk) 526 clust = clusterer 527 if clusterer is None: 528 if (not (self.__fit_ix is None)) and (table_name == self.plots[self.__fit_ix].table_name): 529 fields = {table_name : self.plots[self.__fit_ix].fields} 530 else: 531 fields = self.__fields 532 table = self.tb_by_name[table_name] 533 wgt = self.x_means[table_name].weight_expr 534 try: 535 ww = self.__evalXMeansWeight(wgt, table=table) 536 except: 537 ww = array([1.0]*table.size, dtype='float32') 538 clust = qubx.fast.kmeans.Clusterer([vv for vv in [self.vals[table_name][field]() for field in fields[table_name]] if len(vv)], 539 ww) 540 tpk = trials_per_k 541 if tpk is None: 542 tpk = self.x_means[table_name].trials_per_k 543 544 colors = self.__colors[table_name][:] 545 ssd = clust.cluster_trials(colors, k, tpk) 546 547 self.set_colors(table_name, colors) 548 if not clusterer: 549 LL, aicc = clust.ll_aicc(colors, k) 550 self.get_x_means_plot(table_name).set_ssd(k, ssd, LL, aicc, clust.Ndata, clust.Nseries) 551 return ssd
552 - def cluster_x_means(self, table_name=None, k_min=None, k_max=None, trials_per_k=None, weight=None):
553 if not self.plots: 554 return 555 tbn = table_name 556 klo = k_min 557 khi = k_max 558 tpk = trials_per_k 559 wgt = weight 560 if (table_name is None) or (k_min is None) or (k_max is None) or (trials_per_k is None) or (weight is None): 561 if tbn is None: 562 if self.__add_table_name in self.tb_by_name: 563 tbn = self.__add_table_name 564 else: 565 tbn = self.tb_by_name.keys()[0] 566 if klo is None: klo = self.__xmeans_klo 567 if khi is None: khi = self.__xmeans_khi or int(round(sqrt(self.tb_by_name[tbn].size))) 568 if tpk is None: tpk = self.__profile['TrialsPerK'].data[0] 569 if wgt is None: wgt = self.__xmeans_weight_expr 570 # show a dialog... 571 QubX = qubx.pyenv.env.globals['QubX'] 572 dlg = gtk.Dialog("%s - Charts - X-Means Clustering"%QubX.appname, qubx.GTK.get_active_window(), gtk.DIALOG_MODAL) 573 line = pack_item(gtk.HBox(True), dlg.vbox) 574 pack_label('Table:', line) 575 mnuTable = self.mnuXMeansTable = pack_item(qubx.GTK.StaticComboList(sorted([x for x in self.tb_by_name.keys()])), line) 576 line = pack_item(gtk.HBox(True), dlg.vbox) 577 pack_label('Min K:', line) 578 txtKlo = pack_item(qubx.GTK.NumEntry(klo, acceptIntGreaterThan(0)), line) 579 line = pack_item(gtk.HBox(True), dlg.vbox) 580 pack_label('Max K:', line) 581 txtKhi = pack_item(qubx.GTK.NumEntry(khi, acceptIntGreaterThan(0)), line) 582 line = pack_item(gtk.HBox(True), dlg.vbox) 583 pack_label('Trials/K:', line) 584 txtTrials = pack_item(qubx.GTK.NumEntry(tpk, acceptIntGreaterThan(0)), line) 585 line = pack_item(gtk.HBox(), dlg.vbox) 586 pack_label('Weight:', line) 587 txtWeight = self.txtXMeansWeight = pack_item(qubx.GTK.NumEntry(wgt, self.__acceptXMeansWeight), line, expand=True) 588 txtWeight.onParse(True) # catch initially invalid expr 589 btnHelp = pack_item(qubx.pyenvGTK.HelpTestButton(txtWeight), line) 590 btnHelp.caption = "Enter an expression in terms of field_name" 591 btnHelp.help_msg = XMEANS_WEIGHT_HELP 592 btnHelp.bind = lambda expr: qubx.pyenv.env.globals.__setitem__('xmeans_weight', lambda i: self.__evalXMeansWeight(expr=expr, indices=[i])[0]) 593 btnHelp.write_test = lambda expr: 'xmeans_weight(0)' 594 self.mnuRecent = gtk.Menu() 595 for node in qubx.tree.children(self.__recent_weights): 596 entry = node.name 597 build_menuitem(entry, self.__ref(bind(self.txtXMeansWeight.setValue, entry, parse=True)), menu=self.mnuRecent) 598 self.btnRecent = pack_item(qubx.toolspace.Button_Popup(self.mnuRecent, tooltip='Recent...'), line) 599 600 dlg.add_button('Cancel', gtk.RESPONSE_REJECT) 601 dlg.add_button('OK', gtk.RESPONSE_ACCEPT) 602 response = dlg.run() 603 dlg.destroy() 604 if response != gtk.RESPONSE_ACCEPT: 605 return 606 tbn = mnuTable.active_text 607 klo = txtKlo.value 608 khi = txtKhi.value 609 tpk = txtTrials.value 610 wgt = txtWeight.value 611 self.__profile['TrialsPerK'].data[0] = tpk 612 self.__add_table_name = tbn 613 self.__xmeans_klo = klo 614 self.__xmeans_khi = khi 615 self.__xmeans_weight_expr = wgt 616 617 qubx.pyenv.env.OnScriptable('%s.cluster_x_means(table_name=%s, k_min=%s, k_max=%s, trials_per_k=%s, weight=%s)' % 618 (self.global_name, repr(tbn), repr(klo), repr(khi), repr(tpk), repr(wgt))) 619 620 if (not (self.__fit_ix is None)) and (tbn == self.plots[self.__fit_ix].table_name): 621 fields = {tbn : self.plots[self.__fit_ix].fields} 622 else: 623 fields = self.__fields 624 table = self.tb_by_name[tbn] 625 try: 626 ww = self.__evalXMeansWeight(wgt, table=table) 627 self.__add_recent(self.__recent_weights, wgt) 628 except: 629 ww = array([1.0]*table.size, dtype='float32') 630 631 colors = self.__colors[tbn][:] 632 color_set = set(colors) 633 orig_k = len(color_set) 634 if 0 in color_set: 635 orig_k -= 1 636 637 clusterer = qubx.fast.kmeans.Clusterer([vv for vv in [self.vals[tbn][field]() for field in fields[tbn]] if len(vv)], ww) 638 639 k_ssd_ll_aicc = [] 640 for k in xrange(klo, khi+1): 641 ssd = self.cluster_x_means_k(tbn, k, trials_per_k=tpk, clusterer=clusterer) 642 LL, aicc = clusterer.ll_aicc(self.__colors[tbn], k) 643 k_ssd_ll_aicc.append((k, ssd, LL, aicc)) 644 # set them all at once, to delay showing the chart, in case it's not already there 645 plot = self.get_x_means_plot(tbn) 646 plot.weight_expr = wgt 647 plot.trials_per_k = tpk 648 for k, ssd, LL, aicc in k_ssd_ll_aicc: 649 plot.set_ssd(k, ssd, LL, aicc, clusterer.Ndata, clusterer.Nseries) 650 if orig_k > 0: 651 self.cluster_x_means_k(tbn, orig_k, trials_per_k=tpk, clusterer=clusterer) 652 gobject.idle_add(self.__nb_auto_xmeans, plot)
653 - def __nb_auto_xmeans(self, plot):
654 qubx.notebook.Notebook.send(plot.nbChart, auto_id='Select.XMeans.Chart') 655 qubx.notebook.Notebook.send(plot.nbPicture, auto_id='Select.XMeans.Picture') 656 qubx.notebook.Notebook.send(self.nbCharts, auto_id='Select.XMeans.Charts') 657 qubx.notebook.Notebook.send(self.nbPictures, auto_id='Select.XMeans.Pictures') 658 qubx.notebook.Notebook.send(self.nbStats, auto_id='Select.XMeans.Stats')
659 - def __acceptXMeansWeight(self, expr):
660 self.__evalXMeansWeight(expr=expr, indices=[0]) 661 return expr
662 - def __evalXMeansWeight(self, expr=None, indices=None, table=None):
663 if expr is None: 664 wgt = self.txtXMeansWeight.value 665 else: 666 wgt = expr 667 if table is None: 668 tbl = self.tb_by_name[self.mnuXMeansTable.active_text] 669 else: 670 tbl = table 671 if not expr: 672 return [1.0]*tbl.size 673 ixs = indices 674 if ixs is None: 675 ixs = list(range(tbl.size)) 676 results = [] 677 locals = OverridingDict() 678 for ix in ixs: 679 locals['row'] = locals.base = tbl.get_row(ix, safenames=True).__dict__ 680 results.append(eval(wgt, qubx.pyenv.env.globals, locals)) 681 return results
682 - def edit_bounds_all(self, x0=None, y0=None, x1=None, y1=None):
683 if not self.plots: 684 return 685 if y1 is None: 686 x0 = self.plots[0].t0 if (x0 is None) else x0 687 y0 = self.plots[0].d0 if (y0 is None) else y0 688 x1 = self.plots[0].t1 if (x1 is None) else x1 689 y1 = self.plots[0].d1 if (y1 is None) else y1 690 x0, y0, x1, y1 = qubx.fit_space.PromptChartBounds(x0, y0, x1, y1) 691 if x0 is None: 692 return 693 qubx.pyenv.env.OnScriptable('QubX.Charts.edit_bounds_all(x0=%s, y0=%s, x1=%s, y1=%s)' % (x0, y0, x1, y1)) 694 for plot in self.plots: 695 plot.set_bounds(x0, y0, x1, y1)
696 - def __reset_stats(self, table_name, field_name=None, is_log=False):
697 if field_name is None: 698 fields = self.vals[table_name].keys() 699 else: 700 fields = [(field_name, is_log)] 701 for field_name, is_log in fields: 702 if (field_name, is_log) in self.vals[table_name]: 703 self.vals[table_name][field_name, is_log].reset() 704 if (field_name, is_log) in self.mean[table_name]: 705 self.mean[table_name][field_name, is_log].reset() 706 self.std[table_name][field_name, is_log].reset() 707 self.median[table_name][field_name, is_log].reset() 708 self.mode[table_name][field_name, is_log].reset() 709 self.v_fields[table_name].reset() 710 self.v_names[table_name].reset() 711 self.vvv[table_name].reset()
712 - def __missing(self, group=None):
713 return array([], dtype='float32')
714 - def __vals(self, table_name, field_name, is_log, group=None):
715 table = qubx.pyenv.env.globals['QubX'].Tables.find_table(table_name) 716 if (not table) or not (field_name in table.fields): 717 return self.__missing() 718 try: 719 vals = array([table.get(i, field_name) for i in table.rows_in_group(group)], dtype='float32') 720 if is_log: 721 for i,v in enumerate(vals): 722 if v > 0.0: 723 vals[i] = log10(v) 724 else: 725 vals[i] = 0.0 726 return vals 727 except ValueError, ve: 728 traceback.print_exc() 729 print 'select_charts.SelectFace.__vals(%s, %s, %s, %s) raised %s' % (table_name, field_name, is_log, group, ve) 730 return zeros(shape=(table.size,), dtype='float32')
731 - def __mean(self, table_name, field_name, is_log, group=None):
732 tot = 0.0 733 cnt = 0 734 for v in self.vals[table_name][field_name, is_log](group): 735 try: 736 tot += v 737 cnt += 1 738 except: 739 pass 740 return cnt and (tot / cnt) or 0.0
741 - def __std(self, table_name, field_name, is_log, group=None):
742 tot = 0.0 743 cnt = 0 744 mean = self.mean[table_name][field_name, is_log](group) 745 for v in self.vals[table_name][field_name, is_log](group): 746 try: 747 tot += (mean - v)**2 748 cnt += 1 749 except: 750 pass 751 return cnt and sqrt(tot / cnt) or 0.0
752 - def __median(self, table_name, field_name, is_log, group=None):
753 try: 754 vals = sorted(self.vals[table_name][field_name, is_log](group)) 755 if len(vals) % 2: 756 return vals[len(vals)/2] 757 elif len(vals): 758 mid = len(vals)/2 759 return (vals[mid] + vals[mid-1]) / 2.0 760 else: 761 return 0.0 762 except: 763 return 0.0
764 - def __mode(self, table_name, field_name, is_log, group=None):
765 counter = collections.defaultdict(lambda: 0) 766 for v in self.vals[table_name][field_name, is_log](group): 767 counter[v] += 1 768 counted = collections.defaultdict(lambda: []) 769 maxcnt = 0 770 for val, cnt in counter.iteritems(): 771 counted[cnt].append(val) 772 if cnt > maxcnt: 773 maxcnt = cnt 774 modes = counted[maxcnt] 775 if len(modes) > 1: 776 return sum(modes) / len(modes) 777 elif modes: 778 return modes[0] 779 else: 780 return 0.0
781 - def __v_fields(self, table_name):
782 if not (table_name in self.tb_by_name): 783 return [] 784 table = self.tb_by_name[table_name] 785 if not table: 786 return [] 787 names = [] 788 for name in table.fields: 789 try: 790 float(table.get(0, name)) 791 names.append(name) 792 except: 793 pass 794 return sorted(names, key=lambda x: x.lower())
795 - def __v_names(self, table_name):
796 return [qubx.fit_space.SafeName(name) for name in self.v_fields[table_name]()]
797 - def __vvv(self, table_name):
798 if not (table_name in self.tb_by_name): 799 return [] 800 table = self.tb_by_name[table_name] 801 if not table: 802 return [] 803 return [self.vals[table_name][field_name, False]() for field_name in self.v_fields[table_name]()]
804 - def nb_group_caption(self):
805 return 'Charts group stats'
806 - def nb_group_colors(self):
807 sum = __builtin__.sum 808 return list(set(sum((self.colors_in(tbn) for tbn in self.tb_by_name.keys()), [])))
809 - def nb_group_shape(self):
810 sum = __builtin__.sum 811 return (sum(len(self.__fields[tbn]) for tbn in self.tb_by_name.keys()), 1+4*len(self.nb_group_colors()))
812 - def nb_group_headers(self):
813 sum = __builtin__.sum 814 return ["Name"] + sum((["Mean %d"%c, "Std %d"%c, "Median %d"%c, "Mode %d"%c] 815 for c in self.nb_group_colors()), [])
816 - def nb_group_row(self, i):
817 sum = __builtin__.sum 818 tb_order = sorted(self.tb_by_name.keys()) 819 order = sorted(sum(([(tbn, field) for field in self.__fields[tbn]] for tbn in tb_order), []), key=lambda x: x[1][0].lower()) 820 tbn, field = order[i] 821 return [field[0]] + sum(([stat[tbn][field](c) for stat in (self.mean, self.std, self.median, self.mode)] 822 for c in self.nb_group_colors()), [])
823 - def add_one_plot(self, table_name, field_name, is_log=False, ind_name='Index', ind_is_log=False):
824 if (table_name, field_name, is_log, ind_name, ind_is_log) in self.plots_showing: 825 return self.plots_showing[(table_name, field_name, is_log, ind_name, ind_is_log)] 826 table = self.__bind_plot(table_name, [(field_name, is_log)]) 827 xlabel = FieldLabel(table, ind_name, ind_is_log) 828 ylabel = FieldLabel(table, field_name, is_log) 829 if self.vals[table_name][ind_name, ind_is_log] == self.missing: 830 self.vals[table_name][ind_name, ind_is_log] = memoize(bind_with_args(self.__vals, table_name, ind_name, ind_is_log)) 831 plot = OnePlot(table_name, field_name, is_log, ylabel, ind_name, ind_is_log, xlabel, 832 self.v_names[table_name], self.vvv[table_name], 833 self.vals[table_name][ind_name, ind_is_log], 834 self.vals[table_name][field_name, is_log], 835 self.mean[table_name][field_name, is_log], 836 self.std[table_name][field_name, is_log], 837 table and table.count_in_group or (lambda g: None), 838 global_name='QubX.Figures.Charts.plots_showing[%s]'%str((table_name, field_name, is_log, ind_name, ind_is_log))) 839 plot.table = table 840 self.plots_showing[(table_name, field_name, is_log, ind_name, ind_is_log)] = plot 841 self.__plots_using_field[table_name][field_name].append(plot) 842 self.__plots_using_field[table_name][ind_name].append(plot) 843 return self.__show_plot(plot)
844 - def add_two_plot(self, table_name, field_name_x, field_name_y, is_log_x=False, is_log_y=False):
845 if (table_name, field_name_x, field_name_y, is_log_x, is_log_y) in self.plots_showing: 846 return self.plots_showing[(table_name, field_name_x, field_name_y, is_log_x, is_log_y)] 847 table = self.__bind_plot(table_name, [(field_name_x, is_log_x), (field_name_y, is_log_y)]) 848 xlabel = FieldLabel(table, field_name_x, is_log_x) 849 ylabel = FieldLabel(table, field_name_y, is_log_y) 850 plot = TwoPlot(table_name, field_name_x, field_name_y, is_log_x, is_log_y, xlabel, ylabel, 851 self.v_names[table_name], self.vvv[table_name], 852 self.vals[table_name][field_name_x, is_log_x], 853 self.vals[table_name][field_name_y, is_log_y], 854 self.mean[table_name][field_name_x, is_log_x], 855 self.mean[table_name][field_name_y, is_log_y], 856 self.std[table_name][field_name_x, is_log_x], 857 self.std[table_name][field_name_y, is_log_y], 858 table and table.count_in_group or (lambda g: None), 859 global_name='QubX.Figures.Charts.plots_showing[%s]'%str((table_name, field_name_x, field_name_y, is_log_x, is_log_y))) 860 plot.table = table 861 self.plots_showing[(table_name, field_name_x, field_name_y, is_log_x, is_log_y)] = plot 862 self.__plots_using_field[table_name][field_name_x].append(plot) 863 self.__plots_using_field[table_name][field_name_y].append(plot) 864 return self.__show_plot(plot)
865 - def add_hist_plot(self, table_name, field_name, is_log=False, bin_count=12):
866 if (table_name, field_name, is_log, bin_count) in self.plots_showing: 867 return self.plots_showing[(table_name, field_name, is_log, bin_count)] 868 table = self.__bind_plot(table_name, [(field_name, is_log)]) 869 ylabel = FieldLabel(table, field_name, is_log) 870 plot = HistPlot(table_name, field_name, is_log, bin_count, ylabel, 871 self.vals[table_name][field_name, is_log], 872 self.mean[table_name][field_name, is_log], 873 self.std[table_name][field_name, is_log], 874 table and table.count_in_group or (lambda g: None), 875 global_name='QubX.Figures.Charts.plots_showing[%s]'%str((table_name, field_name, is_log, bin_count))) 876 plot.table = table 877 self.plots_showing[(table_name, field_name, is_log, bin_count)] = plot 878 self.__plots_using_field[table_name][field_name].append(plot) 879 return self.__show_plot(plot)
880 - def __bind_plot(self, table_name, fields):
881 if table_name in self.table_names: 882 try: 883 table = self.tb_by_name[table_name] 884 except KeyError: 885 table = None 886 else: 887 self.table_names.append(table_name) 888 self.table_events.append( (self.__ref(bind_with_args(self.__onInsert, table_name)), 889 self.__ref(bind_with_args(self.__onRemoved, table_name)), 890 self.__ref(bind_with_args(self.__onSet, table_name)), 891 self.__ref(bind_with_args(self.__onAddField, table_name))) ) 892 self.v_fields[table_name] = memoize(bind(self.__v_fields, table_name)) 893 self.v_names[table_name] = memoize(bind(self.__v_names, table_name)) 894 self.vvv[table_name] = memoize(bind(self.__vvv, table_name)) 895 table = qubx.pyenv.env.globals['QubX'].Tables.find_table(table_name) 896 if table: 897 self.__attach_table(table) 898 for field_name, is_log in fields: 899 self.__fields[table_name].add((field_name, is_log)) 900 if self.vals[table_name][field_name, is_log] == self.missing: 901 self.vals[table_name][field_name, is_log] = memoize(bind_with_args(self.__vals, table_name, field_name, is_log)) 902 if self.mean[table_name][field_name, is_log] == self.missing: 903 self.mean[table_name][field_name, is_log] = memoize(bind_with_args(self.__mean, table_name, field_name, is_log)) 904 if self.std[table_name][field_name, is_log] == self.missing: 905 self.std[table_name][field_name, is_log] = memoize(bind_with_args(self.__std, table_name, field_name, is_log)) 906 if self.median[table_name][field_name, is_log] == self.missing: 907 self.median[table_name][field_name, is_log] = memoize(bind_with_args(self.__median, table_name, field_name, is_log)) 908 if self.mode[table_name][field_name, is_log] == self.missing: 909 self.mode[table_name][field_name, is_log] = memoize(bind_with_args(self.__mode, table_name, field_name, is_log)) 910 return table
911 - def __show_plot(self, plot):
912 plot.colors = self.__colors[plot.table_name] 913 plot.show() 914 self.grid.pack_aspect(plot) 915 plot.palette = self.palette 916 plot.OnClickClose += self.__ref(self.__onClickClosePlot) 917 plot.OnShowOne += self.__ref(self.__onShowOne) 918 plot.OnDelOne += self.__ref(self.__onDelOne) 919 plot.OnSelectOne += self.__ref(self.__onSelectOne) 920 plot.OnSelectMany += self.__ref(self.__onSelectMany) 921 plot.OnChangeLayerset += self.__ref(self.__onChangePlotLayerset) 922 plot.OnKeyPress += self.__ref(self.__onKeyPress) 923 if self.plots: 924 self.__profile['Plots'].insert(plot.as_preset, self.plots[-1].as_preset) 925 else: 926 self.__profile['Plots'].append(plot.as_preset) 927 self.empty_msg.hide() 928 self.grid.show() 929 plot.subNotebook.items.append(self.nbCharts) 930 plot.subNotebook.items.append(self.nbPictures) 931 plot.subNotebook.items.append(self.nbStats) 932 self.nbCharts.items.insert(len(self.plots), plot.nbChart) 933 self.nbPictures.items.insert(len(self.plots), plot.nbPicture) 934 self.plots.append(plot) 935 return plot
936 - def remove_plot(self, plot):
937 self.nbPictures.items.remove(plot.nbPicture) 938 self.nbCharts.items.remove(plot.nbChart) 939 if isinstance(plot, OnePlot): 940 del self.plots_showing[(plot.table_name, plot.field_name, plot.is_log, plot.ind_name, plot.ind_is_log)] 941 self.__plots_using_field[plot.table_name][plot.field_name].remove(plot) 942 self.__plots_using_field[plot.table_name][plot.ind_name].remove(plot) 943 elif isinstance(plot, TwoPlot): 944 del self.plots_showing[(plot.table_name, plot.field_name_x, plot.field_name_y, plot.is_log_x, plot.is_log_y)] 945 self.__plots_using_field[plot.table_name][plot.field_name_x].remove(plot) 946 self.__plots_using_field[plot.table_name][plot.field_name_y].remove(plot) 947 elif isinstance(plot, HistPlot): 948 del self.plots_showing[(plot.table_name, plot.field_name, plot.is_log, plot.bin_count)] 949 self.__plots_using_field[plot.table_name][plot.field_name].remove(plot) 950 self.plots.remove(plot) 951 self.__profile['Plots'].remove(plot.as_preset) 952 self.grid.remove(plot) 953 plot.OnClickClose -= self.__ref(self.__onClickClosePlot) 954 plot.OnShowOne -= self.__ref(self.__onShowOne) 955 plot.OnDelOne -= self.__ref(self.__onDelOne) 956 plot.OnSelectOne -= self.__ref(self.__onSelectOne) 957 plot.OnSelectMany -= self.__ref(self.__onSelectMany) 958 plot.OnChangeLayerset -= self.__ref(self.__onChangePlotLayerset) 959 plot.OnKeyPress -= self.__ref(self.__onKeyPress) 960 plot.dispose() 961 tables_remain = set(plot.table_name for plot in self.plots) 962 if not (plot.table_name in tables_remain): 963 if plot.table: 964 self.__detach_table(plot.table) 965 tbi = self.table_names.index(plot.table_name) 966 del self.table_names[tbi] 967 del self.table_events[tbi] 968 del self.__colors[plot.table_name] 969 del self.__fields[plot.table_name] 970 del self.vals[plot.table_name] 971 del self.mean[plot.table_name] 972 del self.std[plot.table_name] 973 del self.median[plot.table_name] 974 del self.mode[plot.table_name] 975 del self.v_fields[plot.table_name] 976 del self.v_names[plot.table_name] 977 del self.vvv[plot.table_name] 978 else: 979 self.__fields[plot.table_name] = set(reduce(operator.add, [p.fields for p in self.plots if p.table_name == plot.table_name], [])) 980 if not self.plots: 981 self.grid.hide() 982 self.empty_msg.show()
983 - def remove_all(self):
984 if not (self.__fit_ix is None): 985 plot = self.plots[self.__fit_ix] 986 plot.layerset = plot.controlsHidden 987 for plot in self.plots[:]: 988 self.remove_plot(plot)
989 - def get_x_means_plot(self, table_name):
990 # in order to add to the aspectgrid, we must replace any temporarily removed plot (solo curvefit display) 991 if not (self.__fit_ix is None): 992 self.plots[self.__fit_ix].layerset = self.plots[self.__fit_ix].controlsHidden 993 if not (self.__xmeans_fit_ix is None): 994 self.x_means_plots[self.__xmeans_fit_ix].layerset = self.x_means_plots[self.__xmeans_fit_ix].controlsHidden 995 if not (table_name in self.x_means): 996 plot = XMeansPlot(table_name, global_name='QubX.Figures.Charts.x_means["%s"]'%table_name) 997 plot.OnClickClose += self.__ref(self.__onClickCloseXMeans) 998 plot.OnClickPoint += self.__ref(self.__onClickPointXMeans) 999 plot.OnClickRefresh += self.__ref(self.__onClickRefreshXMeans) 1000 plot.OnChangeLayerset += self.__ref(self.__onChangeXMeansLayerset) 1001 plot.show() 1002 self.grid.pack_aspect(plot) 1003 plot.subNotebook.items.append(self.nbCharts) 1004 plot.subNotebook.items.append(self.nbPictures) 1005 self.nbCharts.items.insert(len(self.plots)+len(self.x_means_plots), plot.nbChart) 1006 self.nbPictures.items.insert(len(self.plots)+len(self.x_means_plots), plot.nbPicture) 1007 self.x_means_plots.append(plot) 1008 self.x_means[table_name] = plot 1009 table = self.tb_by_name[table_name] 1010 if table: 1011 if 'x_means_ssd' in table.__dict__: 1012 plot.ssd = table.x_means_ssd 1013 plot.update_plot() 1014 else: 1015 table.x_means_ssd = plot.ssd 1016 else: 1017 plot = self.x_means[table_name] 1018 return plot
1019 - def remove_x_means_plot(self, table_name):
1020 plot = self.x_means[table_name] 1021 self.nbPictures.items.remove(plot.nbPicture) 1022 self.nbCharts.items.remove(plot.nbChart) 1023 del self.x_means[table_name] 1024 self.x_means_plots.remove(plot) 1025 self.grid.remove(plot) 1026 plot.dispose()
1027 - def __attach_table(self, table):
1028 #print 'attach',table.label 1029 self.tb_by_name[table.label] = table 1030 tbi = self.table_names.index(table.label) 1031 table.OnInsert += self.table_events[tbi][0] 1032 table.OnRemoved += self.table_events[tbi][1] 1033 table.OnSet += self.table_events[tbi][2] 1034 table.OnAddField += self.table_events[tbi][3] 1035 self.__colors[table.label] = [table.get(i, 'Group') for i in xrange(table.size)] 1036 self.__reset_stats(table.label) 1037 for field_name in table.fields: 1038 if self.vals[table.label][field_name, False] == self.missing: 1039 self.vals[table.label][field_name, False] = memoize(bind_with_args(self.__vals, table.label, field_name, False)) 1040 else: 1041 self.vals[table.label][field_name, False].reset() 1042 self.v_fields[table.label].reset() 1043 self.v_names[table.label].reset() 1044 self.vvv[table.label].reset() 1045 for plot in self.plots: 1046 if plot.table_name == table.label: 1047 plot.table = table 1048 plot.colors = self.__colors[table.label] 1049 plot.get_count = table.count_in_group 1050 if table.label in self.x_means: 1051 plot = self.x_means[table.label] 1052 if 'x_means_ssd' in table.__dict__: 1053 plot.ssd = table.x_means_ssd 1054 plot.update_plot() 1055 else: 1056 table.x_means_ssd = plot.ssd
1057 - def __detach_table(self, table):
1058 #print 'detach',table.label 1059 if not (table.label in self.tb_by_name): 1060 return 1061 del self.tb_by_name[table.label] 1062 tbi = self.table_names.index(table.label) 1063 table.OnInsert -= self.table_events[tbi][0] 1064 table.OnRemoved -= self.table_events[tbi][1] 1065 table.OnSet -= self.table_events[tbi][2] 1066 table.OnAddField -= self.table_events[tbi][3] 1067 self.__reset_stats(table.label) 1068 for plot in self.plots: 1069 if plot.table_name == table.label: 1070 plot.table = None 1071 plot.get_count = lambda g: None 1072 plot.invalidate() 1073 if table.label in self.x_means: 1074 self.x_means[table.label].clear(detach=True)
1075 - def __onAddTable(self, table):
1076 for plot in self.plots: 1077 if plot.table_name == table.label: 1078 self.__attach_table(table) 1079 break
1080 - def __onRemoveTable(self, table):
1081 if table.label in self.table_names: 1082 self.__detach_table(table)
1083 - def __onInsert(self, table_name, i, undoing):
1084 self.__reset_stats(table_name) 1085 colors = self.__colors[table_name] 1086 colors.insert(i, self.tb_by_name[table_name].get(i, 'Group')) 1087 for plot in self.plots: 1088 plot.colors = colors 1089 if table_name in self.x_means: 1090 self.x_means[table_name].clear()
1091 - def __onRemoved(self, table_name, i, undoing):
1092 self.__reset_stats(table_name) 1093 colors = self.__colors[table_name] 1094 del colors[i] 1095 for plot in self.plots: 1096 plot.colors = colors 1097 if table_name in self.x_means: 1098 self.x_means[table_name].clear()
1099 - def __onSet(self, table_name, i, field_name, val, prev, undoing):
1100 if field_name == 'Group': 1101 self.__reset_stats(table_name) 1102 colors = self.__colors[table_name] 1103 colors[i] = val 1104 for plot in self.plots: 1105 if plot.table_name == table_name: 1106 plot.colors = colors 1107 else: 1108 self.__reset_stats(table_name, field_name) 1109 for plot in self.__plots_using_field[table_name][field_name]: 1110 plot.invalidate() 1111 if table_name in self.x_means: 1112 self.x_means[table_name].clear()
1113 - def __onAddField(self, table_name, field_name):
1114 if self.vals[table_name][field_name, False] == self.missing: 1115 self.vals[table_name][field_name, False] = memoize(bind_with_args(self.__vals, table_name, field_name, False)) 1116 else: 1117 self.vals[table_name][field_name, False].reset() 1118 for is_log in (False, True): 1119 if (field_name, is_log) in self.__fields[table_name]: 1120 self.__reset_stats(table_name, field_name, is_log) 1121 for plot in self.__plots_using_field[table_name][field_name]: 1122 plot.invalidate()
1123 - def __onClickClosePlot(self, plot):
1124 qubx.pyenv.env.OnScriptable('%s.remove_plot(%s)' % (self.global_name, plot.global_name)) 1125 gobject.idle_add(self.remove_plot, plot)
1126 - def __onKeyPress(self, event):
1127 if ord('0') <= event.keyval <= ord('9'): 1128 self.palette.color = event.keyval - ord('0') 1129 return 1130 if event.keyval in (ord('m'), ord('M')): 1131 self.mnuActions.do_popup() 1132 return
1133 - def __onShowOne(self, plot, ix):
1134 plot.table.select(ix, sender=self)
1135 - def __onDelOne(self, plot, ix):
1136 qubx.pyenv.env.scriptable_if_matching('%s.remove(%i)' % (plot.table.global_name, ix), 1137 [(plot.table.global_name, plot.table)]) 1138 plot.table.remove(ix)
1139 - def __onSelectOne(self, plot, ix):
1140 if self.__colors[plot.table_name][ix] != self.palette.color: 1141 qubx.pyenv.env.OnScriptable('%s.set_color(table_name=%s, ix=%s, color=%s)' % 1142 (self.global_name, repr(plot.table_name), repr(ix), repr(self.palette.color))) 1143 self.set_color(plot.table_name, ix, self.palette.color) 1144 else: 1145 qubx.pyenv.env.OnScriptable('%s.set_color(table_name=%s, ix=%s, color=0)' % 1146 (self.global_name, repr(plot.table_name), repr(ix))) 1147 self.set_color(plot.table_name, ix, 0)
1148 - def __onSelectMany(self, plot, ixs):
1149 colors = [self.palette.color]*len(ixs) 1150 qubx.pyenv.env.OnScriptable('%s.set_colors(table_name=%s, color_list=%s, index_list=%s)' % 1151 (self.global_name, repr(plot.table_name), repr(colors), repr(ixs))) 1152 self.set_colors(plot.table_name, colors, ixs)
1153 - def __onChangePlotLayerset(self, plot, layerset):
1154 if layerset == plot.controls: 1155 self.fit_ix = self.plots.index(plot) 1156 else: 1157 self.fit_ix = None
1158 - def set_fit_ix(self, ix):
1159 if not (ix is None): 1160 self.grid.hide() 1161 self.__fit_grid_ix = self.grid.remove(self.plots[ix], temporary=True) 1162 self.panLeftRight.pack_start(self.plots[ix], True, True) 1163 else: 1164 self.panLeftRight.remove(self.plots[self.__fit_ix]) 1165 self.grid.pack_aspect(self.plots[self.__fit_ix], self.__fit_grid_ix) 1166 self.grid.show_all() 1167 self.__fit_ix = ix
1168 fit_ix = property(lambda self: self.__fit_ix, lambda self, ix: self.set_fit_ix(ix), 1169 "warning: must alternate between None and a legit index")
1170 - def __onChangeXMeansLayerset(self, plot, layerset):
1171 if layerset == plot.controls: 1172 self.xmeans_fit_ix = self.x_means_plots.index(plot) 1173 else: 1174 self.xmeans_fit_ix = None
1175 - def set_xmeans_fit_ix(self, ix):
1176 if not (ix is None): 1177 self.grid.hide() 1178 self.__xmeans_fit_grid_ix = self.grid.remove(self.x_means_plots[ix], temporary=True) 1179 self.panLeftRight.pack_start(self.x_means_plots[ix], True, True) 1180 else: 1181 self.panLeftRight.remove(self.x_means_plots[self.__xmeans_fit_ix]) 1182 self.grid.pack_aspect(self.x_means_plots[self.__xmeans_fit_ix], self.__xmeans_fit_grid_ix) 1183 self.grid.show_all() 1184 self.__xmeans_fit_ix = ix
1185 xmeans_fit_ix = property(lambda self: self.__xmeans_fit_ix, lambda self, ix: self.set_xmeans_fit_ix(ix), 1186 "warning: must alternate between None and a legit index")
1187 - def onShow(self, showing):
1188 if not showing: 1189 if not (self.__fit_ix is None): 1190 self.plots[self.__fit_ix].layerset = self.plots[self.__fit_ix].controlsHidden 1191 if not (self.__xmeans_fit_ix is None): 1192 self.x_means_plots[self.__xmeans_fit_ix].layerset = self.x_means_plots[self.__xmeans_fit_ix].controlsHidden
1193 - def __onClickCloseXMeans(self, plot):
1194 qubx.pyenv.env.OnScriptable('%s.remove_x_means_plot(%s)' % (self.global_name, repr(plot.table_name))) 1195 self.remove_x_means_plot(plot.table_name)
1196 - def __onClickPointXMeans(self, plot, k):
1197 qubx.pyenv.env.OnScriptable('%s.cluster_x_means_k(%s, %s)' % 1198 (self.global_name, repr(plot.table_name), repr(k))) 1199 self.cluster_x_means_k(plot.table_name, k)
1200 - def __onClickRefreshXMeans(self, plot):
1201 if len(plot.xx): 1202 klo = min(plot.xx) 1203 khi = max(plot.xx) 1204 else: 1205 klo = self.__xmeans_klo 1206 khi = self.__xmeans_khi 1207 self.cluster_x_means(plot.table_name, klo, khi, plot.trials_per_k, plot.weight_expr)
1208 1209
1210 -def rect_increasing(x0, y0, x1, y1):
1211 if x0 > x1: 1212 x0, x1 = x1, x0 1213 if y0 > y1: 1214 y0, y1 = y1, y0 1215 return x0, y0, x1, y1
1216
1217 -def distance(x1, y1, x2, y2):
1218 """Returns the Euclidean distance between (x1, y1) and (x2, y2).""" 1219 return sqrt((x2 - x1)**2 + (y2 - y1)**2)
1220
1221 -class SelectCursor(qubx.fit_space.FitSpaceCursor):
1222 - def __init__(self, space):
1223 qubx.fit_space.FitSpaceCursor.__init__(self, space) 1224 self.__ref = Reffer() 1225 self.cursor = gdk.Cursor(gdk.CROSSHAIR) 1226 self.sel_rect = None 1227 self.mnuDot = gtk.Menu() 1228 build_menuitem('Show in Tables', self.__ref(lambda item: self.space.OnShowOne(self.space, self.press_ix)), menu=self.mnuDot) 1229 build_menuitem('Delete from Table', self.__ref(lambda item: self.space.OnDelOne(self.space, self.press_ix)), menu=self.mnuDot)
1230 - def onRoll(self, x, y, e):
1231 qubx.fit_space.FitSpaceCursor.onRoll(self, x, y, e) 1232 if self.sel_rect: 1233 self.sel_rect = None 1234 self.space.redraw_canvas()
1235 - def onPress(self, x, y, e):
1236 qubx.fit_space.FitSpaceCursor.onPress(self, x, y, e) 1237 self.press_ix = self.space.dotmap.find(x, y, self.space.dot_rad_pix) 1238 self.press_pt = (x, y) 1239 if self.press_ix >= 0: 1240 self.__menuWaiting = True 1241 gobject.timeout_add(MENU_TIMEOUT, self.__onMenuTimeout, 1242 Anon(x=e.x, y=e.y, button=e.button, time=e.time))
1243 - def onDrag(self, x, y, e):
1244 self.update_coords(x, y) 1245 xpress, ypress = self.press_pt 1246 if self.press_ix >= 0: 1247 self.__menuWaiting = self.__menuWaiting and (distance(xpress, ypress, x, y) <= MENU_STRAYDIUS) 1248 else: 1249 self.sel_rect = rect_increasing(xpress, ypress, x, y) 1250 self.space.redraw_canvas()
1251 - def onRelease(self, x, y, e):
1252 qubx.fit_space.FitSpaceCursor.onRelease(self, x, y, e) 1253 if self.press_ix >= 0: 1254 self.space.OnSelectOne(self.space, self.press_ix) 1255 elif self.sel_rect: 1256 indices = self.space.dotmap.in_rect(*self.sel_rect) 1257 if indices: 1258 self.space.OnSelectMany(self.space, indices) 1259 self.space.redraw_canvas() 1260 self.press_ix = -1 1261 self.sel_rect = None 1262 self.__menuWaiting = False
1263 - def __onMenuTimeout(self, e):
1264 if self.__menuWaiting: 1265 self.press_ix = -1 1266 self.space.release_button(e) 1267 self.onNeedPopup(e.x, e.y, e)
1268 - def onNeedPopup(self, x, y, e):
1269 self.press_ix = self.space.dotmap.find(x, y, self.space.dot_rad_pix) 1270 if self.press_ix >= 0: 1271 self.mnuDot.popup(None, None, None, 0, e.time)
1272 - def onOverlay(self, context, w, h):
1273 if self.sel_rect: 1274 x0, y0, x1, y1 = self.sel_rect 1275 context.set_source_rgba(*qubx.toolspace.SETALPHA(self.space.appearance.color(qubx.modelGTK.COLOR_CLASS(self.space.palette.color)), DRAG_ALPHA)) 1276 context.rectangle(x0, y0, x1-x0, y1-y0) 1277 context.fill_preserve() 1278 context.set_source_rgba(*qubx.toolspace.SETALPHA(self.space.appearance.color(qubx.modelGTK.COLOR_CLASS(self.space.palette.color)), DRAG_BORDER_ALPHA)) 1279 context.stroke()
1280 1281
1282 -class SelectPlot(qubx.fit_space.FitSpace):
1283 - def __init__(self, label='Select', **kw):
1284 qubx.fit_space.FitSpace.__init__(self, label=label, **kw) 1285 self.__ref = Reffer() 1286 self.layerset = self.controlsHidden 1287 self.tool_select = SelectCursor(self) 1288 self.data_width = 0.0 # no lines connecting dots 1289 self.draw_fit_when_hidden = False 1290 self.extra_rad = self.extra_width = 0 1291 self.subCaption.w = -3 1292 self.OnClickClose = WeakEvent() # (SelectPlot) 1293 self.OnShowOne = WeakEvent() # (SelectPlot, ix) 1294 self.OnDelOne = WeakEvent() # (SelectPlot, ix) 1295 self.OnSelectOne = WeakEvent() # (SelectPlot, ix) 1296 self.OnSelectMany = WeakEvent() # (SelectPlot [ix]) 1297 self.OnDraw += self.__ref(self.on_overlay) 1298 self.OnOverlay += self.__ref(self.__onOverlay) 1299 self.as_preset = qubx.tree.Node('SelectPlot') # replace with e.g. OnePlot as in __onPreSet above 1300 self.__colors = [] 1301 self.__serial = 0 1302 self.fields = [] 1303 self.palette = None # updated by face 1304 self.dot_rad_pix = 1 # updated in draw 1305 self.dotmap = qubx.fast.clickmap.DotMap() 1306 self.__table = None # assiged by SelectFace when it's available 1307 self.__fitall = None 1308 self.__mnuGroup = None 1309 self.__mnuGroupIgnore = False 1310 self.subClose = qubx.toolspace.SubLayer_Label('X', 0, 1, x=-1.5, w=1.5, h=qubx.fit_space.LINE_EMS, 1311 color=qubx.fit_space.COLOR_FITSPACE_BUTTON, 1312 action=self.__ref(self.__onClickClose)) 1313 self.layCaption.add_sublayer(self.subClose) 1314 1315 self.layTools = qubx.toolspace.Layer_Toolbar(dim=1, x=self.layZoom.rq_x, y=self.layZoom.rq_y+self.layZoom.rq_h, w=self.layZoom.rq_w) 1316 self.add_layer(self.layTools) 1317 self.layTools.add_tool('Pick Scroll cursor', qubx.toolspace.SubLayer_PanIcon(), qubx.fit_space.FitSpaceCursor(self)) 1318 self.layTools.add_tool('Pick Select cursor', SubLayer_SelectIcon(), self.tool_select, active=True) 1319 1320 self.layNotebook = qubx.toolspace.Layer(x=1, y=-5.5-qubx.fit_space.LINE_EMS, w=2, h=2, cBG=qubx.toolspace.COLOR_CLEAR) 1321 self.subNotebook = qubx.notebookGTK.SubLayer_Notebook(x=0, y=0, w=2, h=2) 1322 self.layNotebook.add_sublayer(self.subNotebook) 1323 self.add_layer(self.layNotebook) 1324 self.nbChart = qubx.notebook.NbChart('Chart', self.global_name and ('%s.nbChart'%self.global_name) or '', 1325 lambda: self.caption, 1326 self.nb_get_shape, self.nb_get_headers, 1327 get_col=self.nb_get_col, get_type=lambda: float, 1328 get_series=self.nb_get_series, 1329 get_colors=self.nb_get_colors, get_color_indices=self.nb_get_color_indices) 1330 self.subNotebook.items.append(self.nbChart) 1331 self.nbPicture = qubx.notebookGTK.NbPicture('Picture', self.global_name and ('%s.nbPicture'%self.global_name) or '', 1332 self.nb_picture_get_shape, 1333 self.nb_picture_draw) 1334 self.subNotebook.items.append(self.nbPicture) 1335 self.nbStats = qubx.notebook.NbDynText('Group stats', self.global_name and ('%s.nbStats'%self.global_name) or '', 1336 self.nb_get_group_stats) 1337 self.subNotebook.items.append(self.nbStats) 1338 self.nbParams = qubx.notebook.NbDynText('Parameters', self.global_name and ('%s.nbParams'%self.global_name) or '', 1339 self.controls.nb_get_param_text) 1340 self.nbParams.std_err_est = None 1341 self.controls.robot.OnExpr += self.__ref(self.__onExpr) 1342 self.controls.robot.OnParam += self.__ref(self.__onParam) 1343 self.controls.robot.OnStats += self.__ref(self.__onStats) 1344 self.controls.robot.OnIteration += self.__ref(self.__onIteration) 1345 self.controls.robot.OnEndFit += self.__ref(self.__onEndFit) 1346 self.OnChangeLayerset += self.__ref(self.__onChangeLayerset) 1347 self.__nb_next_stats = False
1348 table = property(lambda self: self.__table, lambda self, x: self.set_table(x))
1349 - def set_table(self, x):
1350 if not (self.__fitall is None): 1351 self.__fitall.OnSet -= self.__ref(self.__onSetFitAll) 1352 self.OnChangeLayerset -= self.__ref(self.__onChangeLayerset) 1353 self.__table = x 1354 self.__fitall = x.custom.fitall if (not (x is None)) else None 1355 if not (self.__fitall is None): 1356 self.__fitall.OnSet += self.__ref(self.__onSetFitAll) 1357 self.OnChangeLayerset += self.__ref(self.__onChangeLayerset) 1358 self.__fitall_index = x.custom.fitall_index 1359 self.draw_fit_when_hidden = True 1360 if not self.__mnuGroup: 1361 self.__mnuGroup = gtk.Menu() 1362 self.__itemGroup = [ 1363 build_menuitem('0: good fit', self.__ref(bind(self.__onItemGroup, 0)), menu=self.__mnuGroup, item_class=gtk.CheckMenuItem), 1364 build_menuitem('1: fit again', self.__ref(bind(self.__onItemGroup, 1)), menu=self.__mnuGroup, item_class=gtk.CheckMenuItem), 1365 build_menuitem('2: discard', self.__ref(bind(self.__onItemGroup, 2)), menu=self.__mnuGroup, item_class=gtk.CheckMenuItem) 1366 ] 1367 self.layTools.add_tool('FitAll: assign group', qubx.toolspace.SubLayer_Popup(self.__mnuGroup, qubx.toolspace.COLOR_WHITE, on_popup=self.__ref(self.__onPopupGroup), w=2.5, h=2.5))
1368 - def __onSetFitAll(self, index, field, val, prev, undoing):
1369 if (field == 'Group') and (index == self.__fitall_index): 1370 self.redraw_canvas(False)
1371 - def __onPopupGroup(self, *args):
1372 group = self.__fitall[self.__fitall_index, 'Group'] 1373 self.__mnuGroupIgnore = True 1374 for i, item in enumerate(self.__itemGroup): 1375 item.set_active(i == group) 1376 self.__mnuGroupIgnore = False 1377 return True
1378 - def __onItemGroup(self, g):
1379 if not self.__mnuGroupIgnore: 1380 self.__fitall[self.__fitall_index, 'Group'] = g
1381 - def __onChangeLayerset(self, selfy, layerset):
1382 if layerset == self.controls: 1383 self.fitall.select(self.__fitall_index, sender=self)
1384 - def __onClickClose(self, x, y, e):
1385 self.OnClickClose(self)
1386 - def set_colors(self, x):
1387 self.__colors = x 1388 self.invalidate()
1389 colors = property(lambda self: self.__colors, lambda self, x: self.set_colors(x))
1390 - def invalidate(self):
1391 self.__serial += 1 1392 gobject.idle_add(self.__update_plot, self.__serial)
1393 - def __update_plot(self, serial):
1394 if serial != self.__serial: 1395 return 1396 self.update_plot()
1397 - def __onChangeLayerset(self, selfy, layerset):
1398 if layerset == self.controls: 1399 self.subNotebook.items.append(self.nbParams) 1400 else: 1401 self.subNotebook.items.remove(self.nbParams)
1402 - def __onExpr(self, curve_name, expr, params, param_vals, lo, hi, can_fit):
1403 self.__param_names = params 1404 self.__param_vals = param_vals 1405 self.__param_active = can_fit
1406 - def __onParam(self, index, name, value, lo, hi, can_fit):
1407 self.__param_names[index] = name # paranoid? 1408 self.__param_vals[index] = value 1409 self.__param_active[index] = can_fit
1410 - def __onIteration(self, param_vals, iteration):
1411 self.__param_vals = param_vals
1412 - def __onEndFit(self):
1413 self.__nb_next_stats = True
1414 - def __onStats(self, correlation, is_pseudo, std_err_est, ssr, r2, runs_prob):
1415 if self.__nb_next_stats: 1416 self.__nb_next_stats = False 1417 n = len(self.get_yy()) 1418 caption = self.caption 1419 qubx.notebook.Notebook.send(qubx.notebook.NbText("""%(caption)s fitting finished. 1420 \tN: %(n)i 1421 \tSSR: %(ssr).6g 1422 \tR^2: %(r2).6g 1423 \tWald-Wolfowitz runs probability: %(runs_prob).6g 1424 \tCorrelation: 1425 %(correlation)s 1426 """ % locals()), auto_id='Select.Results') 1427 self.nbParams.std_err_est = std_err_est 1428 qubx.notebook.Notebook.send(self.nbParams, auto_id='Select.Parameters') 1429 qubx.notebook.Notebook.send(self.nbChart, auto_id='Select.Chart') 1430 qubx.notebook.Notebook.send(self.nbPicture, auto_id='Select.Picture') 1431 self.nbParams.std_err_est = None
1432 - def __onOverlay(self, context, w, h):
1433 if self.__fitall: 1434 group = self.__fitall[self.__fitall_index, 'Group'] 1435 if group: 1436 context.set_source_rgba(*qubx.toolspace.SETALPHA(self.appearance.color(qubx.modelGTK.COLOR_CLASS(group)), 0.2)) 1437 context.paint()
1438 - def on_overlay(self, context, w, h):
1439 raise Exception('unimplemented abstract method')
1440 - def nb_get_shape(self):
1441 return self.controls.nb_get_shape()
1442 - def nb_get_headers(self):
1443 return self.controls.nb_get_headers()
1444 - def nb_get_col(self, c):
1445 return self.controls.nb_get_col(c)
1446 - def nb_get_colors(self):
1447 return [self.appearance.color(qubx.modelGTK.COLOR_CLASS(c)) for c in xrange(10)]
1448 - def nb_get_color_indices(self):
1449 return self.colors
1450 - def nb_get_group_stats(self):
1451 return self.caption+":\n"+"\n".join(["%4d: %.6g +/- %.6g (%d)" % (c, self.get_mean(c), self.get_std(c), self.get_count(c)) for c in sorted(list(set(self.colors)))])
1452 - def update_plot(self):
1453 raise Exception('unimplemented abstract method')
1454 1455
1456 -class OnePlot(SelectPlot):
1457 - def __init__(self, table_name, field_name, is_log, ylabel, ind_name, ind_is_log, xlabel, get_v_names, get_vvv, get_xx, get_yy, get_mean, get_std, get_count, **kw):
1458 SelectPlot.__init__(self, 'Chart.%s'%ylabel, **kw) 1459 self.__ref = Reffer() 1460 self.as_preset.name = 'OnePlot' 1461 self.as_preset['Table'].data = self.table_name = table_name 1462 self.as_preset['Field'].data = self.field_name = field_name 1463 self.as_preset['IsLog'].data = self.is_log = is_log 1464 self.as_preset['Ind'].data = self.ind_name = ind_name 1465 self.as_preset['IndIsLog'].data = self.ind_is_log = ind_is_log 1466 self.get_v_names = get_v_names 1467 self.get_vvv = get_vvv 1468 self.get_xx = get_xx 1469 self.get_yy = get_yy 1470 self.get_mean = get_mean 1471 self.get_std = get_std 1472 self.get_count = get_count 1473 if xlabel == 'Index': 1474 self.caption = ylabel 1475 else: 1476 self.caption = '%s v. %s' % (ylabel, xlabel) 1477 self.fields = [(field_name, is_log)] 1478 self.invalidate()
1479 - def update_plot(self):
1480 xx, yy = self.get_xx(), self.get_yy() 1481 if len(xx) != len(yy): 1482 xx = yy = [] 1483 self.controls.robot.set_data(xx, yy, self.get_vvv(), self.get_v_names())
1484 - def on_overlay(self, context, w, h):
1485 context.set_source_rgb(.1,.1,.1) 1486 context.set_line_width(2.0) 1487 context.rectangle(0,0,w,h) 1488 context.stroke() 1489 lookup = self.appearance.color 1490 color = qubx.modelGTK.COLOR_CLASS 1491 t2x, d2y = self.t2x, self.d2y 1492 rad = self.dot_rad_pix = self.data_rad*self.appearance.emsize 1493 twopi = 2*pi 1494 xx, yy = self.get_xx(), self.get_yy() 1495 for x, y, c in izip(xx, yy, self.colors): 1496 context.set_source_rgba(*lookup(color(c))) 1497 context.arc(t2x(x), d2y(y), rad, 0, twopi) 1498 context.fill() 1499 if self.table: # and (self.layerset == self.controlsHidden): 1500 for c in self.table.groups_occupied(): 1501 mean, std = self.get_mean(c), self.get_std(c) 1502 context.set_source_rgba(*qubx.toolspace.SETALPHA(lookup(color(c)), CENTROID_ALPHA)) 1503 context.move_to(0, d2y(mean-std)) 1504 context.line_to(w, d2y(mean-std)) 1505 context.line_to(w, d2y(mean+std)) 1506 context.line_to(0, d2y(mean+std)) 1507 context.fill() 1508 self.dotmap.reset([t2x(x) for x in xx], [d2y(y) for y in yy])
1509 1510 1511
1512 -class TwoPlot(SelectPlot):
1513 - def __init__(self, table_name, field_name_x, field_name_y, is_log_x, is_log_y, xlabel, ylabel, get_v_names, get_vvv, get_xx, get_yy, get_mean_x, get_mean_y, get_std_x, get_std_y, get_count, **kw):
1514 SelectPlot.__init__(self, 'Chart.%s.v.%s' % (xlabel, ylabel), **kw) # yes the label is backwards, too late to change it 1515 self.as_preset.name = 'TwoPlot' 1516 self.as_preset['Table'].data = self.table_name = table_name 1517 self.as_preset['FieldX'].data = self.field_name_x = field_name_x 1518 self.as_preset['FieldY'].data = self.field_name_y = field_name_y 1519 self.as_preset['IsLogX'].data = self.is_log_x = is_log_x 1520 self.as_preset['IsLogY'].data = self.is_log_y = is_log_y 1521 self.get_v_names = get_v_names 1522 self.get_vvv = get_vvv 1523 self.get_xx = get_xx 1524 self.get_yy = get_yy 1525 self.get_mean_x = get_mean_x 1526 self.get_mean_y = get_mean_y 1527 self.get_std_x = get_std_x 1528 self.get_std_y = get_std_y 1529 self.get_count = get_count 1530 self.caption = '%s v. %s' % (ylabel, xlabel) 1531 self.fields = [(field_name_x, is_log_x), (field_name_y, is_log_y)] 1532 self.invalidate()
1533 - def update_plot(self):
1534 xx, yy = self.get_xx(), self.get_yy() 1535 if len(xx) != len(yy): 1536 xx = yy = [] 1537 self.controls.robot.set_data(xx, yy, self.get_vvv(), self.get_v_names())
1538 - def on_overlay(self, context, w, h):
1539 context.set_source_rgb(.1,.1,.1) 1540 context.set_line_width(2.0) 1541 context.rectangle(0,0,w,h) 1542 context.stroke() 1543 lookup = self.appearance.color 1544 color = qubx.modelGTK.COLOR_CLASS 1545 t2x, d2y = self.t2x, self.d2y 1546 rad = self.dot_rad_pix = self.data_rad*self.appearance.emsize 1547 twopi = 2*pi 1548 xx, yy = self.get_xx(), self.get_yy() 1549 for x, y, c in izip(xx, yy, self.colors): 1550 context.set_source_rgba(*lookup(color(c))) 1551 context.arc(t2x(x), d2y(y), rad, 0, twopi) 1552 context.fill() 1553 if self.table: # and (self.layerset == self.controlsHidden): 1554 for c in self.table.groups_occupied(): 1555 mean_x, std_x = self.get_mean_x(c), self.get_std_x(c) 1556 mean_y, std_y = self.get_mean_y(c), self.get_std_y(c) 1557 if std_x and std_y: 1558 context.save() 1559 context.translate(t2x(mean_x), d2y(mean_y)) 1560 context.scale(t2x(mean_x+std_x)-t2x(mean_x), 1561 d2y(mean_y-std_y)-d2y(mean_y)) 1562 context.set_source_rgba(*qubx.toolspace.SETALPHA(lookup(color(c)), CENTROID_ALPHA)) 1563 context.arc(0, 0, 1.0, 0, 2*pi) 1564 context.fill() 1565 context.restore() 1566 self.dotmap.reset([t2x(x) for x in xx], [d2y(y) for y in yy])
1567 - def nb_get_group_stats(self):
1568 return self.caption+":\n"+"\n".join(["%4d: %.6g +/- %.6g\t,\t%.6g +/- %.6g\t(%d)" % 1569 (c, self.get_mean_x(c), self.get_std_x(c), self.get_mean_y(c), self.get_std_y(c), self.get_count(c)) 1570 for c in sorted(list(set(self.colors)))])
1571
1572 -class HistPlot(SelectPlot):
1573 - def __init__(self, table_name, field_name, is_log, bin_count, ylabel, get_yy, get_mean, get_std, get_count, **kw):
1574 SelectPlot.__init__(self, 'Chart.Hist.%s'%ylabel, **kw) 1575 self.draw_hist = True 1576 self.hist_width = 0.95 1577 self.as_preset.name = 'HistPlot' 1578 self.as_preset['Table'].data = self.table_name = table_name 1579 self.as_preset['Field'].data = self.field_name = field_name 1580 self.as_preset['IsLog'].data = self.is_log = is_log 1581 self.as_preset['BinCount'].data = self.bin_count = bin_count 1582 self.get_yy = get_yy 1583 self.get_mean = get_mean 1584 self.get_std = get_std 1585 self.get_count = get_count 1586 self.caption = ylabel 1587 self.xx = self.yy = self.stacking = [] 1588 self.fields = [(field_name, is_log)] 1589 self.nbHist = qubx.notebook.NbChart('Histogram', self.global_name and ('%s.nbHist'%self.global_name) or '', 1590 lambda: self.caption, 1591 self.controls.nb_get_shape, self.nb_hist_get_headers, 1592 get_col=self.nb_get_col, get_type=lambda: float, 1593 get_series=self.nb_hist_get_series, 1594 get_colors=self.nb_get_colors, get_color_indices=self.nb_get_color_indices) 1595 self.subNotebook.items.append(self.nbHist) 1596 self.invalidate()
1597 - def update_plot(self):
1598 self.xx, self.yy, self.stacking = AllPointsHistogram(self.get_yy(), self.bin_count) 1599 self.controls.robot.set_data(self.xx, self.yy)
1600 - def on_overlay(self, context, w, h):
1601 context.set_source_rgb(.1,.1,.1) 1602 context.set_line_width(2.0) 1603 context.rectangle(0,0,w,h) 1604 context.stroke() 1605 if not len(self.xx): 1606 return 1607 lookup = self.appearance.color 1608 color = qubx.modelGTK.COLOR_CLASS 1609 t2x, d2y = self.t2x, self.d2y 1610 em = self.appearance.emsize 1611 rad = self.dot_rad_pix = min(self.data_rad*em, w/(2*len(self.xx))) 1612 twopi = 2*pi 1613 for x, y, c in izip(self.get_yy(), self.stacking, self.colors): 1614 context.set_source_rgba(*lookup(color(c))) 1615 context.arc(t2x(x), d2y(y), rad, 0, twopi) 1616 context.fill() 1617 if self.table: # and (self.layerset == self.controlsHidden): 1618 border = qubx.fit_space.LINE_EMS*em 1619 for c in self.table.groups_occupied(): 1620 mean, std = self.get_mean(c), self.get_std(c) 1621 context.set_source_rgba(*qubx.toolspace.SETALPHA(lookup(color(c)), CENTROID_ALPHA)) 1622 context.move_to(t2x(mean-std), border) 1623 context.line_to(t2x(mean-std), h-border) 1624 context.line_to(t2x(mean+std), h-border) 1625 context.line_to(t2x(mean+std), border) 1626 context.fill() 1627 self.dotmap.reset([t2x(x) for x in self.get_yy()], [d2y(y) for y in self.stacking])
1628 - def nb_hist_get_headers(self):
1629 return ['Bins', self.caption] + self.controls.nb_get_headers()[2:]
1630 - def nb_hist_get_series(self):
1631 return [qubx.notebook.NbChartSeries(0, 1, ser_type=qubx.notebook.HISTOGRAM, nrow=len(self.yy)), 1632 qubx.notebook.NbChartSeries(0, 2, ser_type=qubx.notebook.LINES, nrow=len(self.yy))]
1633 - def nb_get_shape(self):
1634 nr, nc = self.controls.nb_get_shape() 1635 return (max(nr, len(self.stacking)), nc+3)
1636 - def nb_get_headers(self):
1637 return ['X', 'Y', 'Color', 'Bins', self.caption] + self.controls.nb_get_headers()[2:]
1638 - def nb_get_col(self, c):
1639 if c == 0: 1640 return self.get_yy() 1641 elif c == 1: 1642 return self.stacking 1643 elif c == 2: 1644 return self.colors 1645 else: 1646 return self.controls.nb_get_col(c-3)
1647 - def nb_get_series(self):
1648 return [qubx.notebook.NbChartSeries(0, 1, ser_type=qubx.notebook.DOTS, nrow=len(self.stacking)), 1649 qubx.notebook.NbChartSeries(3, 4, ser_type=qubx.notebook.HISTOGRAM, nrow=len(self.controls.xx)), 1650 qubx.notebook.NbChartSeries(3, 5, ser_type=qubx.notebook.LINES, nrow=len(self.yy))]
1651 1652
1653 -def make_bins(lo, hi, n):
1654 """Returns (bins, bin_width) as (numpy.array[n], float).""" 1655 d = (hi - lo) 1656 one = d/n 1657 bins = arange(n, dtype='float64') 1658 bins *= one 1659 bins += lo + one/2 1660 return bins, one
1661
1662 -def AllPointsHistogram(yy, bin_count):
1663 if len(yy) < 2: 1664 return [], [], [] 1665 lo = min(yy) 1666 bins, bin_width = make_bins(lo, max(yy), bin_count) 1667 bars = zeros(shape=(bin_count,)) 1668 stacking = zeros(shape=(len(yy),)) 1669 if len(yy) and bin_count: 1670 one = 1.0 / len(yy) 1671 last = bin_count - 1 1672 data = array(yy) 1673 data -= lo 1674 data /= bin_width 1675 for i,x in enumerate(data.astype('int32')): 1676 ibar = max(0, min(last, x)) 1677 stacking[i] = bars[ibar] + one/2 1678 bars[ibar] += one 1679 return bins, bars, stacking
1680
1681 -def FieldLabel(table, field_name, is_log):
1682 field_label = field_name 1683 if is_log: 1684 field_label = 'log10(%s)' % field_label 1685 if table and (field_name in table.units) and table.units[field_name]: 1686 return '%s [%s]' % (field_label, table.units[field_name]) 1687 return field_label
1688 1689
1690 -class CriteriaEntry(gtk.HBox):
1691 __explore_featured = ['recent', 'txtCriteria', 'btnHelp', 'mnuRecent', 'btnRecent', 'table', 'get_rows', 'acceptCriteria', 'update_recent']
1692 - def __init__(self, recent_tree):
1693 gtk.HBox.__init__(self) 1694 self.recent = recent_tree 1695 self.__table = None 1696 self.__ref = Reffer() 1697 1698 pack_label('Pick rows which have', self) 1699 self.txtCriteria = pack_item(qubx.GTK.NumEntry('', self.acceptCriteria), self, expand=True) 1700 self.btnHelp = pack_item(qubx.pyenvGTK.HelpTestButton(self.txtCriteria), self) 1701 self.btnHelp.caption = "Enter an expression in terms of field_name which evaluates True or False" 1702 self.btnHelp.help_msg = CRITERIA_HELP 1703 self.btnHelp.bind = lambda expr: qubx.pyenv.env.globals.__setitem__('rows_meeting_criteria', lambda: rows_meeting_criteria(self.table, expr)) 1704 self.btnHelp.write_test = lambda expr: 'print rows_meeting_criteria()' 1705 self.mnuRecent = gtk.Menu() 1706 self.btnRecent = pack_item(qubx.toolspace.Button_Popup(self.mnuRecent, tooltip='Recent...', on_popup=self.__ref(self.__onPopupRecent), color=COLOR_CRITERIA_POPUP), self)
1707 - def set_table(self, x):
1708 self.__table = x 1709 self.txtCriteria.onParse(True)
1710 table = property(lambda self: self.__table, lambda self, x: self.set_table(x))
1711 - def get_rows(self):
1712 return rows_meeting_criteria(self.__table, self.txtCriteria.value)
1713 - def acceptCriteria(self, s):
1714 indices = rows_meeting_criteria(self.table, s, 1) 1715 return s
1716 - def __onPopupRecent(self):
1717 self.mnuRecent.foreach(lambda item: self.mnuRecent.remove(item)) 1718 for entry in qubx.tree.children(self.recent): 1719 build_menuitem(entry.name, self.__ref(bind(self.txtCriteria.setValue, entry.name, parse=True)), menu=self.mnuRecent) 1720 return True
1721 - def update_recent(self):
1722 if self.txtCriteria.value: 1723 self.__add_recent(self.recent, self.txtCriteria.value)
1724 - def __add_recent(self, tree, entry):
1725 if not entry: 1726 return 1727 rec = tree.find(entry) 1728 if not rec.isNull: 1729 tree.remove(rec) 1730 tree.insert(rec) 1731 else: 1732 tree.insert(qubx.tree.Node(entry)) 1733 rec = tree.child 1734 for i in xrange(RECENT_COUNT): 1735 rec = rec.sibling 1736 if rec.isNull: 1737 break 1738 while not rec.isNull: 1739 next = rec.sibling 1740 tree.remove(rec) 1741 rec = next
1742
1743 -class CriteriaDialog(gtk.Dialog):
1744 - def __init__(self, tables, recent):
1745 QubX = qubx.pyenv.env.globals['QubX'] 1746 gtk.Dialog.__init__(self, "%s - Select by criteria"%QubX.appname, qubx.GTK.get_active_window(), gtk.DIALOG_MODAL) 1747 self.set_size_request(500, -1) 1748 self.tables = tables 1749 self.table = tables[0] 1750 self.__ref = Reffer() 1751 line = pack_item(gtk.HBox(True), self.vbox) 1752 pack_label('Table:', line) 1753 self.mnuTable = pack_item(qubx.GTK.StaticComboList([table.label for table in tables]), line) 1754 self.mnuTable.OnChange += self.__ref(self.__onChangeTable) 1755 self.criteria = pack_item(CriteriaEntry(recent), self.vbox) 1756 self.criteria.table = self.table 1757 self.add_button('Cancel', gtk.RESPONSE_REJECT) 1758 self.add_button('Select', gtk.RESPONSE_ACCEPT)
1759 - def run(self, expr):
1760 self.criteria.txtCriteria.value = expr 1761 if (gtk.Dialog.run(self) == gtk.RESPONSE_ACCEPT) and self.criteria.txtCriteria.good: 1762 self.criteria.update_recent() 1763 return self.criteria.txtCriteria.value, rows_meeting_criteria(self.criteria.table, self.criteria.txtCriteria.value) 1764 else: 1765 return self.criteria.txtCriteria.value, []
1766 - def __onChangeTable(self, mnu, name):
1767 self.criteria.table = self.tables[mnu.active_i]
1768 1769
1770 -def rows_meeting_criteria(table, expr, row_count=None):
1771 return table.rows_meeting_criteria(expr, row_count=row_count)
1772 1773 CRITERIA_HELP = """Enter a boolean expression such as 1774 (Group == 1) and (Amp_1 > 2.0) 1775 1776 These names are available: 1777 field_name -- val. of field in this row; replace spaces with underscores; match capitalization 1778 row["field name"] -- value of field in this row 1779 field_mean("field name") -- mean value of field 1780 field_std("field name") -- standard deviation of field 1781 field_median("field name") -- median value of field 1782 field_mode("field name") -- most common value of field 1783 group_mean("field name", c) -- mean value of field, group (color) c only 1784 group_std("field name", c) -- standard deviation of field, group (color) c only 1785 group_median("field name", c) -- median value of field, group (color) c only 1786 group_mode("field name", c) -- most common value of field, group (color) c only 1787 1788 Expressions use Python syntax. Full documentation is available at www.python.org. 1789 """ + qubx.pyenv.PYTHON_HELP 1790 1791
1792 -class XMeansCursor(qubx.fit_space.FitSpaceCursor):
1793 - def onPress(self, x, y, e):
1794 qubx.fit_space.FitSpaceCursor.onPress(self, x, y, e) 1795 self.press_ix = self.space.dotmap.find(x, y, self.space.dot_rad_pix)
1796 - def onRelease(self, x, y, e):
1797 qubx.fit_space.FitSpaceCursor.onRelease(self, x, y, e) 1798 if self.press_ix >= 0: 1799 self.space.OnClickPoint(self.space, self.space.xx[self.press_ix]) 1800 self.press_ix = -1
1801
1802 -class XMeansPlot(qubx.fit_space.FitSpace):
1803 - def __init__(self, table_name, **kw):
1804 qubx.fit_space.FitSpace.__init__(self, label='X-Means SSD', **kw) 1805 self.caption = '%s: X-Means SSD' % table_name 1806 self.table_name = table_name 1807 self.__ref = Reffer() 1808 self.layerset = self.controlsHidden 1809 self.tool = XMeansCursor(self) 1810 self.data_width = 0.0 # no lines connecting dots 1811 self.draw_fit_when_hidden = False 1812 self.subCaption.w = -3 1813 self.OnClickClose = WeakEvent() # (XMeansPlot) 1814 self.OnClickPoint = WeakEvent() # (XMeansPlot, ix) 1815 self.OnClickRefresh = WeakEvent() # (XMeansPlot) 1816 self.OnDraw += self.__ref(self.on_overlay) 1817 self.dot_rad_pix = 1 # updated in draw 1818 self.dotmap = qubx.fast.clickmap.DotMap() 1819 self.subClose = qubx.toolspace.SubLayer_Label('X', 0, 1, x=-1.5, w=1.5, h=qubx.fit_space.LINE_EMS, 1820 color=qubx.fit_space.COLOR_FITSPACE_BUTTON, 1821 action=self.__ref(self.__onClickClose)) 1822 self.layCaption.add_sublayer(self.subClose) 1823 1824 self.layRefresh = qubx.toolspace.Layer(x=-9, y=6, w=8, h=1+qubx.fit_space.LINE_EMS, cBG=self.controls.cLayer) 1825 self.subRefresh = qubx.toolspace.SubLayer_Label('Refresh', 0, 1, y=.5, w=8, h=qubx.fit_space.LINE_EMS, color=self.controls.cButton, 1826 action=self.__ref(lambda x,y,e: self.OnClickRefresh(self))) 1827 self.layRefresh.add_sublayer(self.subRefresh) 1828 self.add_layer(self.layRefresh) 1829 1830 self.layShow = qubx.toolspace.Layer(x=-15, y=4, w=14, h=qubx.fit_space.LINE_EMS, cBG=self.controls.cLayer) 1831 self.subShowSSD = qubx.toolspace.SubLayer_Radio(on_radio=self.__ref(self.__onRadioShow), label='SSD', justify=-1, vcenter=1, 1832 x=0, y=0, w=7, h=qubx.fit_space.LINE_EMS, 1833 color=self.controls.cText, hover_color=self.controls.cText) 1834 self.layShow.add_sublayer(self.subShowSSD) 1835 self.subShowAICc = qubx.toolspace.SubLayer_Radio(fellow=self.subShowSSD, label='AICc', justify=-1, vcenter=1, 1836 x=7, y=0, w=7, h=qubx.fit_space.LINE_EMS, 1837 color=self.controls.cText, hover_color=self.controls.cText) 1838 self.layShow.add_sublayer(self.subShowAICc) 1839 self.add_layer(self.layShow) 1840 1841 self.layHint = qubx.toolspace.Layer(x=-37, y=2.25, w=32, h=qubx.fit_space.LINE_EMS, cBG=qubx.toolspace.COLOR_CLEAR) 1842 self.subHint = qubx.toolspace.SubLayer_Label('Click a point to show the k-clustering', 1, 1, w=32, h=qubx.fit_space.LINE_EMS, 1843 color=self.controls.cText, hover_color=self.controls.cText) 1844 self.layHint.add_sublayer(self.subHint) 1845 self.add_layer(self.layHint) 1846 1847 self.layNotebook = qubx.toolspace.Layer(x=1, y=-5.5-qubx.fit_space.LINE_EMS, w=2, h=2, cBG=qubx.toolspace.COLOR_CLEAR) 1848 self.subNotebook = qubx.notebookGTK.SubLayer_Notebook(x=0, y=0, w=2, h=2) 1849 self.layNotebook.add_sublayer(self.subNotebook) 1850 self.add_layer(self.layNotebook) 1851 self.nbChart = qubx.notebook.NbChart('Chart', self.global_name and ('%s.nbChart'%self.global_name) or '', 1852 lambda: self.caption, 1853 self.controls.nb_get_shape, self.controls.nb_get_headers, 1854 get_col=self.controls.nb_get_col, get_type=lambda: float, 1855 get_series=self.nb_get_series) 1856 self.subNotebook.items.append(self.nbChart) 1857 self.nbPicture = qubx.notebookGTK.NbPicture('Picture', self.global_name and ('%s.nbPicture'%self.global_name) or '', 1858 self.nb_picture_get_shape, 1859 self.nb_picture_draw) 1860 self.subNotebook.items.append(self.nbPicture) 1861 self.ssd = collections.defaultdict(lambda: 0.0) 1862 self.aicc = collections.defaultdict(lambda: 0.0) 1863 self.__xx = self.__yy = [] 1864 self.weight_expr = "" 1865 self.trials_per_k = 300 1866 self.__show_aicc = False
1867 xx = property(lambda self: self.__xx) 1868 yy = property(lambda self: self.__yy)
1869 - def set_show_aicc(self, x):
1870 if x == self.__show_aicc: return 1871 self.__show_aicc = x 1872 self.caption = '%s: X-Means %s' % (self.table_name, 1873 x and 'AICc' or 'SSD') 1874 if x: 1875 self.subShowAICc.active = True 1876 else: 1877 self.subShowSSD.active = True 1878 self.update_plot()
1879 show_aicc = property(lambda self: self.__show_aicc, lambda self, x: self.set_show_aicc(x))
1880 - def __onRadioShow(self, sub):
1881 self.show_aicc = (sub == self.subShowAICc)
1882 - def __onClickClose(self, x, y, e):
1883 self.OnClickClose(self)
1884 - def set_ssd(self, k, ssd, LL, aicc, Ndata, Nvar):
1885 self.ssd[k] = ssd 1886 self.aicc[k] = aicc 1887 self.update_plot()
1888 - def clear(self, detach=False):
1889 if detach: 1890 self.ssd = collections.defaultdict(lambda: 0.0) 1891 else: 1892 self.ssd.clear() 1893 self.update_plot()
1894 - def update_plot(self):
1895 keys = self.ssd.keys() 1896 if keys: 1897 self.__xx = list(range(min(keys), max(keys)+1)) 1898 if self.__show_aicc: 1899 self.__yy = [self.aicc[x] for x in self.__xx] 1900 else: 1901 self.__yy = [self.ssd[x] for x in self.__xx] 1902 else: 1903 self.__xx = self.__yy = [] 1904 self.controls.robot.set_data(self.__xx, self.__yy)
1905 - def on_overlay(self, context, w, h):
1906 context.set_source_rgb(.1,.1,.1) 1907 context.set_line_width(2.0) 1908 context.rectangle(0,0,w,h) 1909 context.stroke() 1910 # actually just storing the geometry for clicking 1911 self.dot_rad_pix = self.data_rad*self.appearance.emsize 1912 t2x, d2y = self.t2x, self.d2y 1913 xx, yy = self.__xx, self.__yy 1914 self.dotmap.reset([t2x(x) for x in xx], [d2y(y) for y in yy])
1915 1916 XMEANS_WEIGHT_HELP = """Enter an expression such as 1917 Weight 1918 or 1.0 / Std_1 1919 or Std_1 and (1.0 / Std_1) or 0.0 (avoids division by zero) 1920 1921 These names are available: 1922 field_name -- val. of field in this row; replace spaces with underscores; match capitalization 1923 row["field name"] -- value of field in this row 1924 1925 Expressions use Python syntax. Full documentation is available at www.python.org. 1926 """ + qubx.pyenv.PYTHON_HELP 1927 1928
1929 -def draw_select_icon(cr, w, h):
1930 w = int(w) 1931 h = int(h) 1932 margin = min(w, h)/6 1933 cr.save() 1934 cr.set_line_width(1.0) 1935 cr.set_source_rgba(1, .3, .3, .5) 1936 cr.rectangle(margin, margin, w-3*margin, h/2) 1937 cr.fill_preserve() 1938 cr.set_source_rgba(1, .3, .3, 1) 1939 cr.stroke() 1940 cr.restore()
1941
1942 -class SubLayer_SelectIcon(qubx.toolspace.SubLayer_Icon):
1943 - def draw(self, context, w, h, appearance):
1944 qubx.toolspace.SubLayer_Icon.draw(self, context, w, h, appearance) 1945 draw_select_icon(context, self.w, self.h)
1946