|
54 | 54 | import plotly.graph_objects as go
|
55 | 55 | from plotly.subplots import make_subplots
|
56 | 56 |
|
| 57 | +import EntropyHub as EH |
57 | 58 |
|
58 | 59 | # For deprecating old functions
|
59 | 60 | import deprecation
|
|
66 | 67 |
|
67 | 68 | class TimeSeries:
|
68 | 69 | """
|
69 |
| - Univariate time series data on which to compute early warning signals. |
| 70 | + Class to hold univariate time series data on which to |
| 71 | + compute early warning signals. |
70 | 72 |
|
71 | 73 | Parameters
|
72 | 74 | ----------
|
@@ -116,7 +118,7 @@ def __init__(self, data, transition=None):
|
116 | 118 | def detrend(self, method="Gaussian", bandwidth=0.2, span=0.2):
|
117 | 119 | """
|
118 | 120 | Detrend the time series using a chosen method.
|
119 |
| - Add column to the dataframe for 'smoothing' and 'residuals' |
| 121 | + Output is stored in the self.state dataframe. |
120 | 122 |
|
121 | 123 | Parameters
|
122 | 124 | ----------
|
@@ -182,8 +184,7 @@ def compute_var(self, rolling_window=0.25):
|
182 | 184 | Compute variance over a rolling window.
|
183 | 185 | If residuals have not been computed, computation will be
|
184 | 186 | performed over state variable.
|
185 |
| -
|
186 |
| - Put into 'ews' dataframe |
| 187 | + Output is stored in the self.ews dataframe. |
187 | 188 |
|
188 | 189 | Parameters
|
189 | 190 | ----------
|
@@ -224,8 +225,8 @@ def compute_std(self, rolling_window=0.25):
|
224 | 225 | Compute standard deviation over a rolling window.
|
225 | 226 | If residuals have not been computed, computation will be
|
226 | 227 | performed over state variable.
|
| 228 | + Output is stored in the self.ews dataframe. |
227 | 229 |
|
228 |
| - Put into 'ews' dataframe |
229 | 230 |
|
230 | 231 | Parameters
|
231 | 232 | ----------
|
@@ -268,8 +269,7 @@ def compute_cv(self, rolling_window=0.25):
|
268 | 269 | mean of the state variable.
|
269 | 270 | If residuals have not been computed, computation will be
|
270 | 271 | performed over state variable.
|
271 |
| -
|
272 |
| - Put into 'ews' dataframe |
| 272 | + Output is stored in the self.ews dataframe. |
273 | 273 |
|
274 | 274 | Parameters
|
275 | 275 | ----------
|
@@ -313,7 +313,10 @@ def compute_cv(self, rolling_window=0.25):
|
313 | 313 |
|
314 | 314 | def compute_auto(self, rolling_window=0.25, lag=1):
|
315 | 315 | """
|
316 |
| - Compute autocorrelation over a rolling window. Add to dataframe. |
| 316 | + Compute autocorrelation at a give lag over a rolling window. |
| 317 | + If residuals have not been computed, computation will be |
| 318 | + performed over state variable. |
| 319 | + Output is stored in the self.ews dataframe. |
317 | 320 |
|
318 | 321 | Parameters
|
319 | 322 | ----------
|
@@ -364,8 +367,8 @@ def compute_skew(self, rolling_window=0.25):
|
364 | 367 | Compute skew over a rolling window.
|
365 | 368 | If residuals have not been computed, computation will be
|
366 | 369 | performed over state variable.
|
| 370 | + Output is stored in the self.ews dataframe. |
367 | 371 |
|
368 |
| - Add to dataframe. |
369 | 372 |
|
370 | 373 | Parameters
|
371 | 374 | ----------
|
@@ -406,8 +409,8 @@ def compute_kurt(self, rolling_window=0.25):
|
406 | 409 | Compute kurtosis over a rolling window.
|
407 | 410 | If residuals have not been computed, computation will be
|
408 | 411 | performed over state variable.
|
| 412 | + Output is stored in the self.ews dataframe. |
409 | 413 |
|
410 |
| - Add to dataframe. |
411 | 414 |
|
412 | 415 | Parameters
|
413 | 416 | ----------
|
@@ -443,6 +446,99 @@ def compute_kurt(self, rolling_window=0.25):
|
443 | 446 |
|
444 | 447 | self.ews["kurtosis"] = kurt_values
|
445 | 448 |
|
| 449 | + def compute_entropy(self, rolling_window=0.25, method="sample", **kwargs): |
| 450 | + """ |
| 451 | + Compute entropy using a given method over a rolling window. |
| 452 | + Uses EntropyHub https://www.entropyhub.xyz/Home.html. |
| 453 | + If residuals have not been computed, computation will be |
| 454 | + performed over state variable. |
| 455 | + Output is stored in the self.ews dataframe. |
| 456 | +
|
| 457 | + Parameters |
| 458 | + ---------- |
| 459 | + rolling_window : float |
| 460 | + Length of rolling window used to compute variance. Can be specified |
| 461 | + as an absolute value or as a proportion of the length of the |
| 462 | + data being analysed. Default is 0.25. |
| 463 | + method : str |
| 464 | + The method by which to compute entropy. Options include 'sample', |
| 465 | + 'approximate', and 'kolmogorov' |
| 466 | + **kwargs |
| 467 | + Keyword arguments for the EntropyHub function |
| 468 | +
|
| 469 | + Returns |
| 470 | + ------- |
| 471 | + None. |
| 472 | +
|
| 473 | + """ |
| 474 | + |
| 475 | + # Get time series data prior to transition |
| 476 | + if self.transition: |
| 477 | + df_pre = self.state[self.state.index <= self.transition] |
| 478 | + else: |
| 479 | + df_pre = self.state |
| 480 | + |
| 481 | + # Get absolute size of rollling window if given as a proportion |
| 482 | + if 0 < rolling_window <= 1: |
| 483 | + rw_absolute = int(rolling_window * len(df_pre)) |
| 484 | + else: |
| 485 | + rw_absolute = rolling_window |
| 486 | + |
| 487 | + # Get data on which to compute entropy |
| 488 | + if "residuals" in df_pre.columns: |
| 489 | + ts_vals = df_pre["residuals"] |
| 490 | + else: |
| 491 | + ts_vals = df_pre["state"] |
| 492 | + |
| 493 | + # Compute sample entropy |
| 494 | + if method == "sample": |
| 495 | + |
| 496 | + def entropy_func(series): |
| 497 | + Samp, A, B = EH.SampEn(series.values, **kwargs) |
| 498 | + return Samp |
| 499 | + |
| 500 | + # Compute approxiamte entropy |
| 501 | + if method == "approximate": |
| 502 | + |
| 503 | + def entropy_func(series): |
| 504 | + Ap, Phi = EH.ApEn(series.values, **kwargs) |
| 505 | + return Ap |
| 506 | + |
| 507 | + # Compute komogorov entropy |
| 508 | + if method == "kolmogorov": |
| 509 | + |
| 510 | + def entropy_func(series): |
| 511 | + K2, Ci = EH.K2En(series.values, **kwargs) |
| 512 | + return K2 |
| 513 | + |
| 514 | + list_times = [] |
| 515 | + list_entropy = [] |
| 516 | + # Set up rolling window (cannot use pandas.rolling as multi-output fn) |
| 517 | + for k in np.arange(0, len(ts_vals) - (rw_absolute - 1)): |
| 518 | + # Select subset of series contained in window |
| 519 | + window_series = ts_vals.iloc[k : k + rw_absolute] |
| 520 | + # Assign the time value for the metrics (right end point of window) |
| 521 | + t_point = ts_vals.index[k + (rw_absolute - 1)] |
| 522 | + # Compute entropy (value for each channel) |
| 523 | + entropy = entropy_func(window_series) |
| 524 | + |
| 525 | + list_times.append(t_point) |
| 526 | + list_entropy.append(entropy) |
| 527 | + |
| 528 | + ar_entropy = np.array(list_entropy) |
| 529 | + |
| 530 | + df_entropy = pd.DataFrame() |
| 531 | + df_entropy["time"] = list_times |
| 532 | + num_embedding_dims = ar_entropy.shape[1] |
| 533 | + for dim in range(num_embedding_dims): |
| 534 | + df_entropy["{}-entropy-{}".format(method, dim)] = ar_entropy[:, dim] |
| 535 | + df_entropy.set_index("time", inplace=True) |
| 536 | + |
| 537 | + # Add info to self.ews dataframe |
| 538 | + for col in df_entropy.columns: |
| 539 | + self.ews[col] = df_entropy[col] |
| 540 | + # self.ews = pd.merge(self.ews, df_entropy, how="left", on="time") |
| 541 | + |
446 | 542 | def compute_ktau(self, tmin="earliest", tmax="latest"):
|
447 | 543 | """
|
448 | 544 | Compute kendall tau values of CSD-based EWS.
|
|
0 commit comments