我遇到了一个顽固的问题,那就是如何平滑模拟软件不太喜欢的一些尖角。

我有以下位移/负载/损坏与步骤/时间的关系:

源数据可以找到。

以下是导入数据和绘制上述图表的代码:

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()

当绘制位移图时,载荷和损坏看起来如下所示:

上述情节的断点如下:

print(bps)
# [0.005806195310298627, 0.02801208361344569]

我的目标是平滑负载和损坏的垂直黑线周围的数据。

到目前为止,我尝试了 lowess statsmodels.api.nonparametric,但结果看起来非常不理想:

上图的小数部分为 0.03,改变小数部分当然会带来很大的变化,但遗憾的是也不是理想的结果。

我尝试过的其他东西是高斯回归模型、奇异谱分析、Savitzky-Golay 滤波器、三次样条等……

到目前为止我唯一还没有检查的是曲线拟合,我可能会在明天检查。

背景信息:

  • 位移是 DIC 分析的结果
  • 通过试验机测量负载
  • 损伤是根据位移、载荷和材料在弹性区域的刚度计算得出的值。

从质量上讲,我希望最终结果是这样的:

另外一个要求是平滑数据的导数也应该是平滑的而不是跳跃的。

我将非常感激任何能帮助我解决这个任务的提示!:D


根据马丁·布朗的建议,我做了以下操作来平滑曲线:

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

来自convolvescipy.signal


高斯的另一种方法看起来像这样:

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

高斯似乎比 boxCar 效果更好一些。gaussian_filter1d来自scipy.ndimage


最佳答案
2

您可以使用(二次)贝塞尔曲线来平滑这两个重要点。

在下面的代码中,a、b、c 是定义贝塞尔曲线的三个点的索引。我随意地挑选了它们,以便 b 值与您的断点一致。您可能希望自动执行此操作。

import pandas as pd
import matplotlib.pyplot as plt

def Fixit( x, y, d, a, b, c ):
    ''' Quadratic Bezier curve using points with indices a, b, c '''
    N = c - a
    xa, xb, xc = x[a], x[b], x[c]
    ya, yb, yc = y[a], y[b], y[c]
    da, db, dc = d[a], d[b], d[c]
    for i in range( 1, N ):
        t = i / N
        x[a+i] = ( 1 - t ) ** 2 * xa + 2 * t * ( 1 - t ) * xb + t ** 2 * xc
        y[a+i] = ( 1 - t ) ** 2 * ya + 2 * t * ( 1 - t ) * yb + t ** 2 * yc
        d[a+i] = ( 1 - t ) ** 2 * da + 2 * t * ( 1 - t ) * db + t ** 2 * dc




df = pd.read_csv("ExampleforStack.txt") # read data
x = df["Displacement"] # get displacement
y = df["Load"] # get load
d = df["Damage"] # get damage


a, b, c = 210, 240, 250
Fixit( x, y, d, a, b, c )              # Smooth with a Bezier curve

a, b, c = 470, 480, 510
Fixit( x, y, d, a, b, c )              # Smooth with a Bezier curve



# plot stuff
def plotall():
    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()
    plt.show()

def plotStress():
    plt.plot(x,y,'b')
    plt.xlabel("Displacement")
    plt.ylabel("Stress")
    plt.show()

def plotDamage():
    plt.plot(x,d,'r')
    plt.xlabel("Displacement")
    plt.ylabel("Damage")
    plt.show()

plotall()
plotStress()
plotDamage()

输出:

1

  • 嘿,谢谢你的精彩回答!


    – 

最简单的解决方案是将低通滤波器应用于尖角函数。将其与具有适当宽度的高斯函数进行卷积应该会使其具有您想要的所有平滑度属性。数据间隔如此细密,您甚至可以对 11-21 个样本(选择奇数)进行简单的箱形平均。

然而,最好还是解决模拟软件中阻止其正确处理实际数据的错误。损坏的发生几乎总是突然发生,所以代码应该能够处理这种情况。过滤数据以使分析代码正常工作并不是我的首选。

3

  • 嘿,谢谢你的 boxcar 建议,不知怎的我忘了它的存在!我根据你的回答解决了这个问题,并将函数编辑到问题中。再次感谢!


    – 

  • 很高兴它对你有用。我仍然建议尝试理解为什么代码的下一阶段不喜欢具有尖锐梯度不连续性的分段连续函数。


    – 

  • 下一部分是 FEM 模拟软件,始终建议使用可以积分/区分而不会出现任何急剧变化的数据(分段函数就是这种情况)


    –