Search code examples
pythonpandasdataframematplotlibplotly

Extract local max and min and Plot an envelope function


My dataset:

I have the following dataset which when plotted using Plotly produces the following result (simplified): data points plotted time on x axis

My task:

I want to create an envelope function that extracts the local maxima and minima from data from the above dataset and plots an envelope curve. It would roughly look like this:

final result

I have tried approaching the solutions provided here and the one for the envelope provided here. However, none of them works for me in this approach. For some strange reason, the scatter plots are producing the following results (for local min), which is not exactly what I need.

Here's my code for the initial plot:

import plotly.express as px
import plotly.graph_objects as go

fig= go.Figure()

fig.add_traces(go.Scatter(x= mem_df['time'], y=mem_df['volatge']))

fig.update_layout(xaxis_title = r'$\text{Time } T \text{ in s}  $',
                      yaxis_title = r'$\text{Deflection angle } \text{ in radians}$')
fig.update_layout(title= r'$\text{Deflection angles versus time for the damping sheet}$')
fig.show()

Any help, in this case, would be appreciated! Thank you in advance!


Solution

  • The problem is that your data contains a bit of noise. One possible approach is to find an appropriate curve fit, then find the local maximum and minimum on the fit. scipy comes to help:

    import plotly.express as px
    import plotly.graph_objects as go
    import pandas as pd
    import numpy as np
    from scipy.optimize import curve_fit
    from scipy.signal import argrelextrema
    
    mem_df = pd.read_csv("path/to/file")
    x = mem_df['time'].values
    y = mem_df['deflection'].values
    # dampened oscillator
    func = lambda x, xi, k, phi, a, b: a * np.sin(k * x - phi) * np.exp(-xi * x) + b
    popt, pcov = curve_fit(func, x, y)
    
    fig= go.Figure()
    fig.add_traces(go.Scatter(x=x, y=y, name="data"))
    
    # plot the curve fit
    xx = np.linspace(0, 20, 150)
    yy = func(xx, *popt)
    fig.add_traces(go.Scatter(x=xx, y=yy, name="fit"))
    
    idx_max = argrelextrema(yy, np.greater)
    idx_min = argrelextrema(yy, np.less)
    fig.add_traces(go.Scatter(x=xx[idx_max], y=yy[idx_max], name="top"))
    fig.add_traces(go.Scatter(x=xx[idx_min], y=yy[idx_min], name="bottom"))
    
    fig.update_layout(xaxis_title = r'$\text{Time } T \text{ in s}  $',
                          yaxis_title = r'$\text{Deflection angle } \text{ in radians}$')
    fig.update_layout(title= r'$\text{Deflection angles versus time for the damping sheet}$')
    fig.show()
    

    enter image description here