|
1 | 1 | """ |
2 | 2 | Metrics and evaluations? |
3 | 3 | """ |
4 | | -import numpy as _np |
5 | | -import matplotlib.pyplot as _plt |
6 | | -import scipy.stats as _scipy_stats |
| 4 | +import numpy as np |
| 5 | +import matplotlib.pyplot as plt |
| 6 | +from scipy import stats |
7 | 7 |
|
8 | 8 | # local imports |
9 | 9 | from pynumdiff.utils import utility as _utility |
|
13 | 13 | # pylint: disable-msg=too-many-locals, too-many-arguments |
14 | 14 | def plot(x, dt, x_hat, dxdt_hat, x_truth, dxdt_truth, xlim=None, ax_x=None, ax_dxdt=None, |
15 | 15 | show_error=True, markersize=5): |
16 | | - """ |
17 | | - Make comparison plots of 'x (blue) vs x_truth (black) vs x_hat (red)' and |
| 16 | + """Make comparison plots of 'x (blue) vs x_truth (black) vs x_hat (red)' and |
18 | 17 | 'dxdt_truth (black) vs dxdt_hat (red)' |
19 | 18 |
|
20 | | - :param x: array of noisy time series |
21 | | - :type x: np.array (float) |
22 | | -
|
23 | | - :param dt: a float number representing the step size |
24 | | - :type dt: float |
25 | | -
|
26 | | - :param x_hat: array of smoothed estimation of x |
27 | | - :type x_hat: np.array (float) |
28 | | -
|
29 | | - :param dxdt_hat: array of estimated derivative |
30 | | - :type dxdt_hat: np.array (float) |
31 | | -
|
32 | | - :param x_truth: array of noise-free time series |
33 | | - :type x_truth: np.array (float) |
34 | | -
|
35 | | - :param dxdt_truth: array of true derivative |
36 | | - :type dxdt_truth: np.array (float) |
37 | | -
|
38 | | - :param xlim: a list specifying range of x |
39 | | - :type xlim: list (2 integers), optional |
40 | | -
|
41 | | - :param ax_x: axis of the first plot |
42 | | - :type ax_x: :class:`matplotlib.axes`, optional |
43 | | -
|
44 | | - :param ax_dxdt: axis of the second plot |
45 | | - :type ax_dxdt: :class:`matplotlib.axes`, optional |
46 | | -
|
47 | | - :param show_error: whether to show the rmse |
48 | | - :type show_error: boolean, optional |
49 | | -
|
50 | | - :param markersize: marker size of noisy observations |
51 | | - :type markersize: int, optional |
| 19 | + :param np.array[float] x: array of noisy data |
| 20 | + :param float dt: a float number representing the step size |
| 21 | + :param np.array[float] x_hat: array of smoothed estimation of x |
| 22 | + :param np.array[float] dxdt_hat: array of estimated derivative |
| 23 | + :param np.array[float] x_truth: array of noise-free time series |
| 24 | + :param np.array[float] dxdt_truth: array of true derivative |
| 25 | + :param list[int] xlim: a list specifying range of x |
| 26 | + :param matplotlib.axes ax_x: axis of the first plot |
| 27 | + :param matplotlib.axes ax_dxdt: axis of the second plot |
| 28 | + :param bool show_error: whether to show the rmse |
| 29 | + :param int markersize: marker size of noisy observations |
52 | 30 |
|
53 | 31 | :return: Display two plots |
54 | | - :rtype: None |
55 | 32 | """ |
56 | | - t = _np.arange(0, dt*len(x), dt) |
| 33 | + t = np.arange(0, dt*len(x), dt) |
57 | 34 | if ax_x is None and ax_dxdt is None: |
58 | | - fig = _plt.figure(figsize=(20, 6)) |
| 35 | + fig = plt.figure(figsize=(20, 6)) |
59 | 36 | ax_x = fig.add_subplot(121) |
60 | 37 | ax_dxdt = fig.add_subplot(122) |
61 | 38 |
|
@@ -89,125 +66,81 @@ def plot(x, dt, x_hat, dxdt_hat, x_truth, dxdt_truth, xlim=None, ax_x=None, ax_d |
89 | 66 | print('RMS error in velocity: ', rms_dxdt) |
90 | 67 |
|
91 | 68 |
|
92 | | -def __rms_error__(a, e): |
93 | | - """ |
94 | | - Calculate rms error |
| 69 | +def _rms_error(a, e): |
| 70 | + """Calculate rms error |
95 | 71 |
|
96 | 72 | :param a: the first array |
97 | 73 | :param e: the second array |
98 | | - :return: a float number representing the rms error |
| 74 | +
|
| 75 | + :return: (float) -- the root mean squared error |
99 | 76 | """ |
100 | | - if _np.max(_np.abs(a-e)) > 1e16: |
| 77 | + if np.max(np.abs(a-e)) > 1e16: |
101 | 78 | return 1e16 |
102 | | - s_error = _np.ravel((a - e))**2 |
103 | | - ms_error = _np.mean(s_error) |
104 | | - rms_error = _np.sqrt(ms_error) |
| 79 | + s_error = np.ravel((a - e))**2 |
| 80 | + ms_error = np.mean(s_error) |
| 81 | + rms_error = np.sqrt(ms_error) |
105 | 82 | return rms_error |
106 | 83 |
|
107 | 84 |
|
108 | 85 | def metrics(x, dt, x_hat, dxdt_hat, x_truth=None, dxdt_truth=None, padding=None): |
109 | | - """ |
110 | | - Evaluate x_hat based on various metrics, depending on whether dxdt_truth and x_truth are known or not. |
111 | | -
|
112 | | - :param x: time series that was differentiated |
113 | | - :type x: np.array |
114 | | -
|
115 | | - :param dt: time step in seconds |
116 | | - :type dt: float |
117 | | -
|
118 | | - :param x_hat: estimated (smoothed) x |
119 | | - :type x_hat: np.array |
120 | | -
|
121 | | - :param dxdt_hat: estimated xdot |
122 | | - :type dxdt_hat: np.array |
123 | | -
|
124 | | - :param x_truth: true value of x, if known, optional |
125 | | - :type x_truth: np.array like x or None |
126 | | -
|
127 | | - :param dxdt_truth: true value of dxdt, if known, optional |
128 | | - :type dxdt_truth: np.array like x or None |
129 | | -
|
130 | | - :param padding: number of snapshots on either side of the array to ignore when calculating the metric. If autor or None, defaults to 2.5% of the size of x |
131 | | - :type padding: int, None, or auto |
132 | | -
|
133 | | - :return: a tuple containing the following: |
134 | | - - rms_rec_x: RMS error between the integral of dxdt_hat and x |
135 | | - - rms_x: RMS error between x_hat and x_truth, returns None if x_truth is None |
136 | | - - rms_dxdt: RMS error between dxdt_hat and dxdt_truth, returns None if dxdt_hat is None |
137 | | - :rtype: tuple -> (float, float, float) |
138 | | -
|
| 86 | + """Evaluate x_hat based on various metrics, depending on whether dxdt_truth and x_truth are known or not. |
| 87 | +
|
| 88 | + :param np.array[float] x: data that was differentiated |
| 89 | + :param float dt: step size |
| 90 | + :param np.array[float] x_hat: estimated (smoothed) x |
| 91 | + :param np.array[float] dxdt_hat: estimated xdot |
| 92 | + :param np.array[float] x_truth: true value of x, if known |
| 93 | + :param np.array[float] dxdt_truth: true value of dxdt, if known, optional |
| 94 | + :param int padding: number of snapshots on either side of the array to ignore when calculating |
| 95 | + the metric. If :code:`'auto'` or :code:`None`, defaults to 2.5% of the size of x |
| 96 | +
|
| 97 | + :return: tuple[float, float, float] containing\n |
| 98 | + - **rms_rec_x** -- RMS error between the integral of dxdt_hat and x |
| 99 | + - **rms_x** -- RMS error between x_hat and x_truth, returns None if x_truth is None |
| 100 | + - **rms_dxdt** -- RMS error between dxdt_hat and dxdt_truth, returns None if dxdt_hat is None |
139 | 101 | """ |
140 | 102 | if padding is None or padding == 'auto': |
141 | 103 | padding = int(0.025*len(x)) |
142 | 104 | padding = max(padding, 1) |
143 | | - if _np.isnan(x_hat).any(): |
144 | | - return _np.nan, _np.nan, _np.nan |
| 105 | + if np.isnan(x_hat).any(): |
| 106 | + return np.nan, np.nan, np.nan |
145 | 107 |
|
146 | 108 | # RMS dxdt |
147 | 109 | if dxdt_truth is not None: |
148 | | - rms_dxdt = __rms_error__(dxdt_hat[padding:-padding], dxdt_truth[padding:-padding]) |
| 110 | + rms_dxdt = _rms_error(dxdt_hat[padding:-padding], dxdt_truth[padding:-padding]) |
149 | 111 | else: |
150 | 112 | rms_dxdt = None |
151 | 113 |
|
152 | 114 | # RMS x |
153 | 115 | if x_truth is not None: |
154 | | - rms_x = __rms_error__(x_hat[padding:-padding], x_truth[padding:-padding]) |
| 116 | + rms_x = _rms_error(x_hat[padding:-padding], x_truth[padding:-padding]) |
155 | 117 | else: |
156 | 118 | rms_x = None |
157 | 119 |
|
158 | 120 | # RMS reconstructed x |
159 | 121 | rec_x = _utility.integrate_dxdt_hat(dxdt_hat, dt) |
160 | 122 | x0 = _utility.estimate_initial_condition(x, rec_x) |
161 | 123 | rec_x = rec_x + x0 |
162 | | - rms_rec_x = __rms_error__(rec_x[padding:-padding], x[padding:-padding]) |
| 124 | + rms_rec_x = _rms_error(rec_x[padding:-padding], x[padding:-padding]) |
163 | 125 |
|
164 | 126 | return rms_rec_x, rms_x, rms_dxdt |
165 | 127 |
|
166 | 128 |
|
167 | 129 | def error_correlation(dxdt_hat, dxdt_truth, padding=None): |
168 | | - """ |
169 | | - Calculate the error correlation (pearsons correlation coefficient) between the estimated dxdt and true dxdt |
| 130 | + """Calculate the error correlation (pearsons correlation coefficient) between the estimated |
| 131 | + dxdt and true dxdt |
170 | 132 |
|
171 | | - :param dxdt_hat: estimated xdot |
172 | | - :type dxdt_hat: np.array |
173 | | -
|
174 | | - :param dxdt_truth: true value of dxdt, if known, optional |
175 | | - :type dxdt_truth: np.array like x or None |
176 | | -
|
177 | | - :param padding: number of snapshots on either side of the array to ignore when calculating the metric. If autor or None, defaults to 2.5% of the size of x |
178 | | - :type padding: int, None, or auto |
179 | | -
|
180 | | - :return: r-squared correlation coefficient |
181 | | - :rtype: float |
| 133 | + :param np.array[float] dxdt_hat: estimated xdot |
| 134 | + :param np.array[float] dxdt_truth: true value of dxdt, if known, optional |
| 135 | + :param int padding: number of snapshots on either side of the array to ignore when calculating |
| 136 | + the metric. If auto or None, defaults to 2.5% of the size of x |
182 | 137 |
|
| 138 | + :return: (float) -- r-squared correlation coefficient |
183 | 139 | """ |
184 | 140 | if padding is None or padding == 'auto': |
185 | 141 | padding = int(0.025*len(dxdt_hat)) |
186 | 142 | padding = max(padding, 1) |
187 | 143 | errors = (dxdt_hat[padding:-padding] - dxdt_truth[padding:-padding]) |
188 | | - r = _scipy_stats.linregress(dxdt_truth[padding:-padding] - |
189 | | - _np.mean(dxdt_truth[padding:-padding]), errors) |
| 144 | + r = stats.linregress(dxdt_truth[padding:-padding] - |
| 145 | + np.mean(dxdt_truth[padding:-padding]), errors) |
190 | 146 | return r.rvalue**2 |
191 | | - |
192 | | - |
193 | | -def rmse(dxdt_hat, dxdt_truth, padding=None): |
194 | | - """ |
195 | | - Calculate the Root Mean Squared Error between the estimated dxdt and true dxdt |
196 | | -
|
197 | | - :param dxdt_hat: estimated xdot |
198 | | - :type dxdt_hat: np.array |
199 | | -
|
200 | | - :param dxdt_truth: true value of dxdt, if known, optional |
201 | | - :type dxdt_truth: np.array like x or None |
202 | | -
|
203 | | - :param padding: number of snapshots on either side of the array to ignore when calculating the metric. If autor or None, defaults to 2.5% of the size of x |
204 | | - :type padding: int, None, or auto |
205 | | -
|
206 | | - :return: Root Mean Squared Error |
207 | | - :rtype: float |
208 | | - """ |
209 | | - if padding is None or padding == 'auto': |
210 | | - padding = int(0.025*len(dxdt_hat)) |
211 | | - padding = max(padding, 1) |
212 | | - RMSE = _np.sqrt(_np.mean((dxdt_hat[padding:-padding] - dxdt_truth[padding:-padding])**2)) |
213 | | - return RMSE |
0 commit comments