Skip to content

Commit 0ff45cb

Browse files
author
Paul Müller
committed
Merge pull request #130 from paulmueller/develop
Develop
2 parents 1a78576 + 07dffd2 commit 0ff45cb

File tree

8 files changed

+289
-123
lines changed

8 files changed

+289
-123
lines changed

ChangeLog.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
0.9.0
2+
- Improve parameter display (#52, #114)
3+
- Display Chi2 on each page (#115)
4+
- The displayed Chi2-value for non-weighted fits is now
5+
normalized to the expected values of the fit. The
6+
documentation has been updated accordingly.
7+
- Add "All files" option in save dialogs (#97)
8+
- Improved plot export dialog (#99)
19
0.8.9
210
- Improved support for "ALV-7004" files (#104)
311
- Increase resolution for image export

doc/PyCorrFit_doc_content.tex

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -696,11 +696,35 @@ \subsection{Fitting}
696696

697697
\subsubsection{Weighted fitting}
698698
\label{sec:theor.weigh}
699-
In certain cases, it is useful to implement weights (standard deviation) $\sigma_i$ for the calculation of $\chi^2$. For example, very noisy parts of a correlation curve can falsify the resulting fit. In \textit{PyCorrFit}, weighting is implemented as follows:
699+
In certain cases, it is useful to perform weighted fitting with a known variance $\sigma_i^2$ at the data points $\tau_i$. In \textit{PyCorrFit}, weighted fitting is implemented as follows:
700700
\begin{equation}
701701
\chi^2_\mathrm{weighted} = \min_{\alpha_1, \dots, \alpha_k} \sum_{i=1}^n \frac{\left[ G(\tau_i,\alpha_1, \dots, \alpha_k) - H(\tau_i) \right]^2}{\sigma_i^2}
702702
\end{equation}
703-
\textit{PyCorrFit} is able to calculate the weights $\sigma_i$ from the experimental data. The different approaches of this calculation of weights implemented in \textit{PyCorrFit} are explained in \hyref{Section}{sec:intro.graph}.
703+
704+
Besides importing the variance alongside experimental data, \textit{PyCorrFit} is able to estimate the variance from the experimental data via several different approaches. A recommended approach is averaging over several curves. Other approaches such as estimation of the variance from spline fits or from the model function (see \hyref{Section}{sec:intro.graph}) cannot be considered unbiased.
705+
Note that when performing global fits (see \hyref{Section}{sec:menub.tools.globa}), different types of weights for different correlation curves can strongly influence the result of the fit. Especially mixing curves with and without weights will most likely result in unphysical fits.
706+
707+
\subsubsection{Displayed $\chi^2$ values}
708+
The displayed value of $\chi^2$ is defined by the type of the performed fit. This value is commonly normalized by the degrees of freedom $\nu = N - n - 1$, where $N$ is the number of observations (data points) and $n$ is the number of fitting parameters.
709+
\begin{itemize}
710+
711+
\item \textbf{reduced expected sum of squares}: This value is used when there is no variance available for plot normalization.
712+
\begin{equation}
713+
\chi^2_\mathrm{red,exp} = \frac{1}{\nu}\sum_{i=1}^n \frac{\left[ G(\tau_i,\alpha_\mathrm{min}) - H(\tau_i) \right]^2}{G(\tau_i,\alpha_\mathrm{min})}
714+
\end{equation}
715+
716+
\item \textbf{reduced weighted sum of squares}: This value is used when the fit was performed with variances $\sigma^2$.
717+
\begin{equation}
718+
\chi^2_\mathrm{red,weight} = \frac{1}{\nu}\sum_{i=1}^n \frac{\left[ G(\tau_i,\alpha_\mathrm{min}) - H(\tau_i) \right]^2}{\sigma^2}
719+
\end{equation}
720+
721+
\item \textbf{reduced global sum of squares}: This value is used for global fits. The weights are computed identically to the situation with reduced weights, except that the variance $\sigma_\textrm{glob}^2$ may result in non-physical weighting (hence the emphasis on global).
722+
\begin{equation}
723+
\chi^2_\mathrm{red,weight} = \frac{1}{\nu}\sum_{i=1}^n \frac{\left[ G(\tau_i,\alpha_\mathrm{min}) - H(\tau_i) \right]^2}{\sigma_\textrm{glob}^2}
724+
\end{equation}
725+
726+
\end{itemize}
727+
704728

705729
\subsubsection{Algorithms}
706730
\label{sec:theor.alg}

pycorrfit/edclasses.py

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,35 +23,11 @@
2323
except:
2424
pass
2525

26-
27-
import numpy as np
2826
import sys
2927
import traceback
30-
from wx.lib.agw import floatspin # Float numbers in spin fields
3128
import wx
3229

3330

34-
class FloatSpin(floatspin.FloatSpin):
35-
def __init__(self, parent, digits=10, increment=.01):
36-
floatspin.FloatSpin.__init__(self, parent, digits=digits,
37-
increment = increment)
38-
self.Bind(wx.EVT_SPINCTRL, self.increment)
39-
#self.Bind(wx.EVT_SPIN, self.increment)
40-
#self.increment()
41-
42-
43-
def increment(self, event=None):
44-
# Find significant digit
45-
# and use it as the new increment
46-
x = self.GetValue()
47-
if x == 0:
48-
incre = 0.1
49-
else:
50-
digit = int(np.ceil(np.log10(abs(x)))) - 2
51-
incre = 10**digit
52-
self.SetIncrement(incre)
53-
54-
5531
class ChoicesDialog(wx.Dialog):
5632
def __init__(self, parent, dropdownlist, title, text):
5733
# parent is main frame
@@ -107,7 +83,7 @@ def save_figure(self, evt=None):
10783
Page = self.canvas.HACK_Page
10884
add = self.canvas.HACK_append
10985
dirname = parent.dirname
110-
filename = Page.tabtitle.GetValue().strip()+Page.counter[:2]+add
86+
filename = self.canvas.get_window_title().replace(" ", "_").lower()+add
11187
formats = fig.canvas.get_supported_filetypes()
11288
except:
11389
dirname = "."
@@ -129,12 +105,8 @@ def save_figure(self, evt=None):
129105
if dlg.ShowModal() == wx.ID_OK:
130106
wildcard = keys[dlg.GetFilterIndex()]
131107
filename = dlg.GetPath()
132-
haswc = False
133-
for key in keys:
134-
if filename.lower().endswith("."+key) is True:
135-
haswc = True
136-
if haswc == False:
137-
filename = filename+"."+wildcard
108+
if not filename.endswith(wildcard):
109+
filename += "."+wildcard
138110
dirname = dlg.GetDirectory()
139111
#savename = os.path.join(dirname, filename)
140112
savename = filename

pycorrfit/fcs_data_set.py

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,7 @@ def __init__(self, correlations=[], global_fit=False,
667667
self.global_fit_variables = global_fit_variables
668668
self.verbose = verbose
669669
self.uselatex = uselatex
670+
self.is_weighted_fit = False
670671

671672
if not global_fit:
672673
# Fit each correlation separately
@@ -679,6 +680,7 @@ def __init__(self, correlations=[], global_fit=False,
679680
# fit_bool: True for variable
680681
self.fit_bool = corr.fit_parameters_variable.copy()
681682
self.fit_parm = corr.fit_parameters.copy()
683+
self.is_weighted_fit = corr.is_weighted_fit
682684
self.fit_weights = Fit.compute_weights(corr,
683685
verbose=verbose,
684686
uselatex=uselatex)
@@ -705,6 +707,7 @@ def __init__(self, correlations=[], global_fit=False,
705707
varin = list() # names of variable fitting parameters
706708
variv = list() # values of variable fitting parameters
707709
varmap = list() # list of indices of fitted parameters
710+
self.is_weighted_fit = None
708711
for corr in self.correlations:
709712
xtemp.append(corr.correlation_fit[:,0])
710713
ytemp.append(corr.correlation_fit[:,1])
@@ -816,13 +819,15 @@ def get_fit_results(self, correlation):
816819
c = correlation
817820
d = {
818821
"chi2" : self.chi_squared,
822+
"chi2 type" : self.chi_squared_type,
819823
"weighted fit" : c.is_weighted_fit,
820824
"fit algorithm" : c.fit_algorithm,
821825
"fit result" : c.fit_parameters.copy(),
822826
"fit parameters" : np.where(c.fit_parameters_variable)[0],
823827
"fit weights" : self.compute_weights(c)
824828
}
825829

830+
826831
if c.is_weighted_fit:
827832
d["weighted fit type"] = c.fit_weight_type
828833
if isinstance(c.fit_weight_data, (int, float)):
@@ -837,14 +842,47 @@ def get_fit_results(self, correlation):
837842

838843
@property
839844
def chi_squared(self):
840-
"""
841-
Calculate Chi² for the current class.
845+
""" Calculate displayed Chi²
846+
847+
Calculate reduced Chi² for the current class.
842848
"""
843849
# Calculate degrees of freedom
844850
dof = len(self.x) - np.sum(self.fit_bool) - 1
845851
# This is exactly what is minimized by the scalar minimizers
846852
chi2 = self.fit_function_scalar(self.fit_parm[self.fit_bool], self.x)
847-
return chi2 / dof
853+
if self.chi_squared_type == "reduced expected sum of squares":
854+
fitted = self.func(self.fit_parm, self.x)
855+
chi2 = np.sum((self.y-fitted)**2/np.abs(fitted)) / dof
856+
elif self.chi_squared_type == "reduced weighted sum of squares":
857+
fitted = self.func(self.fit_parm, self.x)
858+
variance = self.fit_weights**2
859+
chi2 = np.sum((self.y-fitted)**2/variance) / dof
860+
elif self.chi_squared_type == "reduced global sum of squares":
861+
fitted = self.func(self.fit_parm, self.x)
862+
variance = self.fit_weights**2
863+
chi2 = np.sum((self.y-fitted)**2/variance) / dof
864+
return chi2
865+
866+
867+
@property
868+
def chi_squared_type(self):
869+
""" The type of Chi² that currently applies.
870+
871+
Returns
872+
-------
873+
"reduced" - if variance of data was used for fitting
874+
"reduced Pearson" - if variance of data is not available
875+
"""
876+
if self.is_weighted_fit is None:
877+
# global fitting
878+
return "reduced global sum of squares"
879+
elif self.is_weighted_fit == True:
880+
return "reduced weighted sum of squares"
881+
elif self.is_weighted_fit == False:
882+
return "reduced expected sum of squares"
883+
else:
884+
raise ValueError("Unknown weight type!")
885+
848886

849887
@staticmethod
850888
def compute_weights(correlation, verbose=0, uselatex=False):
@@ -1111,6 +1149,7 @@ def minimize(self):
11111149
# Only allow physically correct parameters
11121150
self.fit_parm = self.check_parms(self.fit_parm)
11131151
# Write optimal parameters back to this class.
1152+
# Must be called after `self.fitparm = ...`
11141153
chi = self.chi_squared
11151154
# Compute error estimates for fit (Only "Lev-Mar")
11161155
if self.fit_algorithm == "Lev-Mar":

pycorrfit/frontend.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,8 +1454,8 @@ def OnSaveData(self,e=None):
14541454
# Export CSV data
14551455
filename = Page.tabtitle.GetValue().strip()+Page.counter[:2]+".csv"
14561456
dlg = wx.FileDialog(self, "Save curve", self.dirname, filename,
1457-
"Correlation with trace (*.csv)|*.csv;*.CSV"+\
1458-
"|Correlation only (*.csv)|*.csv;*.CSV",
1457+
"Correlation with trace (*.csv)|*.csv;*.*"+\
1458+
"|Correlation only (*.csv)|*.csv;*.*",
14591459
wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
14601460
# user cannot do anything until he clicks "OK"
14611461
if dlg.ShowModal() == wx.ID_OK:
@@ -1594,7 +1594,7 @@ def OnSaveSession(self,e=None):
15941594
Infodict["Comments"]["Session"] = self.SessionComment
15951595
# File dialog
15961596
dlg = wx.FileDialog(self, "Save session file", self.dirname, "",
1597-
"PyCorrFit session (*.pcfs)|*.pcfs",
1597+
"PyCorrFit session (*.pcfs)|*.pcfs|All files (*.*)|*.*",
15981598
wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
15991599
if dlg.ShowModal() == wx.ID_OK:
16001600
# Save everything

0 commit comments

Comments
 (0)