Updated copyright blurbs in all files to '# Copyright'
[hooke.git] / hooke / plugin / peakspot.py
1 # Copyright
2
3 """a library of helping functions for spotting force spectroscopy peaks.
4 """
5
6 import numpy as np
7
8 def conv_dx(data,vect):
9     '''
10     Returns the right-centered convolution of data with vector vect
11     '''
12     dim=len(data)
13     window=len(vect)
14     temparr=[0.0]*dim
15
16     end=dim-window
17
18     for j in range(end):
19      for k in range(window):
20       temparr[j]+=data[j+k]*vect[k]
21
22     return temparr
23
24 def absdev(arr):
25     '''
26     Calculates the absolute deviation of a vector
27     '''
28     med=0.0
29     absD=0.0
30
31     med=np.mean(arr)
32     for j in arr:
33         absD+=abs(j-med)
34     return absD/len(arr)
35
36 def noise_absdev(data,positive=False,maxcut=0.2,stable=0.005):
37     '''
38     Returns the standard deviation of the noise.
39     The logic is: we cut the most negative (or positive) data points until the absolute deviation
40     becomes stable (it doesn't vary more than 0.005) or we have cut more than maxcut*len(data) points.
41     Then calculate the absolute deviation.
42
43     If positive=True we cut the most positive data points, False=we cut the negative ones.
44     '''
45     out=[item for item in data] #we copy just to be sure...
46     out.sort()
47     if positive:
48         out.reverse()
49
50     temp_absdev=absdev(out)
51
52     for index in range(len(out)):
53         cutindex=(index+1)*5
54         cut_absdev=absdev(out[cutindex:]) #we jump five points after five points...
55         if 1-(cut_absdev/temp_absdev) < stable and cutindex<(maxcut*len(out)):
56             temp_absdev=cut_absdev
57         else:
58             break
59
60     return cut_absdev
61
62 def abovenoise(convoluted,noise_level,cut_index=0,abs_devs=4):
63     '''
64     Generates a vector which is 0 where the vector is less than abs_devs*noise_level ; 1 if not (spike).
65     '''
66     #calculate absolute noise deviation
67     #noise_level=noise_absdev(convoluted[cut_index:])
68
69     above=[]
70
71     for index in range(len(convoluted)):
72         if index<cut_index:
73             above.append(0)
74         else:
75             #FIXME: should calculate the *average* (here we assume that convolution mean is 0, which is FALSE!)
76             if convoluted[index] < -1*noise_level*abs_devs:
77                 above.append(convoluted[index])
78             else:
79                 above.append(0)
80     return above
81
82 def find_peaks(above, seedouble=10):
83     '''
84     Finds individual peak location.
85     abovenoise() finds all points above a given threshold in the convolution. This point is often not unique
86     but there is a "cluster" of points above a threshold.
87     Here we obtain only the coordinates of the largest *convolution* spike for each cluster.
88
89     above=vector obtained from abovenoise()
90     seedouble=value at which we want to "delete" double peaks. That is, if two peaks have a distance
91     < than $seedouble points , only the first is kept.
92     '''
93     nonzero=[]
94     peaks_location=[]
95     peaks_size=[]
96
97     for index in range(len(above)):
98         if above[index] != 0:
99             nonzero.append(index)
100         else:
101             if len(nonzero) != 0:
102                 if len(nonzero)==1:
103                     nonzero.append(nonzero[0]+1)
104                 peaks_size.append(min(above[nonzero[0]:nonzero[-1]]))
105                 peaks_location.append(above[nonzero[0]:nonzero[-1]].index(peaks_size[-1])+nonzero[0])
106                 nonzero=[]
107             else:
108                 pass
109
110     #recursively eliminate double peaks
111     #(maybe not the smartest of cycles, but i'm asleep...)
112     temp_location=None
113     while temp_location != []:
114         temp_location=[]
115         if len(peaks_location)>1:
116             for index in range(len(peaks_location)-1):
117                 if peaks_location[index+1]-peaks_location[index] < seedouble:
118                     temp_location=peaks_location[:index]+peaks_location[index+1:]
119         if temp_location != []:
120             peaks_location=temp_location
121
122     return peaks_location,peaks_size