Source code for patato.utils.time_series_analysis

#  Copyright (c) Thomas Else 2023-25.
#  License: MIT

import numpy as np
from scipy.signal import fftconvolve


[docs] def find_gc_boundaries( mask, data_so2, window=10, display=False, sigma=2, skip_start=0, sign=1 ): """ Find the runs in the time series at which the breathing gas of the animal was changed. Parameters ---------- mask : array_like A boolean array which can be applied to the time series. data_so2 : SingleParameterData The time series of the SO2 data. window : int, optional The window size to use for the convolution. display : bool, optional Whether to display the results. sigma : float, optional The sigma to use for the convolution. skip_start : int, optional The number of runs to skip at the start of the time series. sign : int, optional The sign of the change in the time series. Returns ------- list List of integers with the indices of the detected gas changes. """ # STEP 1: Smooth the curve with a Gaussian kernel # Complete error here: measurement = np.squeeze(data_so2.raw_data.T[mask.T].T) if sign != 2: measurement *= sign kernel = np.arange(window) - window / 2 + 1 / 2 kernel = np.exp(-((kernel / sigma) ** 2)) kernel /= np.sum(kernel) kernel = kernel[:, None] from scipy.ndimage import median_filter smoothed = median_filter(measurement, kernel.shape) smoothed = fftconvolve(smoothed, kernel, "valid") # Find the gradient of the curve (to give us the maximum). smoothed_grad = np.median(np.gradient(smoothed, axis=0), axis=-1) steps = [skip_start] if sign == 2: smoothed_grad = smoothed_grad**2 # Find first peak in derivative. step_point = np.argmax(smoothed_grad) steps.append(step_point + window // 2) step_grad = smoothed_grad[step_point] # Find second peak in the derivative step_point_b = step_point + np.argmin(smoothed_grad[step_point:]) step_grad_b = smoothed_grad[step_point_b] # Custom stuff to try and avoid false positives (this could be improved somehow). if step_grad_b < -step_grad / 2 and step_point_b - step_point > step_point * 0.5: # I.e. detect if there actually was a second change. steps.append(step_point_b + window // 2) steps.append(len(measurement) - 1) else: steps.append(len(measurement) - 1) # Display the changeover points. if display: import matplotlib.pyplot as plt plt.plot( np.median(smoothed * sign, axis=-1), label="Gas Challenge Trace in the Reference Region", ) plt.twinx() plt.plot( smoothed_grad * sign, c="C1", label="Derivative of Gas Challenge Trace in the Reference Region", ) for s in steps: plt.axvline(s - window // 2) plt.gcf().legend() plt.show() return steps