forked from IPKM/nmreval
fitresult window is reused and remembers fitplot range/log; closes #65
add color to sub-functions in fit result window Fitresult window shows one set at a time, more space for plot; closes #66
This commit is contained in:
@ -27,70 +27,76 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
|
||||
self.extrapolate_box.stateChanged.connect(lambda x: self.minx_line.setEnabled(x))
|
||||
self.extrapolate_box.stateChanged.connect(lambda x: self.numx_line.setEnabled(x))
|
||||
|
||||
self._prevs = {}
|
||||
self._models = {}
|
||||
self._previous_fits = {}
|
||||
self._opts = []
|
||||
self._results = {}
|
||||
self.graph_opts = {}
|
||||
self.last_idx = None
|
||||
|
||||
for res in results:
|
||||
idx = res.idx
|
||||
data_k = management.data[idx]
|
||||
|
||||
if res.name not in self._models:
|
||||
self._models[res.name] = []
|
||||
|
||||
self._models[res.name].append(idx)
|
||||
|
||||
self._prevs[idx] = []
|
||||
for fit in data_k.get_fits():
|
||||
self._prevs[idx].append((fit.name, fit.statistics, fit.nobs-fit.nvar))
|
||||
|
||||
self._results = {res.idx: res for res in results}
|
||||
self._opts = [(False, False) for _ in range(len(self._results))]
|
||||
|
||||
self.residplot = self.graphicsView.addPlot(row=0, col=0)
|
||||
self.resid_graph = PlotItem(x=[], y=[],
|
||||
symbol='o', symbolPen=None, symbolBrush=mkBrush(color=(31, 119, 180)),
|
||||
pen=None)
|
||||
self.resid_graph_imag = PlotItem(x=[], y=[],
|
||||
symbol='s', symbolPen=None, symbolBrush=mkBrush(color=(255, 127, 14)),
|
||||
pen=None)
|
||||
self.residplot.addItem(self.resid_graph)
|
||||
self.residplot.addItem(self.resid_graph_imag)
|
||||
self.residplot.setLabel('left', 'Residual')
|
||||
self.resid_plot.addItem(self.resid_graph)
|
||||
self.resid_plot.addItem(self.resid_graph_imag)
|
||||
self.resid_plot.setLabel('left', 'Residual')
|
||||
|
||||
self.fitplot = self.graphicsView.addPlot(row=1, col=0)
|
||||
self.data_graph = PlotItem(x=[], y=[],
|
||||
symbol='o', symbolPen=None, symbolBrush=mkBrush(color=(31, 119, 180)),
|
||||
pen=None)
|
||||
self.data_graph_imag = PlotItem(x=[], y=[],
|
||||
symbol='s', symbolPen=None, symbolBrush=mkBrush(color=(255, 127, 14)),
|
||||
pen=None)
|
||||
self.fitplot.addItem(self.data_graph)
|
||||
self.fitplot.addItem(self.data_graph_imag)
|
||||
self.fitplot.setLabel('left', 'Function')
|
||||
self.fit_plot.addItem(self.data_graph)
|
||||
self.fit_plot.addItem(self.data_graph_imag)
|
||||
self.fit_plot.setLabel('left', 'Function')
|
||||
|
||||
self.fit_graph = PlotItem(x=[], y=[])
|
||||
self.fit_graph_imag = PlotItem(x=[], y=[])
|
||||
self.fitplot.addItem(self.fit_graph)
|
||||
self.fitplot.addItem(self.fit_graph_imag)
|
||||
self.fit_plot.addItem(self.fit_graph)
|
||||
self.fit_plot.addItem(self.fit_graph_imag)
|
||||
|
||||
self.cmap = RdBuCMap(vmin=-1, vmax=1)
|
||||
|
||||
self.sets_comboBox.blockSignals(True)
|
||||
for n in self._models.keys():
|
||||
self.sets_comboBox.addItem(n)
|
||||
self.sets_comboBox.blockSignals(False)
|
||||
|
||||
self.set_parameter(0)
|
||||
self.buttonBox.accepted.connect(self.accept)
|
||||
|
||||
self.param_tableWidget.itemClicked.connect(self.show_results)
|
||||
self.param_tableWidget.horizontalHeader().sectionClicked.connect(lambda i: self.show_results(None, idx=i))
|
||||
|
||||
self.graph_checkBox.stateChanged.connect(lambda x: self.graph_comboBox.setEnabled(x == QtCore.Qt.Unchecked))
|
||||
|
||||
self.logy_box.stateChanged.connect(lambda x: self.fitplot.setLogMode(y=bool(x)))
|
||||
self.logx_box.stateChanged.connect(lambda x: self.fitplot.setLogMode(x=bool(x)))
|
||||
self.residplot.setXLink(self.fitplot)
|
||||
self.logy_box.stateChanged.connect(lambda x: self.fit_plot.setLogMode(y=bool(x)))
|
||||
self.logx_box.stateChanged.connect(lambda x: self.fit_plot.setLogMode(x=bool(x)))
|
||||
self.resid_plot.setXLink(self.fit_plot)
|
||||
|
||||
self.set_results(results)
|
||||
|
||||
def __call__(self, results: list):
|
||||
self._previous_fits = {}
|
||||
self.sets_comboBox.blockSignals(True)
|
||||
self.sets_comboBox.clear()
|
||||
self.sets_comboBox.blockSignals(False)
|
||||
self._results = {}
|
||||
self._opts = {}
|
||||
|
||||
self.set_results(results)
|
||||
|
||||
def set_results(self, results: list):
|
||||
self.sets_comboBox.blockSignals(True)
|
||||
for res in results:
|
||||
idx = res.idx
|
||||
data_k = self._management.data[idx]
|
||||
|
||||
self._previous_fits[idx] = []
|
||||
for fit in data_k.get_fits():
|
||||
self._previous_fits[idx].append((fit.name, fit.statistics, fit.nobs - fit.nvar))
|
||||
|
||||
self.sets_comboBox.addItem(data_k.name, userData=idx)
|
||||
self.sets_comboBox.blockSignals(False)
|
||||
|
||||
self._results = {res.idx: res for res in results}
|
||||
self._opts = [(False, False) for _ in range(len(self._results))]
|
||||
|
||||
self.set_parameter(0)
|
||||
|
||||
def add_graphs(self, graphs: list):
|
||||
self.graph_comboBox.clear()
|
||||
@ -99,56 +105,40 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
|
||||
|
||||
@QtCore.pyqtSlot(int, name='on_sets_comboBox_currentIndexChanged')
|
||||
def set_parameter(self, idx: int):
|
||||
model_name = self.sets_comboBox.itemText(idx)
|
||||
sets = self._models[model_name]
|
||||
self.param_tableWidget.setColumnCount(len(sets))
|
||||
set_id = self.sets_comboBox.itemData(idx, QtCore.Qt.UserRole)
|
||||
|
||||
r = self._results[sets[0]]
|
||||
self.param_tableWidget.setRowCount(len(r.parameter))
|
||||
res = self._results[set_id]
|
||||
self.param_tableWidget.setRowCount(len(res.parameter))
|
||||
for j, pvalue in enumerate(res.parameter.values()):
|
||||
name = pvalue.name
|
||||
p_header = QtWidgets.QTableWidgetItem(convert(name, 'tex', 'str', brackets=True))
|
||||
self.param_tableWidget.setVerticalHeaderItem(j, p_header)
|
||||
|
||||
for i, pval in enumerate(r.parameter.values()):
|
||||
name = pval.full_name
|
||||
p_header = QtWidgets.QTableWidgetItem(convert(name, 'tex', 'html', brackets=False))
|
||||
self.param_tableWidget.setVerticalHeaderItem(i, p_header)
|
||||
item_text = f'{pvalue.value:.4g}'
|
||||
if pvalue.error is not None:
|
||||
item_text += f' \u00b1 {pvalue.error:.4g}'
|
||||
self.param_tableWidget.setItem(2*j+1, 0, QtWidgets.QTableWidgetItem('-'))
|
||||
else:
|
||||
self.param_tableWidget.setItem(2*j+1, 0, QtWidgets.QTableWidgetItem())
|
||||
item = QtWidgets.QTableWidgetItem(item_text)
|
||||
self.param_tableWidget.setItem(j, 0, item)
|
||||
|
||||
for i, set_id in enumerate(sets):
|
||||
data_i = self._management[set_id]
|
||||
header_item = QtWidgets.QTableWidgetItem(data_i.name)
|
||||
header_item.setData(QtCore.Qt.UserRole, set_id)
|
||||
self.param_tableWidget.setHorizontalHeaderItem(i, header_item)
|
||||
|
||||
res = self._results[set_id]
|
||||
for j, pvalue in enumerate(res.parameter.values()):
|
||||
item_text = f'{pvalue.value:.4g}'
|
||||
if pvalue.error is not None:
|
||||
item_text += f' \u00b1 {pvalue.error:.4g}'
|
||||
self.param_tableWidget.setItem(2*j+1, i, QtWidgets.QTableWidgetItem('-'))
|
||||
else:
|
||||
self.param_tableWidget.setItem(2*j+1, i, QtWidgets.QTableWidgetItem())
|
||||
item = QtWidgets.QTableWidgetItem(item_text)
|
||||
self.param_tableWidget.setItem(j, i, item)
|
||||
|
||||
self.param_tableWidget.resizeColumnsToContents()
|
||||
self.param_tableWidget.selectColumn(0)
|
||||
self.show_results(None, idx=0)
|
||||
self.param_tableWidget.resizeColumnToContents(0)
|
||||
self.show_results(idx)
|
||||
|
||||
@QtCore.pyqtSlot(int, name='on_reject_fit_checkBox_stateChanged')
|
||||
@QtCore.pyqtSlot(int, name='on_del_prev_checkBox_stateChanged')
|
||||
def change_opts(self, _):
|
||||
idx = self.param_tableWidget.currentIndex().column()
|
||||
idx = self.sets_comboBox.currentIndex()
|
||||
|
||||
self._opts[idx] = (self.reject_fit_checkBox.checkState() == QtCore.Qt.Checked,
|
||||
self.del_prev_checkBox.checkState() == QtCore.Qt.Checked)
|
||||
|
||||
def show_results(self, item, idx=None):
|
||||
if item is not None:
|
||||
idx = self.param_tableWidget.indexFromItem(item).column()
|
||||
|
||||
set_id = self.param_tableWidget.horizontalHeaderItem(idx).data(QtCore.Qt.UserRole)
|
||||
def show_results(self, idx):
|
||||
set_id = self.sets_comboBox.itemData(idx, QtCore.Qt.UserRole)
|
||||
self.set_plot(set_id)
|
||||
self.set_correlation(set_id)
|
||||
self.set_statistics(set_id)
|
||||
|
||||
self.reject_fit_checkBox.blockSignals(True)
|
||||
self.reject_fit_checkBox.setChecked(self._opts[idx][0])
|
||||
self.reject_fit_checkBox.blockSignals(False)
|
||||
@ -157,13 +147,21 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
|
||||
self.del_prev_checkBox.blockSignals(False)
|
||||
|
||||
def set_plot(self, idx: str):
|
||||
if self.last_idx is not None:
|
||||
self.graph_opts[self.last_idx] = (
|
||||
self.fit_plot.getPlotItem().viewRange(),
|
||||
self.logx_box.isChecked(),
|
||||
self.logy_box.isChecked(),
|
||||
)
|
||||
|
||||
self.last_idx = idx
|
||||
res = self._results[idx]
|
||||
iscomplex = res.iscomplex
|
||||
|
||||
sub_funcs = res.sub(res.x)
|
||||
for item in self.fitplot.curves[::-1]:
|
||||
for item in self.fit_plot.plotItem.items[::-1]:
|
||||
if item not in [self.data_graph, self.data_graph_imag, self.fit_graph, self.fit_graph_imag]:
|
||||
self.fitplot.removeItem(item)
|
||||
self.fit_plot.removeItem(item)
|
||||
|
||||
if iscomplex:
|
||||
self.data_graph.setData(x=res.x_data, y=res.y_data.real)
|
||||
@ -173,11 +171,11 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
|
||||
self.resid_graph.setData(x=res.x_data, y=res.residual.real)
|
||||
self.resid_graph_imag.setData(x=res.x_data, y=res.residual.imag)
|
||||
|
||||
for f in sub_funcs:
|
||||
item = PlotItem(x=f.x, y=f.y.real)
|
||||
self.fitplot.addItem(item)
|
||||
item = PlotItem(x=f.x, y=f.y.imag)
|
||||
self.fitplot.addItem(item)
|
||||
for i, f in enumerate(sub_funcs):
|
||||
item = PlotItem(x=f.x, y=f.y.real, pen=mkPen({'color': i, 'style': 2}))
|
||||
self.fit_plot.addItem(item)
|
||||
item = PlotItem(x=f.x, y=f.y.imag, pen=mkPen({'color': i, 'style': 2}))
|
||||
self.fit_plot.addItem(item)
|
||||
|
||||
else:
|
||||
self.resid_graph.setData(x=res.x_data, y=res.residual)
|
||||
@ -187,12 +185,27 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
|
||||
self.fit_graph.setData(x=res.x, y=res.y)
|
||||
self.fit_graph_imag.setData(x=[], y=[])
|
||||
|
||||
for f in sub_funcs:
|
||||
item = PlotItem(x=f.x, y=f.y, pen=mkPen({'style': 2}))
|
||||
self.fitplot.addItem(item)
|
||||
for i, f in enumerate(sub_funcs):
|
||||
item = PlotItem(x=f.x, y=f.y, pen=mkPen({'color': i, 'style': 2}))
|
||||
self.fit_plot.addItem(item)
|
||||
|
||||
self.fitplot.setLogMode(x=res.islog)
|
||||
self.residplot.setLogMode(x=res.islog)
|
||||
self.logx_box.blockSignals(True)
|
||||
self.logx_box.setChecked(res.islog)
|
||||
self.logx_box.blockSignals(False)
|
||||
|
||||
self.fit_plot.setLogMode(x=res.islog)
|
||||
self.resid_plot.setLogMode(x=res.islog)
|
||||
|
||||
if idx in self.graph_opts:
|
||||
view_range, logx, logy = self.graph_opts[idx]
|
||||
self.fit_plot.setRange(xRange=view_range[0], yRange=view_range[1], padding=0)
|
||||
self.fit_plot.setLogMode(x=logx, y=logy)
|
||||
self.logx_box.blockSignals(True)
|
||||
self.logx_box.setChecked(logx)
|
||||
self.logx_box.blockSignals(False)
|
||||
self.logy_box.blockSignals(True)
|
||||
self.logy_box.setChecked(logy)
|
||||
self.logy_box.blockSignals(False)
|
||||
|
||||
def set_correlation(self, idx: str):
|
||||
while self.corr_tableWidget.rowCount():
|
||||
@ -223,7 +236,7 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
|
||||
|
||||
res = self._results[idx]
|
||||
|
||||
self.stats_tableWidget.setColumnCount(1 + len(self._prevs[idx]))
|
||||
self.stats_tableWidget.setColumnCount(1 + len(self._previous_fits[idx]))
|
||||
self.stats_tableWidget.setRowCount(len(res.statistics)+3)
|
||||
|
||||
it = QtWidgets.QTableWidgetItem(f'{res.dof}')
|
||||
@ -231,7 +244,7 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
|
||||
self.stats_tableWidget.setVerticalHeaderItem(0, QtWidgets.QTableWidgetItem('DoF'))
|
||||
self.stats_tableWidget.setItem(0, 0, it)
|
||||
|
||||
for col, (name, _, dof) in enumerate(self._prevs[idx], start=1):
|
||||
for col, (name, _, dof) in enumerate(self._previous_fits[idx], start=1):
|
||||
self.stats_tableWidget.setHorizontalHeaderItem(0, QtWidgets.QTableWidgetItem(name))
|
||||
it = QtWidgets.QTableWidgetItem(f'{dof}')
|
||||
it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable)
|
||||
@ -245,7 +258,7 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
|
||||
|
||||
best_idx = -1
|
||||
best_val = v
|
||||
for col, (_, stats, _) in enumerate(self._prevs[idx], start=1):
|
||||
for col, (_, stats, _) in enumerate(self._previous_fits[idx], start=1):
|
||||
if k in ['adj. R^2', 'R^2']:
|
||||
best_idx = col if best_val < stats[k] else max(0, best_idx)
|
||||
else:
|
||||
@ -265,7 +278,7 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
|
||||
self.stats_tableWidget.setVerticalHeaderItem(row+1, QtWidgets.QTableWidgetItem('Pr(>F)'))
|
||||
self.stats_tableWidget.setItem(row+1, 0, QtWidgets.QTableWidgetItem('-'))
|
||||
|
||||
for col, (_, stats, dof) in enumerate(self._prevs[idx], start=1):
|
||||
for col, (_, stats, dof) in enumerate(self._previous_fits[idx], start=1):
|
||||
f_value, prob_f = res.f_test(stats['chi^2'], dof)
|
||||
it = QtWidgets.QTableWidgetItem(f'{f_value:.4g}')
|
||||
it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable)
|
||||
@ -312,7 +325,6 @@ class QFitResult(QtWidgets.QDialog, Ui_Dialog):
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
|
||||
self.closed.emit(self._results, self._opts, graph, plot_fits, parts, extrapolate)
|
||||
self.accept()
|
||||
|
||||
@ -348,7 +360,7 @@ class FitExtension(QtWidgets.QDialog):
|
||||
|
||||
self.buttonBox = QtWidgets.QDialogButtonBox()
|
||||
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
|
||||
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
|
||||
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel | QtWidgets.QDialogButtonBox.Ok)
|
||||
gridLayout.addWidget(self.buttonBox, 3, 0, 1, 2)
|
||||
|
||||
self.setLayout(gridLayout)
|
||||
@ -365,4 +377,4 @@ class FitExtension(QtWidgets.QDialog):
|
||||
except TypeError:
|
||||
return None
|
||||
|
||||
return xmin, xmax, nums
|
||||
return xmin, xmax, nums
|
||||
|
Reference in New Issue
Block a user