# How to automatically identify the start and stop times of a "ramp" seen in time series?

I am analyzing pressure data sampled at 1Hz. The times series exhibit "ramps" (a linear increase in pressure followed by a sudden drop) for which I would like to automatically detect the start and stop times. Please note that these pressure events are not periodic:

My first attempt was to use SciPy's find_peaks function on a smoothed version of my data.

I first smoothed these time series using the function described in the SciPy's cookbook:

smoothed_pressure = smooth(df['Pressure'], window_len=21)


I then applied find_peaks to find the maximum peaks (in red) above the mean_amplitude of the smoothed data and the minimum peaks (in yellow) below this amplitude:

mean_amplitude = np.mean(smoothed_pressure)
max_indices = find_peaks(smoothed_pressure, distance=200, height=mean_amplitude)[0]
min_indices = find_peaks(smoothed_pressure, distance=200, height=[0,mean_amplitude])[0]

plt.figure(figsize=(20,3))
plt.title(filename)
plt.plot(df['Time'], df["Pressure"])
plt.scatter(df[df.index.isin(max_indices-11)]['Time'], df[df.index.isin(max_indices-11)]["Pressure"], color='red', zorder=5)
plt.scatter(df[df.index.isin(min_indices-11)]['Time'], df[df.index.isin(min_indices-11)]["Pressure"], color='yellow', zorder=5)


After tweaking the distance I managed to get this strategy to work for this specific example, but this solution is not robust enough and fails when applied to other datasets.

I am looking for other strategies to deal with this problem.

I am thinking of differentiating the data before trying to identify the peaks. I am also open to trying machine learning strategies. Any other idea will be welcome!

------------------------------------------------------------------------------------------------------------

EDIT: IMPLEMENTING MAXTRON'S SOLUTION

As suggested by Maxtron in his answer below, computing the second order derivative is an excellent way to nail down the stop time of each ramp.

In the example below I apply the second order derivative on a (heavily) smoothed version of the original data:

ds=pd.DataFrame()
mylength=61
smoothed_time = smooth(df['Time'], window_len=mylength)
smoothed_pressure = smooth(df['Pressure'], window_len=mylength)
ds['Pressure']=smoothed_pressure
ds['Time']=smoothed_time


I then compute the first and second order derivatives of this smoothed signal:

ds['Pressure_first_derivative']=ds['Pressure'].diff() / ds['Time'].diff()
ds['Pressure_second_derivative']=ds['Pressure_first_derivative'].diff() / ds['Time'].diff()


The second order derivative looks like this:

Identifying the samples exceeding a user-defined threshold of 0.15 is a good starting point to identify the end of each ramp:

max_indices=ds[ds['Pressure_second_derivative']>0.15].index-np.int(mylength/2)


There are probably ways to automate the thresholding. Also, because multiple samples exceed this threshold for each peak, I think that find_peaks would still be needed to boil down each group of points to a single sample.

Approach: If the original signal $$\mathbf{x}$$ is given as $$x(0), \ldots, x(N-1)$$, then the second derivative is given as
\begin{align} y[t] = x[t] - 2 x[t-1] + x[t-2], \quad 2 \le t < N \end{align}
The second derivative is sparse at points when the ramp signal ends. You can avoid find_peaks function when you use the second derivative signal. Applying a simple thresholding technique should help you find the location of the peaks.
• I tried implementing the approach that you suggested: I edited my post accordingly. I think that your solution is great to identify the end of each ramp, but now I must still find a workaround to identify the beginning of each ramp. Also, as stated above, I think that find_peaks will still come in handy. Anyways, thanks again for your answer! Dec 31, 2021 at 11:28