|
| 1 | +import numpy as np |
| 2 | +import matplotlib.pyplot as plt |
| 3 | + |
| 4 | + |
| 5 | +def running_mean(x, win_size): |
| 6 | + """Compute a running mean with a given window size. |
| 7 | +
|
| 8 | + Parameters: |
| 9 | + x (ndarray): Input data. |
| 10 | + win_size (int): Window size. |
| 11 | +
|
| 12 | + Returns: |
| 13 | + ndarray: Smoothened data. |
| 14 | + """ |
| 15 | + return np.convolve(x, np.ones(w) / w, mode="valid") |
| 16 | + |
| 17 | + |
| 18 | +def running_mean(x, win_size, win_type=np.ones): |
| 19 | + """Compute a running mean with a given window size. |
| 20 | +
|
| 21 | + Parameters: |
| 22 | + x (ndarray): Input data. |
| 23 | + win_size (int): Window size. |
| 24 | + win_type (callable): A callable object that when passed a `win_size` |
| 25 | + will return an array of weights. |
| 26 | +
|
| 27 | + Returns: |
| 28 | + ndarray: Smoothened data. |
| 29 | + """ |
| 30 | + w = win_type(win_size) |
| 31 | + return np.convolve(x, w / w.sum(), mode="valid") |
| 32 | + |
| 33 | + |
| 34 | +def running_mean(x, win_size, win_type=np.ones, aggregation=None): |
| 35 | + """Compute a running mean with a given window size. |
| 36 | +
|
| 37 | + Parameters: |
| 38 | + x (ndarray): Input data. |
| 39 | + win_size (int): Window size. |
| 40 | + win_type (callable): A callable object that when passed a `win_size` |
| 41 | + will return an array of weights. |
| 42 | + aggregartion (callable): A callable object which aggregrates the data |
| 43 | + within the window region. By default, the function computes a |
| 44 | + running mean. |
| 45 | +
|
| 46 | + Note: |
| 47 | + In the default configuration, i.e. a running mean, the function makes |
| 48 | + use of a convolution which is implemented in a very efficient way. |
| 49 | + When passing the `aggregation` keyword this approach is no |
| 50 | + longer feasible because the data has to be explicitly "grouped". |
| 51 | +
|
| 52 | + Returns: |
| 53 | + ndarray: Smoothened data. |
| 54 | + """ |
| 55 | + w = win_type(win_size) |
| 56 | + |
| 57 | + if aggregation is None: |
| 58 | + # In the default case, we can use fast Fourier transform |
| 59 | + return np.convolve(x, w / w.sum(), mode="valid") |
| 60 | + else: |
| 61 | + # In the generic case, we have to group our array which is slower. |
| 62 | + # Therefore, it is good to have a separate API (i.e. keyword) for this case. |
| 63 | + return aggregation( |
| 64 | + np.lib.stride_tricks.sliding_window_view(x, win_size) * w, |
| 65 | + axis=1, |
| 66 | + ) / w.mean() |
| 67 | + |
| 68 | + |
| 69 | +def main(): |
| 70 | + windows = ( |
| 71 | + np.ones, |
| 72 | + np.bartlett, |
| 73 | + np.blackman, |
| 74 | + np.hamming, |
| 75 | + ) |
| 76 | + |
| 77 | + np.random.seed(1) |
| 78 | + x = np.random.randn(256) + 2 |
| 79 | + |
| 80 | + fig, ax = plt.subplots(figsize=(10, 6)) |
| 81 | + ax.plot(x, c="grey") |
| 82 | + for window in windows: |
| 83 | + ax.plot( |
| 84 | + running_mean(x, win_size=16, win_type=window), |
| 85 | + linewidth=2, |
| 86 | + label=window.__name__.capitalize(), |
| 87 | + ) |
| 88 | + ax.legend() |
| 89 | + ax.set_ylim(np.percentile(x, [5, 95])) |
| 90 | + |
| 91 | + plt.show() |
| 92 | + |
| 93 | + |
| 94 | +if __name__ == "__main__": |
| 95 | + main() |
0 commit comments