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

Source Code for Module qubx.select

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