-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgaussian.py
129 lines (104 loc) · 2.78 KB
/
gaussian.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import numpy as np
import scipy as sp
import scipy.linalg as sl
def gauss(x, A=1, mu=1, sigma=1):
"""
Evaluate Gaussian.
Parameters
----------
A : float
Amplitude.
mu : float
Mean.
std : float
Standard deviation.
"""
if sigma==0:
sigma = 1
return np.real(A * np.exp(-(x - mu)**2 / (2 * sigma**2)))
def fit_direct(x, y, F=0, weighted=True, _weights=None):
"""Fit a Gaussian to the given data.
Returns a fit so that y ~ gauss(x, A, mu, sigma)
Parameters
----------
x : ndarray
Sampling positions.
y : ndarray
Sampled values.
F : float
Ignore values of y <= F.
weighted : bool
Whether to use weighted least squares. If True, weigh
the error function by y, ensuring that small values
has less influence on the outcome.
Additional Parameters
---------------------
_weights : ndarray
Weights used in weighted least squares. For internal use
by fit_iterative.
Returns
-------
A : float
Amplitude.
mu : float
Mean.
std : float
Standard deviation.
"""
mask = (y > F)
x = x[mask]
y = y[mask]
if _weights is None:
_weights = y
else:
_weights = _weights[mask]
# We do not want to risk working with negative values
np.clip(y, 1e-10, np.inf, y)
e = np.ones(len(x))
if weighted:
e = e * (_weights**2)
v = (np.sum(np.vander(x, 5) * e[:, None], axis=0))[::-1]
A = v[sl.hankel([0, 1, 2], [2, 3, 4])]
ly = e * np.log(y)
ls = np.sum(ly)
x_ls = np.sum(ly * x)
xx_ls = np.sum(ly * x**2)
B = np.array([ls, x_ls, xx_ls])
(a, b, c), res, rank, s = np.linalg.lstsq(A, B)
A = np.exp(a - (b**2 / (4 * c)))
mu = -b / (2 * c)
sigma = sp.sqrt(-1 / (2 * c))
return A, mu, sigma
def fit_iterative(x, y, F=0, weighted=True, N=10):
"""Fit a Gaussian to the given data.
Returns a fit so that y ~ gauss(x, A, mu, sigma)
This function iteratively fits using fit_direct.
Parameters
----------
x : ndarray
Sampling positions.
y : ndarray
Sampled values.
F : float
Ignore values of y <= F.
weighted : bool
Whether to use weighted least squares. If True, weigh
the error function by y, ensuring that small values
has less influence on the outcome.
N : int
Number of iterations.
Returns
-------
A : float
Amplitude.
mu : float
Mean.
std : float
Standard deviation.
"""
y_ = y
for i in range(N):
p = fit_direct(x, y, weighted=True, _weights=y_)
A, mu, sigma = p
y_ = gauss(x, A, mu, sigma)
return np.real(A), np.real(mu), np.real(sigma)