1
0
forked from IPKM/nmreval
nmreval/nmreval/gui_qt/fit/result.py
2022-03-08 10:27:40 +01:00

246 lines
10 KiB
Python

from math import isnan
from numpy import r_
from pyqtgraph import mkBrush
from ..lib.utils import RdBuCMap
from ...utils.text import convert
from ..Qt import QtWidgets, QtGui, QtCore
from .._py.fitresult import Ui_Dialog
from ..lib.pg_objects import PlotItem
class QFitResult(QtWidgets.QDialog, Ui_Dialog):
closed = QtCore.pyqtSignal(dict, list)
redoFit = QtCore.pyqtSignal(dict)
def __init__(self, results: list, management, parent=None):
super().__init__(parent=parent)
self.setupUi(self)
self._management = management
self._prevs = {}
self._models = {}
for (res, parts) in results:
idx = res.idx
print(parts)
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._parts = {res.idx: parts for (res, parts) 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='r'), pen=None)
self.residplot.addItem(self.resid_graph)
self.residplot.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='r'), pen=None)
self.fitplot.addItem(self.data_graph)
self.fitplot.setLabel('left', 'Function')
self.fit_graph = PlotItem(x=[], y=[])
self.fitplot.addItem(self.fit_graph)
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.horizontalHeader().sectionClicked.connect(self.show_results)
self.logy_box.stateChanged.connect(lambda x: self.fitplot.setLogMode(y=bool(x)))
def add_graphs(self, graphs: list):
self.graph_comboBox.clear()
for (graph_id, graph_name) in graphs:
self.graph_comboBox.addItem(graph_name, userData=graph_id)
@QtCore.pyqtSlot(int, name='on_graph_checkBox_stateChanged')
def change_graph(self, state: int):
self.graph_comboBox.setEnabled(state == QtCore.Qt.Unchecked)
@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))
r = self._results[sets[0]]
self.param_tableWidget.setRowCount(len(r.parameter))
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)
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(0)
@QtCore.pyqtSlot(int, name='on_reject_fit_checkBox_stateChanged')
@QtCore.pyqtSlot(int, name='on_del_prev_checkBox_stateChanged')
def change_opts(self, _):
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, idx: int):
set_id = self.param_tableWidget.horizontalHeaderItem(idx).data(QtCore.Qt.UserRole)
self.set_plot(set_id)
self.set_correlation(set_id)
self.set_statistics(set_id)
def set_plot(self, idx: str):
res = self._results[idx]
iscomplex = res.iscomplex
self.resid_graph.setData(x=res.x_data, y=res.residual)
if iscomplex == 'complex':
self.data_graph.setData(x=r_[res.x_data, res.x_data],
y=r_[res.y_data.real, res.y_data.imag])
self.fit_graph.setData(x=r_[res.x, res.x],
y=r_[res.y.real, res.y.imag])
else:
self.data_graph.setData(x=res.x_data, y=res.y_data)
self.fit_graph.setData(x=res.x, y=res.y)
self.fitplot.setLogMode(x=res.islog)
self.residplot.setLogMode(x=res.islog)
def set_correlation(self, idx: str):
while self.corr_tableWidget.rowCount():
self.corr_tableWidget.removeRow(0)
res = self._results[idx]
c = res.correlation_list()
for pi, pj, corr, pcorr in c:
cnt = self.corr_tableWidget.rowCount()
self.corr_tableWidget.insertRow(cnt)
self.corr_tableWidget.setItem(cnt, 0, QtWidgets.QTableWidgetItem(convert(pi, old='tex', new='html')))
self.corr_tableWidget.setItem(cnt, 1, QtWidgets.QTableWidgetItem(convert(pj, old='tex', new='html')))
for i, val in enumerate([corr, pcorr]):
if isnan(val):
val = 1000.
val_item = QtWidgets.QTableWidgetItem(f'{val:.4g}')
val_item.setBackground(self.cmap.color(val))
if abs(val) > 0.75:
val_item.setForeground(QtGui.QColor('white'))
self.corr_tableWidget.setItem(cnt, i+2, val_item)
self.corr_tableWidget.resizeColumnsToContents()
def set_statistics(self, idx: str):
while self.stats_tableWidget.rowCount():
self.stats_tableWidget.removeRow(0)
res = self._results[idx]
self.stats_tableWidget.setColumnCount(1 + len(self._prevs[idx]))
self.stats_tableWidget.setRowCount(len(res.statistics)+3)
it = QtWidgets.QTableWidgetItem(f'{res.dof}')
it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable)
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):
self.stats_tableWidget.setHorizontalHeaderItem(0, QtWidgets.QTableWidgetItem(name))
it = QtWidgets.QTableWidgetItem(f'{dof}')
it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable)
self.stats_tableWidget.setItem(0, col, it)
for row, (k, v) in enumerate(res.statistics.items(), start=1):
self.stats_tableWidget.setVerticalHeaderItem(row, QtWidgets.QTableWidgetItem(k))
it = QtWidgets.QTableWidgetItem(f'{v:.4f}')
it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable)
self.stats_tableWidget.setItem(row, 0, it)
best_idx = -1
best_val = v
for col, (_, stats, _) in enumerate(self._prevs[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:
best_idx = col if best_val > stats[k] else max(0, best_idx)
it = QtWidgets.QTableWidgetItem(f'{stats[k]:.4f}')
it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable)
self.stats_tableWidget.setItem(row, col, it)
if best_idx > -1:
self.stats_tableWidget.item(row, best_idx).setBackground(QtGui.QColor('green'))
self.stats_tableWidget.item(row, best_idx).setForeground(QtGui.QColor('white'))
row = self.stats_tableWidget.rowCount() - 2
self.stats_tableWidget.setVerticalHeaderItem(row, QtWidgets.QTableWidgetItem('F'))
self.stats_tableWidget.setItem(row, 0, QtWidgets.QTableWidgetItem('-'))
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):
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)
self.corr_tableWidget.setItem(row, col, it)
it = QtWidgets.QTableWidgetItem(f'{prob_f:.4g}')
it.setFlags(it.flags() ^ QtCore.Qt.ItemIsEditable)
if prob_f < 0.05:
it.setBackground(QtGui.QColor('green'))
it.setForeground(QtGui.QColor('white'))
self.stats_tableWidget.setItem(row+1, col, it)
@QtCore.pyqtSlot(QtWidgets.QAbstractButton)
def on_buttonBox_clicked(self, button: QtWidgets.QAbstractButton):
button_type = self.buttonBox.standardButton(button)
if button_type == self.buttonBox.Retry:
self.redoFit.emit(self._results)
elif button_type == self.buttonBox.Ok:
graph = '' if self.graph_checkBox.checkState() == QtCore.Qt.Checked else self.graph_comboBox.currentData()
subplots = self.partial_checkBox.checkState() == QtCore.Qt.Checked
self._opts.extend([graph, subplots])
self.closed.emit(self._results, self._opts)
self.accept()
else:
self.reject()