xboa
_refine_peak_finder.py
Go to the documentation of this file.
1 # This file is a part of xboa
2 #
3 # xboa is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation, either version 3 of the License, or
6 # (at your option) any later version.
7 #
8 # xboa is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
12 #
13 # You should have received a copy of the GNU General Public License
14 # along with xboa in the doc folder. If not, see
15 # <http://www.gnu.org/licenses/>.
16 
17 import xboa.Common as common
18 
19 try:
20  import ROOT
21 except ImportError:
22  pass
23 try:
24  import numpy
25 except ImportError:
26  pass
27 
28 class RefinePeakFinder(object):
29  """
30  Find peaks in a list of data points when you have an estimate of the peaks
31  position already, using a quadratic fit (in ROOT) to the peak. This can help
32  to refine a peak estimate e.g. in the presence of noise.
33 
34  The quality of the fit is estimated using the RMS of the residuals compared
35  to the RMS of the actual data within a certain range of the estimated peak.
36  """
37  def __init__(self, peak_list, delta_seed, max_delta, draw):
38  """
39  Initialise the peak finder
40  - peak_list: [list of ints] list of integer indices of estimated peak
41  positions
42  - delta_seed: [int] xboa initially considers points within a range given
43  by delta_seed from the estimated peak position to refine
44  the peak; xboa subsequently then varies delta to improve
45  the fit quality.
46  - max_delta: [int] delta cannot increment beyond max_delta.
47  - draw: [bool] set to True to draw the fits. In fit plots, "0" is the
48  estimated peak position
49  """
50  self.fit = None
51  self.peak_list = peak_list
52  self.delta_seed = delta_seed
53  self.max_delta = max_delta
54  self.draw = draw
55 
56  @classmethod
57  def sigma(self, data):
58  """
59  Find the standard deviation from the mean of data
60 
61  - data: list of floats containing data
62 
63  Retuns float S(x**2)/N - {S(x)/N}**2 where N is the length of data
64  """
65  mean_square_sum = sum([y**2 for y in data])/len(data)
66  mean_sum_square = sum([y for y in data])/len(data)
67  return (mean_square_sum-mean_sum_square**2)**0.5
68 
69  def _peak_fit(self, data, peak_index, delta_fit, delta_bite):
70  """
71  Fit the data in the vicinity of peak_index to a quadratic
72 
73  - data: list of floats containing data
74  - peak_index: the index at which the peak is found
75  - delta_fit: the maximum distance from the peak from which data will be
76  drawn for fitting
77  - delta_bite: the maximum distance from the peak from which data will be
78  drawn for assessing fit quality
79 
80  Goodness-of-fit is an estimation of how well the data is fitted, based
81  on comparing the standard deviation of the fit residuals to the sigma of
82  data within a small "bite". fit_quality is given by comparing sigma
83  (standard deviation) of data in a small bite with sigma of fit
84  residuals, like
85  Q = (sigma(res)/sigma(bite)-1.)*N(bite)**0.5
86  sigma(bite) in this sense is taken to be the natural spread of the raw
87  data, and we seek to ensure sigma(res) does not go much above this
88  spread.
89 
90  Return value is a tuple of (fit_quality, histogram, graph, fit)
91  - fit_quality: goodness-of-fit estimation.
92  """
93  x_min = max(peak_index-delta_fit, 0)
94  x_max = min(peak_index+delta_fit+1, len(data))
95  x_list = range(x_min-peak_index, x_max-peak_index)
96  y_list = data[x_min:x_max]
97  hist, graph = common.make_root_graph("name", x_list, "", data[x_min:x_max], "")
98  if self.fit == None:
99  quadratic = "[0]+[1]*x+[2]*x*x"
100  self.fit = ROOT.TF1("fit", quadratic, min(y_list), max(y_list))
101  fit = self.fit
102  fit.SetRange(min(y_list), max(y_list))
103  for i in range(3):
104  fit.ReleaseParameter(i)
105  fit.SetParameter(i, 0.)
106  #QNOS: Quiet, Do not store graphics, Dont draw
107  graph.Fit(fit, 'QNO')
108  delta_list = [y_list[i]-fit.Eval(x) for i, x in enumerate(x_list)]
109  sigma_delta = self.sigma(delta_list)
110  sigma_bite = self.sigma(y_list[delta_fit-delta_bite:delta_fit+delta_bite])
111  # fit_quality = (s(residual)/s(data)-1.)*sqrt(n)
112  # where n is the number in the s(data) calculation
113  fit_quality = (sigma_delta/sigma_bite-1.)*delta_bite**0.5
114  return fit_quality, hist, graph, fit
115 
116  def find_peak_errors(self, data):
117  """
118  Find the error on the peak estimation based on a linear fit to the
119  derivative
120  """
121  # we want to fit for x = my + c
122  # c is the peak
123  # chi2 gives estimate of fit errors
124  this_max_delta = min(len(data), self.max_delta)
125  peak_with_errors = []
126  fit_quality = 0.
127  fail_list = []
128  for peak in self.peak_list:
129  delta_fit = self.delta_seed
130  while fit_quality < 1. and delta_fit < this_max_delta:
131  delta_fit *= 2
132  try:
133  fit_quality, hist, graph, fit = self._peak_fit(data, peak, delta_fit, self.delta_seed)
134  except Exception:
135  break
136  delta_fit /= 2
137  fit_quality, hist, graph, fit = self._peak_fit(data, peak, delta_fit, self.delta_seed)
138  if self.draw:
139  canvas = common.make_root_canvas("values")
140  canvas.Draw()
141  canvas.cd()
142  hist.Draw()
143  graph.SetMarkerStyle(6)
144  graph.Draw('p')
145  fit_result = graph.Fit(fit, "S") #Return FitResult
146  self.fit_list.append(fit)
147  self.fit = None
148  canvas.Update()
149  else:
150  #QNOS: Quiet, Do not store graphics, Dont draw, Return FitResult
151  fit_result = graph.Fit(fit, 'QNOS')
152  common.clear_root()
153 
154  p0 = fit.GetParameter(0)
155  p1 = fit.GetParameter(1)
156  p2 = fit.GetParameter(2)
157 
158 
159  peak_out = [-p1/2./p2+peak, -p1*p1/4./p2+p0] #x, y
160  if abs(peak_out[0]-peak) > delta_fit or \
161  peak_out[0] < 0 or \
162  peak_out[0] > len(data):
163  continue
164  J = numpy.matrix([[0., -1./2./p2, p1/2./p2/p2],
165  [1., -p1/2./p2, p1*p1/4./p2/p2]])
166  JT = J.T
167  CovP = numpy.matrix([
168  [fit_result.GetCovarianceMatrix()(i, j) for i in range(3)]
169  for j in range(3)])
170  CovPeak = J*CovP*JT
171  if self.draw:
172  print "=========="
173  print "Fit parameters"
174  print numpy.array([p0, p1, p2])
175  print "Fit errors"
176  print CovP
177  print "Jacobian"
178  print J
179  print "Peak x, y"
180  print numpy.array(peak_out)
181  print "Covariances errors"
182  print CovPeak
183  print "=========="
184  CovPeak = [[CovPeak[i, j] for i in range(2)] for j in range(2)]
185  peak_with_errors.append({"x":peak_out[0],
186  "y":peak_out[1],
187  "x_in":peak,
188  "cov(x,y)":CovPeak})
189  return peak_with_errors
190 
191  def find_peaks(self, data):
192  """
193  Find peaks in the data
194 
195  - data list of floats that contains ordinates (y-axis values) of data
196  abscissa are assumed to be data index.
197 
198  Returns a list of indices, each index corresponding to the location of a
199  peak in data
200  """
201  peak_error_list = self.find_peak_errors(data)
202  peak_list = [int(x["x"]) for x in peak_error_list]
203  return peak_list
204 
205  fit_list = []
def sigma
Find the standard deviation from the mean of data.
Find peaks in a list of data points when you have an estimate of the peaks position already...
def _peak_fit
Fit the data in the vicinity of peak_index to a quadratic.
Deprecated accessor to xboa.common module.
Definition: Common.py:1
def find_peak_errors
Find the error on the peak estimation based on a linear fit to the derivative.