【问题标题】:Plot smooth line with PyPlot使用 PyPlot 绘制平滑线
【发布时间】:2011-07-14 02:36:58
【问题描述】:

我有以下绘制图表的简单脚本:

import matplotlib.pyplot as plt
import numpy as np

T = np.array([6, 7, 8, 9, 10, 11, 12])
power = np.array([1.53E+03, 5.92E+02, 2.04E+02, 7.24E+01, 2.72E+01, 1.10E+01, 4.70E+00])

plt.plot(T,power)
plt.show()

就像现在一样,这条线从一个点到另一个点是笔直的,看起来不错,但在我看来可能会更好。我想要的是平滑点之间的线。在 Gnuplot 中,我会使用 smooth cplines 进行绘图。

在 PyPlot 中是否有一种简单的方法可以做到这一点?我找到了一些教程,但它们看起来都相当复杂。

【问题讨论】:

    标签: python matplotlib plot smoothing


    【解决方案1】:

    我发现最简单的implementations 之一是使用 Tensorboard 使用的指数移动平均线:

    def smooth(scalars: List[float], weight: float) -> List[float]:  # Weight between 0 and 1
        last = scalars[0]  # First value in the plot (first timestep)
        smoothed = list()
        for point in scalars:
            smoothed_val = last * weight + (1 - weight) * point  # Calculate smoothed value
            smoothed.append(smoothed_val)                        # Save it
            last = smoothed_val                                  # Anchor the last smoothed value
            
        return smoothed
    
    
    ax.plot(x_labels, smooth(train_data, .9), x_labels, train_data)
    
    

    【讨论】:

      【解决方案2】:

      值得您花时间查看 seaborn 以绘制平滑线。

      seaborn lmplot 函数将绘制数据和回归模型拟合。

      以下说明了多项式和lowess 拟合:

      import numpy as np
      import pandas as pd
      import seaborn as sns
      import matplotlib.pyplot as plt
      
      T = np.array([6, 7, 8, 9, 10, 11, 12])
      power = np.array([1.53E+03, 5.92E+02, 2.04E+02, 7.24E+01, 2.72E+01, 1.10E+01, 4.70E+00])
      
      df = pd.DataFrame(data = {'T': T, 'power': power})
          
      sns.lmplot(x='T', y='power', data=df, ci=None, order=4, truncate=False)
      sns.lmplot(x='T', y='power', data=df, ci=None, lowess=True, truncate=False)
      

      order = 4 多项式拟合过度拟合这个玩具数据集。我没有在这里展示它,但order = 2order = 3 给出了更糟糕的结果。

      lowess = True 拟合欠拟合这个小数据集,但可能会在更大的数据集上提供更好的结果。

      查看seaborn regression tutorial 了解更多示例。

      【讨论】:

        【解决方案3】:

        有关示例,请参阅 scipy.interpolate 文档。

        以下示例演示了它在线性和三次样条插值中的用途:

        import matplotlib.pyplot as plt
        import numpy as np
        from scipy.interpolate import interp1d
        
        # Define x, y, and xnew to resample at.
        x = np.linspace(0, 10, num=11, endpoint=True)
        y = np.cos(-x**2/9.0)
        xnew = np.linspace(0, 10, num=41, endpoint=True)
        
        # Define interpolators.
        f_linear = interp1d(x, y)
        f_cubic = interp1d(x, y, kind='cubic')
        
        # Plot.
        plt.plot(x, y, 'o', label='data')
        plt.plot(xnew, f_linear(xnew), '-', label='linear')
        plt.plot(xnew, f_cubic(xnew), '--', label='cubic')
        plt.legend(loc='best')
        plt.show()
        

        稍作修改以提高可读性。

        【讨论】:

          【解决方案4】:

          另一种方法,根据你使用的参数稍微修改函数:

          from statsmodels.nonparametric.smoothers_lowess import lowess
          
          def smoothing(x, y):
              lowess_frac = 0.15  # size of data (%) for estimation =~ smoothing window
              lowess_it = 0
              x_smooth = x
              y_smooth = lowess(y, x, is_sorted=False, frac=lowess_frac, it=lowess_it, return_sorted=False)
              return x_smooth, y_smooth
          

          对于我的特定应用案例,这比其他答案更适合。

          【讨论】:

            【解决方案5】:

            这是一个简单的日期解决方案:

            from scipy.interpolate import make_interp_spline
            import numpy as np
            import matplotlib.pyplot as plt
            import matplotlib.dates as dates
            from datetime import datetime
            
            data = {
                datetime(2016, 9, 26, 0, 0): 26060, datetime(2016, 9, 27, 0, 0): 23243,
                datetime(2016, 9, 28, 0, 0): 22534, datetime(2016, 9, 29, 0, 0): 22841,
                datetime(2016, 9, 30, 0, 0): 22441, datetime(2016, 10, 1, 0, 0): 23248 
            }
            #create data
            date_np = np.array(list(data.keys()))
            value_np = np.array(list(data.values()))
            date_num = dates.date2num(date_np)
            # smooth
            date_num_smooth = np.linspace(date_num.min(), date_num.max(), 100) 
            spl = make_interp_spline(date_num, value_np, k=3)
            value_np_smooth = spl(date_num_smooth)
            # print
            plt.plot(date_np, value_np)
            plt.plot(dates.num2date(date_num_smooth), value_np_smooth)
            plt.show()
            

            【讨论】:

              【解决方案6】:

              您可以使用scipy.interpolate.spline 自己平滑您的数据:

              from scipy.interpolate import spline
              
              # 300 represents number of points to make between T.min and T.max
              xnew = np.linspace(T.min(), T.max(), 300)  
              
              power_smooth = spline(T, power, xnew)
              
              plt.plot(xnew,power_smooth)
              plt.show()
              

              spline 在 scipy 0.19.0 中已弃用,请改用 BSpline 类。

              spline 切换到BSpline 不是简单的复制/粘贴,需要稍作调整:

              from scipy.interpolate import make_interp_spline, BSpline
              
              # 300 represents number of points to make between T.min and T.max
              xnew = np.linspace(T.min(), T.max(), 300) 
              
              spl = make_interp_spline(T, power, k=3)  # type: BSpline
              power_smooth = spl(xnew)
              
              plt.plot(xnew, power_smooth)
              plt.show()
              

              之前:

              之后:

              【讨论】:

              • spline 已弃用! spline 在 scipy 0.19.0 中已弃用,请改用 BSpline 类:from scipy.interpolate import BSpline
              • 如果 T 没有排序,这将不起作用。并且如果 functiton(T) 不是一对一的。
              • 您可能希望将 #BSpline object 注释设置为类型提示,例如 spl = make_interp_spline(T, power, k=3) # type: BSpline object,以便 BSpline 的导入导致更有效的使用......或者其他任何事情都需要它?我在这里提醒 :) (另外,让评论更具 PEP8 风格并没有什么坏处,毕竟它是“公开的代码”。)但总的来说:谢谢你的例子!
              • k = 3 是什么??
              • @AminGuermazi k=3 是样条插值的度数:https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.make_interp_spline.html。所以如果你使用更大的数字,比如k=6,曲线应该会更平滑。
              【解决方案7】:

              对于这个例子,样条曲线效果很好,但是如果函数本身不是平滑的并且你想要平滑版本,你也可以尝试:

              from scipy.ndimage.filters import gaussian_filter1d
              
              ysmoothed = gaussian_filter1d(y, sigma=2)
              plt.plot(x, ysmoothed)
              plt.show()
              

              如果增加 sigma,您可以获得更平滑的函数。

              请谨慎操作。它会修改原始值,可能不是您想要的。

              【讨论】:

              • 谨慎使用这个。它会修改原始值,可能不是您想要的。
              • 确实不怎么好用,真的把整个函数弄平了,根本就不再关注这些点......
              【解决方案8】:

              我认为您的意思是 curve-fitting 而不是 anti-aliasing 从您的问题的上下文中。 PyPlot 对此没有任何内置支持,但您可以轻松地自己实现一些基本的曲线拟合,例如看到的代码 here,或者如果您使用的是 GuiQwt,它具有曲线拟合 module。 (您也可以从SciPy 窃取代码来执行此操作)。

              【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2013-05-23
              • 2011-10-26
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多