source: sasview/src/sas/qtgui/LinearFit.py @ 87cc73a

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since 87cc73a was 87cc73a, checked in by Piotr Rozyczko <rozyczko@…>, 7 years ago

Added Modify Plot Properties functionality. SASVIEW-169

  • Property mode set to 100644
File size: 8.6 KB
Line 
1"""
2Adds a linear fit plot to the chart
3"""
4import re
5import numpy
6from PyQt4 import QtGui
7from PyQt4 import QtCore
8
9#from sas.sasgui.transform import
10from sas.qtgui.GuiUtils import formatNumber
11from sas.sasgui.plottools import fittings
12from sas.sasgui.plottools import transform
13
14from sas.sasgui.plottools.LineModel import LineModel
15
16# Local UI
17from sas.qtgui.UI.LinearFitUI import Ui_LinearFitUI
18
19class LinearFit(QtGui.QDialog, Ui_LinearFitUI):
20    def __init__(self, parent=None,
21                 data=None,
22                 max_range=(0.0, 0.0),
23                 fit_range=(0.0, 0.0),
24                 xlabel="",
25                 ylabel=""):
26        super(LinearFit, self).__init__()
27
28        self.setupUi(self)
29        assert(isinstance(max_range, tuple))
30        assert(isinstance(fit_range, tuple))
31
32        self.data = data
33        self.parent = parent
34
35        self.max_range = max_range
36        self.fit_range = fit_range
37        self.xLabel = xlabel
38        self.yLabel = ylabel
39
40        self.x_is_log = self.xLabel == "log10(x)"
41        self.y_is_log = self.yLabel == "log10(y)"
42
43        self.txtFitRangeMin.setValidator(QtGui.QDoubleValidator())
44        self.txtFitRangeMax.setValidator(QtGui.QDoubleValidator())
45
46        # Default values in the line edits
47        self.txtA.setText("1")
48        self.txtB.setText("1")
49        self.txtAerr.setText("0")
50        self.txtBerr.setText("0")
51        self.txtChi2.setText("0")
52
53        # Initial ranges
54        self.txtRangeMin.setText(str(max_range[0]))
55        self.txtRangeMax.setText(str(max_range[1]))
56        self.txtFitRangeMin.setText(str(fit_range[0]))
57        self.txtFitRangeMax.setText(str(fit_range[1]))
58
59        # cast xLabel into html
60        label = re.sub(r'\^\((.)\)(.*)', r'<span style=" vertical-align:super;">\1</span>\2',
61                      str(self.xLabel).rstrip())
62        self.lblRange.setText('Fit range of ' + label)
63
64        self.model = LineModel()
65        # Display the fittings values
66        self.default_A = self.model.getParam('A')
67        self.default_B = self.model.getParam('B')
68        self.cstA = fittings.Parameter(self.model, 'A', self.default_A)
69        self.cstB = fittings.Parameter(self.model, 'B', self.default_B)
70        self.transform = transform
71
72        # connect Fit button
73        self.cmdFit.clicked.connect(self.fit)
74
75    def setRangeLabel(self, label=""):
76        """
77        Overwrite default fit range label to correspond to actual unit
78        """
79        assert(isinstance(label, basestring))
80        self.lblRange.setText(label)
81
82    def a(self):
83        return (float(self.txtA.text()), float(self.txtAerr.text()))
84
85    def b(self):
86        return (float(self.txtB.text()), float(self.txtBerr.text()))
87
88    def chi(self):
89        return float(self.txtChi2.text())
90
91    def range(self):
92        return (float(self.txtFitRangeMin.text()), float(self.txtFitRangeMax.text()))
93
94    def fit(self, event):
95        """
96        Performs the fit. Receive an event when clicking on
97        the button Fit.Computes chisqr ,
98        A and B parameters of the best linear fit y=Ax +B
99        Push a plottable to the caller
100        """
101        tempx = []
102        tempy = []
103        tempdy = []
104
105        # Checks to assure data correctness
106        if len(self.data.view.x) < 2:
107            return
108        if not self.checkFitValues(self.txtFitRangeMin):
109            return
110
111        self.xminFit, self.xmaxFit = self.range()
112
113        xmin = self.xminFit
114        xmax = self.xmaxFit
115        xminView = xmin
116        xmaxView = xmax
117
118        # Set the qmin and qmax in the panel that matches the
119        # transformed min and max
120        #value_xmin = X_VAL_DICT[self.xLabel].floatTransform(xmin)
121        #value_xmax = X_VAL_DICT[self.xLabel].floatTransform(xmax)
122
123        value_xmin = self.floatInvTransform(xmin)
124        value_xmax = self.floatInvTransform(xmax)
125        self.txtRangeMin.setText(formatNumber(value_xmin))
126        self.txtRangeMax.setText(formatNumber(value_xmax))
127
128        tempx, tempy, tempdy = self.origData()
129
130        # Find the fitting parameters
131        self.cstA = fittings.Parameter(self.model, 'A', self.default_A)
132        self.cstB = fittings.Parameter(self.model, 'B', self.default_B)
133        tempdy = numpy.asarray(tempdy)
134        tempdy[tempdy == 0] = 1
135
136        if self.x_is_log:
137            xmin = numpy.log10(xmin)
138            xmax = numpy.log10(xmax)
139
140        chisqr, out, cov = fittings.sasfit(self.model,
141                                           [self.cstA, self.cstB],
142                                           tempx, tempy, tempdy,
143                                           xmin, xmax)
144        # Use chi2/dof
145        if len(tempx) > 0:
146            chisqr = chisqr / len(tempx)
147
148        # Check that cov and out are iterable before displaying them
149        errA = numpy.sqrt(cov[0][0]) if cov is not None else 0
150        errB = numpy.sqrt(cov[1][1]) if cov is not None else 0
151        cstA = out[0] if out is not None else 0.0
152        cstB = out[1] if out is not None else 0.0
153
154        # Reset model with the right values of A and B
155        self.model.setParam('A', float(cstA))
156        self.model.setParam('B', float(cstB))
157
158        tempx = []
159        tempy = []
160        y_model = 0.0
161
162        # load tempy with the minimum transformation
163        y_model = self.model.run(xmin)
164        tempx.append(xminView)
165        tempy.append(numpy.power(10, y_model) if self.y_is_log else y_model)
166
167        # load tempy with the maximum transformation
168        y_model = self.model.run(xmax)
169        tempx.append(xmaxView)
170        tempy.append(numpy.power(10, y_model) if self.y_is_log else y_model)
171
172        # Set the fit parameter display when  FitDialog is opened again
173        self.Avalue = cstA
174        self.Bvalue = cstB
175        self.ErrAvalue = errA
176        self.ErrBvalue = errB
177        self.Chivalue = chisqr
178
179        # Update the widget
180        self.txtA.setText(formatNumber(self.Avalue))
181        self.txtAerr.setText(formatNumber(self.ErrAvalue))
182        self.txtB.setText(formatNumber(self.Bvalue))
183        self.txtBerr.setText(formatNumber(self.ErrBvalue))
184        self.txtChi2.setText(formatNumber(self.Chivalue))
185
186        #self.parent.updatePlot.emit((tempx, tempy))
187        self.parent.emit(QtCore.SIGNAL('updatePlot'), (tempx, tempy))
188
189    def origData(self):
190        # Store the transformed values of view x, y and dy before the fit
191        xmin_check = numpy.log10(self.xminFit)
192        # Local shortcuts
193        x = self.data.view.x
194        y = self.data.view.y
195        dy = self.data.view.dy
196
197        if self.y_is_log:
198            if self.x_is_log:
199                tempy  = [numpy.log10(y[i])
200                         for i in range(len(x)) if x[i] >= xmin_check]
201                tempdy = [transform.errToLogX(y[i], 0, dy[i], 0)
202                         for i in range(len(x)) if x[i] >= xmin_check]
203            else:
204                tempy = map(numpy.log10, y)
205                tempdy = map(lambda t1,t2:transform.errToLogX(t1,0,t2,0),y,dy)
206        else:
207            tempy = y
208            tempdy = dy
209
210        if self.x_is_log:
211            tempx = [numpy.log10(x) for x in self.data.view.x if x > xmin_check]
212        else:
213            tempx = x
214
215        return tempx, tempy, tempdy
216
217    def checkFitValues(self, item):
218        """
219        Check the validity of input values
220        """
221        flag = True
222        value = item.text()
223        p_white = item.palette()
224        p_white.setColor(item.backgroundRole(), QtCore.Qt.white)
225        p_pink = item.palette()
226        p_pink.setColor(item.backgroundRole(), QtGui.QColor(255, 128, 128))
227        item.setAutoFillBackground(True)
228        # Check for possible values entered
229        if self.x_is_log:
230            if float(value) > 0:
231                item.setPalette(p_white)
232            else:
233                flag = False
234                item.setPalette(p_pink)
235        return flag
236
237    def floatInvTransform(self, x):
238        """
239        transform a float.It is used to determine the x.View min and x.View
240        max for values not in x.  Also used to properly calculate RgQmin,
241        RgQmax and to update qmin and qmax in the linear range boxes on the
242        panel.
243
244        """
245        # TODO: refactor this. This is just a hack to make the
246        # functionality work without rewritting the whole code
247        # with good design (which really should be done...).
248        if self.xLabel == "x":
249            return x
250        elif self.xLabel == "x^(2)":
251            return numpy.sqrt(x)
252        elif self.xLabel == "x^(4)":
253            return numpy.sqrt(numpy.sqrt(x))
254        elif self.xLabel == "log10(x)":
255            return numpy.power(10, x)
256        elif self.xLabel == "ln(x)":
257            return numpy.exp(x)
258        elif self.xLabel == "log10(x^(4))":
259            return numpy.sqrt(numpy.sqrt(numpy.power(10, x)))
260        return x
261
262
Note: See TracBrowser for help on using the repository browser.