pythonpandasmatplotlibcurve-fittingsmoothing

Smoothing out the sharp corners and jumps of a piecewise regression load-displacement curve in python


I am having a stubborn problem with smoothing out some sharp corners that the simulation software does not really like.

I have the following displacement/ load/ damage vs step/time:

Data over time

The source data can be found here.

Here's the code for importing the data and plotting the above plot:

df = pd.read_csv("ExampleforStack.txt") # read data
x = df["Displacement"] # get displacement
y = df["Load"] # get load
d = df["Damage"] # get damage
# plot stuff
plt.figure()
plt.subplot(3,1,1)
plt.plot(x)
plt.grid()
plt.ylabel("Displacement")
plt.subplot(3,1,2)
plt.plot(y)
plt.grid()
plt.ylabel("Load")
plt.subplot(3,1,3)
plt.plot(d)
plt.grid()
plt.ylabel("Damage")
plt.xlabel("Step")
plt.gcf().align_ylabels()
plt.tight_layout()

When plotted against displacement, the load and damage look something like this:

Load, Damage vs Displacement

The breaking points in the above plots are:

print(bps)
# [0.005806195310298627, 0.02801208361344569]

My aim would be to smooth the data around the vertical black lines for both the load and the damage.

So far, I tried lowess from statsmodels.api.nonparametric, with the results looking very suboptimal:

LOWESS at frac 0.03

The above picture is with a frac of 0.03, changing the frac of course changes a lot, but sadly not in a desirable way either.

Others stuff that I have tried are Gaussian regression models, Singular Spectrum Analysis, Savitzky-Golay filters, cubic splines, etc...

The only thing that I have not checked so far is curve fitting, which I might check tomorrow.

Background information:

Qualitatively, here's what I would like the end result to look like:

Smoothed

An additionaly requirement would be that the derivative of the smothed data should also be smooth and not jumpy.

I would appreciate any hints to help me solve this task! :D


As suggested by Martin Brown, I did the following to smooth out the curves:

def boxCar(data, winSize):
    kernel = np.ones(winSize) / winSize # generate the kernel
    dataSmoothed = convolve(data, kernel, mode='same') # convolve
    # the next two lines is to correct the smoothing on the start and end of the arrays
    dataSmoothed[0:winSize] = data[0:winSize] # assign first elements to original data
    dataSmoothed[-winSize:] = data[-winSize:] # assign last elements to original data
    return dataSmoothed

The convolve is from scipy.signal.


Another approach with the gaussian would look something like this:

def gaussian(data, sigma):
    dataSmoothed = gaussian_filter1d(data, sigma=sigma)
    dataSmoothed[0:50] = data[0:50] # assign first elements to original data
    dataSmoothed[-50:] = data[-50:] # assign last elements to original data
    return dataSmoothed

The Gaussian seems to work a bit better than boxCar. gaussian_filter1d is from scipy.ndimage


Solution

  • Simplest solution is apply a low pass filter to your sharp cornered function(s). Convolving it with a Gaussian with an appropriate width should give it all of the smoothness properties that you desire. The data are so finely spaced that you might even be able to get away with a simple boxcar average over 11-21 samples (choose an odd number).

    However, it might be preferable to sort out the bugs in the simulation software that prevent it from working correctly with realistic data. The onset of damage is almost always an all or nothing sudden change so the code should be able to handle that. Filtering data to make analysis code work would not be my first choice.