【问题标题】:Smooth a curve in python with no error at the boundaries?在 python 中平滑一条曲线,在边界处没有错误?
【发布时间】:2014-09-14 21:00:19
【问题描述】:

考虑以下与两个 numpy 数组 xy 关联的曲线:

如何在 xmax 附近没有问题地在 python 中正确平滑它? (如果我应用高斯滤波器,曲线最后会上升)

数据在这里(两列):http://lite4.framapad.org/p/xqhpGJpV5R

【问题讨论】:

    标签: python numpy scipy interpolation smoothing


    【解决方案1】:

    最简单的方法是在过滤之前去除信号的趋势。您看到的边缘效应主要是由于信号不是静止的(即它有一个斜率)。

    首先,让我们演示一下这个问题:

    import numpy as np
    import matplotlib.pyplot as plt
    from scipy.ndimage import gaussian_filter1d
    
    x, y = np.loadtxt('spectrum.dat').T
    
    # Smooth with a guassian filter
    smooth = gaussian_filter1d(y, 10)
    
    fig, ax = plt.subplots()
    ax.loglog(x, y, color='black')
    ax.loglog(x, smooth, color='red')
    plt.show()
    

    哎哟!边缘效应在数据的末端(右手尺寸)特别糟糕,因为那是斜率最陡的地方。如果你在开始的时候有一个更陡峭的斜坡,你也会在那里看到更强的边缘效果。


    好消息是,有很多方法可以解决这个问题。 @ChristianK. 的回答展示了如何使用平滑样条线来有效地执行低通滤波器。我将演示使用其他一些信号处理方法来完成同样的事情。哪个是“最好的”完全取决于您的需求。平滑样条曲线是直截了当的。使用“更高级”的信号处理方法可以让您准确控制过滤掉的频率。

    您的数据在对数空间中看起来像一条抛物线,所以让我们在对数空间中使用二阶多项式对其进行去趋势处理,然后应用过滤器。

    举个简单的例子:

    import numpy as np
    import matplotlib.pyplot as plt
    from scipy.ndimage import gaussian_filter1d
    
    x, y = np.loadtxt('spectrum.dat').T
    
    # Let's detrend by fitting a second-order polynomial in log space
    # (Note that your data looks like a parabola in log-log space.)
    logx, logy = np.log(x), np.log(y)
    model = np.polyfit(logx, logy, 2)
    trend = np.polyval(model, logx)
    
    # Smooth with a guassian filter
    smooth = gaussian_filter1d(logy - trend, 10)
    
    # Add the trend back in and convert back to linear space
    smooth = np.exp(smooth + trend)
    
    fig, ax = plt.subplots()
    ax.loglog(x, y, color='black')
    ax.loglog(x, smooth, color='red')
    plt.show()
    

    请注意,我们仍然有一些边缘效应。这是因为我使用的高斯滤波器会导致相移。如果我们真的想变得花哨,我们可以去趋势,然后使用零相位滤波器来进一步减少边缘效应。

    import numpy as np
    import matplotlib.pyplot as plt
    import scipy.signal as signal
    
    def main():
        x, y = np.loadtxt('spectrum.dat').T
    
        logx, logy = np.log(x), np.log(y)
        smooth_log = detrend_zero_phase(logx, logy)
        smooth = np.exp(smooth_log)
    
        fig, ax = plt.subplots()
        ax.loglog(x, y, 'k-')
        ax.loglog(x, smooth, 'r-')
        plt.show()
    
    def zero_phase(y):
        # Low-pass filter...
        b, a = signal.butter(3, 0.05)
    
        # Filtfilt applies the filter twice to avoid phase shifts.
        return signal.filtfilt(b, a, y)
    
    def detrend_zero_phase(x, y):
        # Fit a second order polynomial (Can't just use scipy.signal.detrend here,
        # because we need to know what the trend is to add it back in.)
        model = np.polyfit(x, y, 2)
        trend = np.polyval(model, x)
    
        # Apply a zero-phase filter to the detrended curve.
        smooth = zero_phase(y - trend)
    
        # Add the trend back in
        return smooth + trend
    
    main()
    

    【讨论】:

      【解决方案2】:

      如果您的所有数据都在日志空间中缓慢修改,我会执行以下操作:

      1. 在线性范围内大幅下采样对数数据
      2. 计算平滑样条
      3. 转换回线性刻度

      例如:

      import numpy as np
      from scipy.interpolate import interp1d, splrep, splev
      import pylab
      
      x = np.log10(x)
      y = np.log10(y)
      
      ip = interp1d(x,y)
      xi = np.linspace(x.min(),x.max(),10)
      yi = ip(xi)
      
      tcl = splrep(xi,yi,s=1)
      xs = np.linspace(x.min(), x.max(), 100)
      ys = splev(xs, tcl)
      
      xi = np.power(10,xi)
      yi = np.power(10,yi)
      xs = np.power(10,xs)
      ys = np.power(10,ys)
      
      f = pylab.figure()
      pl = f.add_subplot(111)
      pl.loglog(aset.x,aset.y,alpha=0.4)
      pl.loglog(xi,yi,'go--',linewidth=1, label='linear')
      pl.loglog(xs,ys,'r-',linewidth=1, label='spline')
      pl.legend(loc=0)
      f.show()
      

      这会产生:

      【讨论】:

        猜你喜欢
        • 2018-03-19
        • 2020-11-14
        • 2018-12-21
        • 2023-03-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多