Conditions | 8 |
Total Lines | 101 |
Code Lines | 55 |
Lines | 0 |
Ratio | 0 % |
Changes | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
1 | # Licensed under a 3-clause BSD style license - see LICENSE |
||
89 | def OU_fit(self, bins=None, rang=None, a=1e-5, b=100): |
||
90 | """Fit the signal to an OU stochastic process, using several statistical approaches. |
||
91 | |||
92 | This function tries to fit the signal to an OU stochastic |
||
93 | process using both basic curve fitting and the Maximum |
||
94 | Likelihood Estimation method, and returns some plots of |
||
95 | the signals and its properties, and the estimated parameters. |
||
96 | """ |
||
97 | |||
98 | # TODO: make a generic fit method |
||
99 | res = dict() |
||
100 | |||
101 | y = self.values |
||
102 | t = self.times |
||
103 | |||
104 | dy = np.diff(y) |
||
105 | dt = np.diff(t) |
||
106 | |||
107 | # estimate sigma |
||
108 | try: |
||
109 | sigma_est = (np.nanmean(dy ** 2 / y[:-1] ** 2 / dt)) ** 0.5 |
||
110 | res["sigma_est"] = sigma_est |
||
111 | except Exception as e: |
||
112 | log.error(f"Could not estimate sigma: {e}") |
||
113 | |||
114 | # plot histogram |
||
115 | if bins is None: |
||
116 | bins = int(y.size ** 0.5 / 1.5) # bins='auto' |
||
117 | if rang is None: |
||
118 | rang = (np.percentile(y, 0), np.percentile(y, 99)) |
||
119 | |||
120 | p, x = np.histogram(y, density=True, bins=bins, range=rang) # bins='sqrt') |
||
121 | x = (x + np.roll(x, -1))[:-1] / 2.0 |
||
122 | |||
123 | plt.subplots() |
||
124 | |||
125 | plt.hist(y, density=True, alpha=0.75, bins=bins, range=rang) |
||
126 | plt.plot(x, p, "r-", alpha=0.5) |
||
127 | |||
128 | anchored_text = AnchoredText( |
||
129 | f"mean {np.mean(y):.2g} \n " |
||
130 | f"median {np.median(y):.2g} \n " |
||
131 | f"mode {scipy_stats.mode(y)[0]:.2g} \n " |
||
132 | f"std {np.std(y):.2g} \n " |
||
133 | f"var {np.var(y):.2g}", |
||
134 | loc="upper right", |
||
135 | ) |
||
136 | plt.gca().add_artist(anchored_text) |
||
137 | |||
138 | # curve_fit |
||
139 | try: |
||
140 | popt, pcov = scipy_optimize.curve_fit(f=self.pdf, xdata=x, ydata=p) |
||
141 | # print('curve_fit: (l, mu)') |
||
142 | # print('popt: ') |
||
143 | # print(popt) |
||
144 | # print('pcov: ') |
||
145 | # print(np.sqrt(np.diag(pcov))) |
||
146 | x_c = np.linspace(1e-5, 1.1 * np.max(x), 1000) |
||
147 | plt.plot(x_c, self.pdf(x_c, *popt), "k-", label="curve_fit", alpha=0.8) |
||
148 | res["curve_fit"] = (popt, np.sqrt(np.diag(pcov))) |
||
149 | except Exception as e: |
||
150 | log.error(f"Some error fitting with curve_fit {e}") |
||
151 | |||
152 | # fit pdf with MLE |
||
153 | # TODO: place outside as a helper class |
||
154 | # mu here is different than self.mu |
||
155 | class OU(scipy_stats.rv_continuous): |
||
156 | def _pdf(self, x, l, mu): |
||
157 | return ( |
||
158 | (l * mu) ** (1 + l) |
||
159 | / scipy_special.gamma(1 + l) |
||
160 | * np.exp(-l * mu / x) |
||
161 | / x ** (l + 2) |
||
162 | ) |
||
163 | |||
164 | try: |
||
165 | fit = OU(a=a, b=np.percentile(y, b)).fit(y, 1, 1, floc=0, fscale=1) |
||
166 | # print('MLE fit: (l, mu)') |
||
167 | # print(fit) |
||
168 | x_c = np.linspace(0, 1.1 * np.max(x), 1000) |
||
169 | plt.plot(x_c, self.pdf(x_c, fit[0], fit[1]), "k-.", label="MLE", alpha=0.8) |
||
170 | res["MLE_fit"] = fit[:-2] |
||
171 | except Exception as e: |
||
172 | log.error(f"Some error fitting with MLE {e}") |
||
173 | |||
174 | plt.legend(loc="lower right") |
||
175 | # plt.show() |
||
176 | |||
177 | # estimate theta (from curve_fit) |
||
178 | try: |
||
179 | res["th_est1"] = fit[0] * sigma_est ** 2 / 2 |
||
180 | except NameError as e: |
||
181 | res["th_est1"] = None |
||
182 | |||
183 | # estimate theta (from MLE) |
||
184 | try: |
||
185 | res["th_est2"] = popt[0] * sigma_est ** 2 / 2 |
||
186 | except NameError as e: |
||
187 | res["th_est2"] = None |
||
188 | |||
189 | return res |
||
190 | |||
280 |