class StatisticalAnalysisDialog(wx.Dialog): rel_cov = None def __init__(self, parent, model): wx.Dialog.__init__(self, parent, style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.MAXIMIZE_BOX) vbox = wx.BoxSizer(wx.VERTICAL) self.SetSizer(vbox) self.SetTitle('Statistical Analysis of Parameters') dpi_scale_factor = wx.GetApp().dpi_scale_factor self.ptxt = wx.StaticText(self, label="...") self.pbar = wx.Gauge(self, range=1000) hbox = wx.BoxSizer(wx.HORIZONTAL) vbox.Add(hbox, proportion=1, flag=wx.EXPAND) lpanel = wx.Panel(self) gpanel = wx.Panel(self) gbox = wx.BoxSizer(wx.VERTICAL) gpanel.SetSizer(gbox) self.grid = Grid(gpanel) gbox.Add(wx.StaticText(gpanel, label='Estimated covariance matrix:'), proportion=0, flag=wx.FIXED_MINSIZE) gbox.Add(self.grid, proportion=1, flag=wx.EXPAND) self.normalize_checkbox = wx.CheckBox( gpanel, label='Normalize value (σ_ij/σ_i/σ_j)') self.normalize_checkbox.SetValue(True) self.normalize_checkbox.Bind(wx.EVT_CHECKBOX, self.OnToggleNormalize) gbox.Add(self.normalize_checkbox, proportion=0, flag=wx.FIXED_MINSIZE) nfparams = len([pi for pi in model.parameters if pi.fit]) self.grid.CreateGrid(nfparams + 1, nfparams) self.grid.SetColLabelTextOrientation(wx.VERTICAL) self.grid.SetColLabelSize(int(dpi_scale_factor * 80)) for i in range(nfparams): self.grid.SetRowSize(i, int(dpi_scale_factor * 80)) self.grid.SetColSize(i, int(dpi_scale_factor * 80)) self.grid.SetColLabelValue(i, '%i' % i) self.grid.SetRowLabelValue(i + 1, '%i' % i) self.grid.SetRowSize(nfparams, int(dpi_scale_factor * 80)) self.grid.DisableCellEditControl() self.grid.SetMinSize( (int(dpi_scale_factor * 200), int(dpi_scale_factor * 200))) self.grid.Bind(EVT_GRID_CELL_LEFT_DCLICK, self.OnSelectCell) rpanel = PlotPanel(self) rpanel.SetMinSize( (int(dpi_scale_factor * 200), int(dpi_scale_factor * 200))) self.plot_panel = rpanel self.ax = rpanel.figure.add_subplot(111) hbox.Add(lpanel, proportion=0, flag=wx.EXPAND | wx.ALIGN_TOP) hbox.Add(gpanel, proportion=1, flag=wx.EXPAND) hbox.Add(rpanel, proportion=1, flag=wx.EXPAND) lsizer = wx.GridSizer(vgap=1, hgap=2, cols=2) lpanel.SetSizer(lsizer) self.entries = {} for key, emin, emax, val in [('pop', 1, 20 * nfparams, 2 * nfparams), ('samples', 1000, 10000000, 10000), ('burn', 0, 10000, 200)]: lsizer.Add(wx.StaticText(lpanel, label='%s:' % key), flag=wx.FIXED_MINSIZE) self.entries[key] = wx.SpinCtrl(lpanel, wx.ID_ANY, min=emin, max=emax, value=str(val)) lsizer.Add(self.entries[key], flag=wx.FIXED_MINSIZE) lsizer.AddStretchSpacer(10) but = wx.Button(self, label='Run Analysis...') vbox.Add(but) vbox.Add(self.ptxt, proportion=0, flag=wx.EXPAND) vbox.Add(self.pbar, proportion=0, flag=wx.EXPAND) self.Bind(wx.EVT_BUTTON, self.OnRunAnalysis, but) self.Bind(wx.EVT_CLOSE, self.OnClose) self.model = model self.thread = None psize = parent.GetSize() self.SetSize(int(psize.GetWidth() * 0.75), int(psize.GetHeight() * 0.75)) def OnRunAnalysis(self, event): if self.thread is not None: return self.thread = threading.Thread(target=self.run_bumps) self.thread.start() def run_bumps(self): self.bproblem = self.model.bumps_problem() mon = ProgressMonitor(self.bproblem, self.pbar, self.ptxt) pop = self.entries['pop'].GetValue() burn = self.entries['burn'].GetValue() samples = self.entries['samples'].GetValue() self.pbar.SetRange( int(samples / (len(self.bproblem.model_parameters()) * pop)) + burn) res = self.model.bumps_fit(method='dream', pop=pop, samples=samples, burn=burn, thin=1, alpha=0, outliers='none', trim=False, monitors=[mon], problem=self.bproblem) self._res = res wx.CallAfter(self.display_bumps) def display_bumps(self): self.thread.join(timeout=5.0) self.thread = None self.pbar.SetValue(0) res = self._res self.draw = res.state.draw() self.abs_cov = res.cov self.rel_cov = res.cov / res.dx[:, newaxis] / res.dx[newaxis, :] rel_max = [0, 1, abs(self.rel_cov[0, 1])] if self.normalize_checkbox.IsChecked(): display_cov = self.rel_cov fmt = "%.6f" else: display_cov = self.abs_cov fmt = "%.4g" self.grid.SetRowLabelValue(0, 'Value/Error:') for i, ci in enumerate(self.rel_cov): self.grid.SetColLabelValue(i, self.draw.labels[i]) self.grid.SetRowLabelValue(i + 1, self.draw.labels[i]) self.grid.SetCellValue(0, i, "%.8g\n%.4g" % (res.x[i], res.dx[i])) self.grid.SetCellAlignment(0, i, wx.ALIGN_CENTRE, wx.ALIGN_CENTRE) self.grid.SetReadOnly(0, i) self.grid.SetCellBackgroundColour(0, i, "#cccccc") for j, cj in enumerate(ci): self.grid.SetCellValue(i + 1, j, fmt % display_cov[i, j]) self.grid.SetReadOnly(i + 1, j) self.grid.SetCellAlignment(i + 1, j, wx.ALIGN_CENTRE, wx.ALIGN_CENTRE) if i == j: self.grid.SetCellBackgroundColour(i + 1, j, "#888888") elif abs(cj) > 0.4: self.grid.SetCellBackgroundColour(i + 1, j, "#ffcccc") elif abs(cj) > 0.3: self.grid.SetCellBackgroundColour(i + 1, j, "#ffdddd") elif abs(cj) > 0.2: self.grid.SetCellBackgroundColour(i + 1, j, "#ffeeee") if i != j and abs(cj) > rel_max[2]: rel_max = [min(i, j), max(i, j), abs(cj)] self.hists = _hists(self.draw.points.T, bins=50) fig = self.plot_panel.figure fig.clear() ax = fig.add_subplot(111) data, x, y = self.hists[(rel_max[0], rel_max[1])] vmin, vmax = data[data > 0].min(), data.max() ax.pcolorfast(y, x, maximum(vmin, data), norm=LogNorm(vmin, vmax), cmap='inferno') ax.set_xlabel(self.draw.labels[rel_max[1]]) ax.set_ylabel(self.draw.labels[rel_max[0]]) self.plot_panel.flush_plot() # add analysis data do model for later storange in export header exdict = {} exdict['library'] = 'bumps' exdict['version'] = bumps_version exdict['settings'] = dict(pop=self.entries['pop'].GetValue(), burn=self.entries['burn'].GetValue(), samples=self.entries['samples'].GetValue()) exdict['parameters'] = [ dict(name=li, value=float(xi), error=float(dxi), cross_correlations=dict( (self.draw.labels[j], float(res.cov[i, j])) for j in range(len(res.x)))) for i, (li, xi, dxi) in enumerate(zip(self.draw.labels, res.x, res.dx)) ] self.model.extra_analysis['statistics_mcmc'] = exdict def OnToggleNormalize(self, evt): if self.rel_cov is None: evt.Skip() return if self.normalize_checkbox.IsChecked(): display_cov = self.rel_cov fmt = "%.6f" else: display_cov = self.abs_cov fmt = "%.4g" for i, ci in enumerate(self.rel_cov): for j, cj in enumerate(ci): self.grid.SetCellValue(i + 1, j, fmt % display_cov[i, j]) def OnSelectCell(self, evt): i, j = evt.GetCol(), evt.GetRow() - 1 if i == j or j == -1: return elif i > j: itmp = i i = j j = itmp fig = self.plot_panel.figure fig.clear() ax = fig.add_subplot(111) data, x, y = self.hists[(i, j)] vmin, vmax = data[data > 0].min(), data.max() ax.pcolorfast(y, x, maximum(vmin, data), norm=LogNorm(vmin, vmax), cmap='inferno') ax.set_xlabel(self.draw.labels[j]) ax.set_ylabel(self.draw.labels[i]) self.plot_panel.flush_plot() def OnClose(self, event): if self.thread is not None and self.thread.is_alive(): # a running bumps simulation can't be interrupted from other thread, stop window from closing self.ptxt.SetLabel("Simulation running") event.Veto() return event.Skip()
class PanelMeasure(wx.Panel): def __init__(self, graph, settings): wx.Panel.__init__(self, graph) self.spectrum = None self.graph = graph self.settings = settings self.measure = None self.checked = {Measure.MIN: "", Measure.MAX: "", Measure.AVG: "", Measure.GMEAN: "", Measure.HBW: "", Measure.OBW: ""} self.selected = None self.SetBackgroundColour('white') self.grid = Grid(self) self.grid.CreateGrid(3, 19) self.grid.EnableEditing(True) self.grid.EnableDragGridSize(False) self.grid.SetColLabelSize(1) self.grid.SetRowLabelSize(1) self.grid.SetColMinimalAcceptableWidth(1) self.grid.SetColSize(2, 1) self.grid.SetColSize(7, 1) self.grid.SetColSize(11, 1) self.grid.SetColSize(15, 1) self.grid.SetMargins(0, wx.SystemSettings.GetMetric(wx.SYS_HSCROLL_Y)) for x in range(self.grid.GetNumberRows()): self.grid.SetRowLabelValue(x, '') for y in range(self.grid.GetNumberCols()): self.grid.SetColLabelValue(y, '') for row in range(self.grid.GetNumberRows()): for col in range(self.grid.GetNumberCols()): self.grid.SetReadOnly(row, col, True) self.locsDesc = {'F Start': (0, 0), 'F End': (1, 0), 'F Delta': (2, 0), 'P Min': (0, 4), 'P Max': (1, 4), 'P Delta': (2, 4), 'Mean': (0, 9), 'GMean': (1, 9), 'Flatness': (2, 9), '-3dB Start': (0, 13), '-3dB End': (1, 13), '-3dB Delta': (2, 13), 'OBW Start': (0, 17), 'OBW End': (1, 17), 'OBW Delta': (2, 17)} self.__set_descs() self.locsCheck = {Measure.MIN: (0, 3), Measure.MAX: (1, 3), Measure.AVG: (0, 8), Measure.GMEAN: (1, 8), Measure.HBW: (0, 12), Measure.OBW: (0, 16)} self.__set_check_editor() self.locsFreq = [(0, 1), (1, 1)] self.__set_freq_editor() colour = self.grid.GetBackgroundColour() self.grid.SetCellTextColour(2, 3, colour) self.grid.SetCellTextColour(2, 8, colour) self.grid.SetCellTextColour(1, 12, colour) self.grid.SetCellTextColour(2, 12, colour) self.grid.SetCellTextColour(1, 16, colour) self.grid.SetCellTextColour(2, 16, colour) self.__clear_checks() self.locsMeasure = {'start': (0, 1), 'end': (1, 1), 'deltaF': (2, 1), 'minFP': (0, 5), 'maxFP': (1, 5), 'deltaFP': (2, 5), 'minP': (0, 6), 'maxP': (1, 6), 'deltaP': (2, 6), 'avg': (0, 10), 'gmean': (1, 10), 'flat': (2, 10), 'hbwstart': (0, 14), 'hbwend': (1, 14), 'hbwdelta': (2, 14), 'obwstart': (0, 18), 'obwend': (1, 18), 'obwdelta': (2, 18)} fontCell = self.grid.GetDefaultCellFont() fontSize = fontCell.GetPointSize() fontStyle = fontCell.GetStyle() fontWeight = fontCell.GetWeight() font = wx.Font(fontSize, wx.FONTFAMILY_MODERN, fontStyle, fontWeight) dc = wx.WindowDC(self.grid) dc.SetFont(font) widthMHz = dc.GetTextExtent('###.######')[0] * 1.2 widthdB = dc.GetTextExtent('-##.##')[0] * 1.2 for _desc, (_row, col) in self.locsDesc.items(): self.grid.AutoSizeColumn(col) for col in [1, 5, 14, 18]: self.grid.SetColSize(col, widthMHz) for row in range(self.grid.GetNumberRows()): self.grid.SetCellFont(row, col, font) for col in [6, 10]: self.grid.SetColSize(col, widthdB) for row in range(self.grid.GetNumberRows()): self.grid.SetCellFont(row, col, font) for _desc, (_row, col) in self.locsCheck.items(): self.grid.AutoSizeColumn(col) toolTips = {self.locsMeasure['start']: 'Selection start (MHz)', self.locsMeasure['end']: 'Selection end (MHz)', self.locsMeasure['deltaF']: 'Selection bandwidth (MHz)', self.locsMeasure['minFP']: 'Minimum power location (MHz)', self.locsMeasure['maxFP']: 'Maximum power location (MHz)', self.locsMeasure['deltaFP']: 'Power location difference (MHz)', self.locsMeasure['minP']: 'Minimum power (dB)', self.locsMeasure['maxP']: 'Maximum power (dB)', self.locsMeasure['deltaP']: 'Power difference (dB)', self.locsMeasure['avg']: 'Mean power (dB)', self.locsMeasure['gmean']: 'Geometric mean power (dB)', self.locsMeasure['flat']: 'Spectral flatness', self.locsMeasure['hbwstart']: '-3db start location (MHz)', self.locsMeasure['hbwend']: '-3db end location (MHz)', self.locsMeasure['hbwdelta']: '-3db bandwidth (MHz)', self.locsMeasure['obwstart']: '99% start location (MHz)', self.locsMeasure['obwend']: '99% end location (MHz)', self.locsMeasure['obwdelta']: '99% bandwidth (MHz)'} self.toolTips = GridToolTips(self.grid, toolTips) self.popupMenu = wx.Menu() self.popupMenuCopy = self.popupMenu.Append(wx.ID_ANY, "&Copy", "Copy entry") self.Bind(wx.EVT_MENU, self.__on_copy, self.popupMenuCopy) self.Bind(EVT_GRID_CELL_RIGHT_CLICK, self.__on_popup_menu) self.Bind(EVT_GRID_CELL_LEFT_CLICK, self.__on_cell_click) if wx.VERSION >= (3, 0, 0, 0): self.Bind(EVT_GRID_CELL_CHANGED, self.__on_cell_change) box = wx.BoxSizer(wx.VERTICAL) box.Add(self.grid, 0, wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border=10) self.SetSizer(box) def __set_descs(self): font = self.grid.GetCellFont(0, 0) font.SetWeight(wx.FONTWEIGHT_BOLD) for desc, (row, col) in self.locsDesc.items(): self.grid.SetCellValue(row, col, desc) self.grid.SetCellFont(row, col, font) def __set_check_editor(self): for _desc, (row, col) in self.locsCheck.items(): self.grid.SetCellEditor(row, col, GridCellBoolEditor()) self.grid.SetCellAlignment(row, col, wx.ALIGN_RIGHT, wx.ALIGN_CENTRE) self.grid.SetCellRenderer(row, col, CheckBoxCellRenderer(self)) def __set_freq_editor(self): for (row, col) in self.locsFreq: self.grid.SetReadOnly(row, col, False) self.grid.SetCellAlignment(row, col, wx.ALIGN_RIGHT, wx.ALIGN_CENTRE) self.grid.SetCellEditor(row, col, GridCellFloatEditor(precision=4)) def __set_check_value(self, cell, value): (row, col) = self.locsCheck[cell] self.grid.SetCellValue(row, col, value) def __set_measure_value(self, cell, value): (row, col) = self.locsMeasure[cell] try: self.grid.SetCellValue(row, col, value) except TypeError: pass def __set_check_enable(self, cell, enable): (row, col) = self.locsCheck[cell] renderer = self.grid.GetCellRenderer(row, col) renderer.Enable(not enable) def __get_checks(self): checks = {} for cell in self.checked: if self.checked[cell] == '1': checks[cell] = True else: checks[cell] = False return checks def __update_checks(self): for cell in self.checked: self.__set_check_value(cell, self.checked[cell]) def __clear_checks(self): for cell in self.checked: self.checked[cell] = '0' self.__update_checks() def __on_cell_click(self, event): self.grid.ClearSelection() row = event.GetRow() col = event.GetCol() if (row, col) in self.locsCheck.values(): if self.grid.GetCellRenderer(row, col).enabled and self.measure is not None: check = self.grid.GetCellValue(row, col) if check == '1': check = '0' else: check = '1' self.grid.SetCellValue(row, col, check) for control, (r, c) in self.locsCheck.items(): if (r, c) == (row, col): self.checked[control] = check if self.selected is None: self.selected = self.locsMeasure['start'] row = self.selected[0] col = self.selected[1] self.grid.SetGridCursor(row, col) self.update_measure() elif (row, col) in self.locsMeasure.values(): self.selected = (row, col) self.grid.SetGridCursor(row, col) elif self.selected is None: self.selected = self.locsMeasure['start'] row = self.selected[0] col = self.selected[1] self.grid.SetGridCursor(row, col) def __on_cell_change(self, event): row = event.GetRow() col = event.GetCol() if (row, col) in self.locsFreq: start = None end = None try: start = float(self.grid.GetCellValue(self.locsFreq[0][0], self.locsFreq[0][1])) except ValueError: pass try: end = float(self.grid.GetCellValue(self.locsFreq[1][0], self.locsFreq[1][1])) except ValueError: pass if start is None and end is None: return elif start is None and end is not None: start = end - 1 elif start is not None and end is None: end = start + 1 if start > end: swap = start start = end end = swap self.graph.set_selected(start, end) self.set_selected(self.spectrum, start, end) def __on_popup_menu(self, _event): if self.selected: self.popupMenuCopy.Enable(True) else: self.popupMenuCopy.Enable(False) self.PopupMenu(self.popupMenu) def __on_copy(self, _event): value = self.grid.GetCellValue(self.selected[0], self.selected[1]) clip = wx.TextDataObject(value) wx.TheClipboard.Open() wx.TheClipboard.SetData(clip) wx.TheClipboard.Close() def update_measure(self): show = self.__get_checks() self.graph.update_measure(self.measure, show) def clear_measurement(self): for control in self.locsMeasure: self.__set_measure_value(control, "") self.__clear_checks() self.update_measure() self.measure = None def set_selected(self, spectrum, start, end): self.spectrum = spectrum if start is None: return self.measure = Measure(spectrum, start, end) if not self.measure.is_valid(): self.clear_measurement() return minF, maxF = self.measure.get_f() minP = self.measure.get_min_p() maxP = self.measure.get_max_p() avgP = self.measure.get_avg_p() gMeanP = self.measure.get_gmean_p() flatness = self.measure.get_flatness() hbw = self.measure.get_hpw() obw = self.measure.get_obw() self.__set_measure_value('start', format_precision(self.settings, minF, units=False)) self.__set_measure_value('end', format_precision(self.settings, maxF, units=False)) self.__set_measure_value('deltaF', format_precision(self.settings, maxF - minF, units=False)) self.__set_measure_value('minFP', format_precision(self.settings, minP[0], units=False)) self.__set_measure_value('maxFP', format_precision(self.settings, maxP[0], units=False)) self.__set_measure_value('deltaFP', format_precision(self.settings, maxP[0] - minP[0], units=False)) self.__set_measure_value('minP', format_precision(self.settings, level=minP[1], units=False)) self.__set_measure_value('maxP', format_precision(self.settings, level=maxP[1], units=False)) self.__set_measure_value('deltaP', format_precision(self.settings, level=maxP[1] - minP[1], units=False)) self.__set_measure_value('avg', format_precision(self.settings, level=avgP, units=False)) self.__set_measure_value('gmean', format_precision(self.settings, level=gMeanP, units=False)) self.__set_measure_value('flat', "{0:.4f}".format(flatness)) text = "" if hbw[0] is not None: text = format_precision(self.settings, hbw[0], units=False) else: text = '' self.__set_measure_value('hbwstart', text) if hbw[1] is not None: text = format_precision(self.settings, hbw[1], units=False) else: text = '' self.__set_measure_value('hbwend', text) if hbw[0] is not None and hbw[1] is not None: text = format_precision(self.settings, hbw[1] - hbw[0], units=False) else: text = '' self.__set_measure_value('hbwdelta', text) if obw[0] is not None: text = format_precision(self.settings, obw[0], units=False) else: text = '' self.__set_measure_value('obwstart', text) if obw[1] is not None: text = format_precision(self.settings, obw[1], units=False) else: text = '' self.__set_measure_value('obwend', text) if obw[0] is not None and obw[1] is not None: text = format_precision(self.settings, obw[1] - obw[0], units=False) else: text = '' self.__set_measure_value('obwdelta', text) self.update_measure() def show(self, show): if show: self.Show() else: self.Hide() self.Layout() def set_type(self, display): for cell in self.locsCheck: self.__set_check_enable(cell, True) if display == Display.PLOT: for cell in self.locsCheck: self.__set_check_enable(cell, False) elif display == Display.SPECT: self.__set_check_enable(Measure.HBW, False) self.__set_check_enable(Measure.OBW, False) self.grid.Refresh()