我试图找到一个python软件包,该软件包将提供一个选项,以使自然平滑样条曲线与用户可选的平滑因子相匹配。有没有实现的方法?如果没有,您将如何使用可用的工具自己实现?

  • 通过自然样条,我的意思是应该存在一个条件,即拟合函数在端点处的二阶导数为零(线性)。
  • 通过平滑样条线,我的意思是样条线不应被“插值”(通过所有数据点)。我想自行决定正确的平滑系数lambda(请参见Wikipedia page来平滑样条曲线)。

  • 我发现了什么
  • scipy.interpolate.CubicSpline [link]:进行自然(三次)样条拟合。进行插值,并且无法平滑数据。
  • scipy.interpolate.UnivariateSpline [link]:样条曲线是否适合用户可选的平滑因子。但是,无法选择使花键自然化。
  • 最佳答案

    经过数小时的调查,我发现没有任何pip可安装的程序包可以适合用户控制的平滑度的自然三次样条。但是,在决定写一个自己的文章之后,在阅读有关该主题的文章时,我偶然发现了github用户blog postmadrury。他编写了能够生成自然三次样条模型的python代码。

    该模型代码可通过herea BSD-licence(NaturalCubicSpline)一起使用。他还用IPython notebook编写了一些示例。

    但是,由于这是Internet,并且链接容易消失,因此我将在此处复制源代码的相关部分+我编写的帮助函数(get_natural_cubic_spline_model),并显示如何使用它的示例。配合的平滑度可以通过使用不同的结数来控制。结的位置也可以由用户指定。

    例子

    from matplotlib import pyplot as plt
    import numpy as np
    
    def func(x):
        return 1/(1+25*x**2)
    
    # make example data
    x = np.linspace(-1,1,300)
    y = func(x) + np.random.normal(0, 0.2, len(x))
    
    # The number of knots can be used to control the amount of smoothness
    model_6 = get_natural_cubic_spline_model(x, y, minval=min(x), maxval=max(x), n_knots=6)
    model_15 = get_natural_cubic_spline_model(x, y, minval=min(x), maxval=max(x), n_knots=15)
    y_est_6 = model_6.predict(x)
    y_est_15 = model_15.predict(x)
    
    
    plt.plot(x, y, ls='', marker='.', label='originals')
    plt.plot(x, y_est_6, marker='.', label='n_knots = 6')
    plt.plot(x, y_est_15, marker='.', label='n_knots = 15')
    plt.legend(); plt.show()
    

    python - Python自然平滑样条线-LMLPHP
    get_natural_cubic_spline_model的源代码
    import numpy as np
    import pandas as pd
    from sklearn.base import BaseEstimator, TransformerMixin
    from sklearn.linear_model import LinearRegression
    from sklearn.pipeline import Pipeline
    
    
    def get_natural_cubic_spline_model(x, y, minval=None, maxval=None, n_knots=None, knots=None):
        """
        Get a natural cubic spline model for the data.
    
        For the knots, give (a) `knots` (as an array) or (b) minval, maxval and n_knots.
    
        If the knots are not directly specified, the resulting knots are equally
        space within the *interior* of (max, min).  That is, the endpoints are
        *not* included as knots.
    
        Parameters
        ----------
        x: np.array of float
            The input data
        y: np.array of float
            The outpur data
        minval: float
            Minimum of interval containing the knots.
        maxval: float
            Maximum of the interval containing the knots.
        n_knots: positive integer
            The number of knots to create.
        knots: array or list of floats
            The knots.
    
        Returns
        --------
        model: a model object
            The returned model will have following method:
            - predict(x):
                x is a numpy array. This will return the predicted y-values.
        """
    
        if knots:
            spline = NaturalCubicSpline(knots=knots)
        else:
            spline = NaturalCubicSpline(max=maxval, min=minval, n_knots=n_knots)
    
        p = Pipeline([
            ('nat_cubic', spline),
            ('regression', LinearRegression(fit_intercept=True))
        ])
    
        p.fit(x, y)
    
        return p
    
    
    class AbstractSpline(BaseEstimator, TransformerMixin):
        """Base class for all spline basis expansions."""
    
        def __init__(self, max=None, min=None, n_knots=None, n_params=None, knots=None):
            if knots is None:
                if not n_knots:
                    n_knots = self._compute_n_knots(n_params)
                knots = np.linspace(min, max, num=(n_knots + 2))[1:-1]
                max, min = np.max(knots), np.min(knots)
            self.knots = np.asarray(knots)
    
        @property
        def n_knots(self):
            return len(self.knots)
    
        def fit(self, *args, **kwargs):
            return self
    
    
    class NaturalCubicSpline(AbstractSpline):
        """Apply a natural cubic basis expansion to an array.
        The features created with this basis expansion can be used to fit a
        piecewise cubic function under the constraint that the fitted curve is
        linear *outside* the range of the knots..  The fitted curve is continuously
        differentiable to the second order at all of the knots.
        This transformer can be created in two ways:
          - By specifying the maximum, minimum, and number of knots.
          - By specifying the cutpoints directly.
    
        If the knots are not directly specified, the resulting knots are equally
        space within the *interior* of (max, min).  That is, the endpoints are
        *not* included as knots.
        Parameters
        ----------
        min: float
            Minimum of interval containing the knots.
        max: float
            Maximum of the interval containing the knots.
        n_knots: positive integer
            The number of knots to create.
        knots: array or list of floats
            The knots.
        """
    
        def _compute_n_knots(self, n_params):
            return n_params
    
        @property
        def n_params(self):
            return self.n_knots - 1
    
        def transform(self, X, **transform_params):
            X_spl = self._transform_array(X)
            if isinstance(X, pd.Series):
                col_names = self._make_names(X)
                X_spl = pd.DataFrame(X_spl, columns=col_names, index=X.index)
            return X_spl
    
        def _make_names(self, X):
            first_name = "{}_spline_linear".format(X.name)
            rest_names = ["{}_spline_{}".format(X.name, idx)
                          for idx in range(self.n_knots - 2)]
            return [first_name] + rest_names
    
        def _transform_array(self, X, **transform_params):
            X = X.squeeze()
            try:
                X_spl = np.zeros((X.shape[0], self.n_knots - 1))
            except IndexError: # For arrays with only one element
                X_spl = np.zeros((1, self.n_knots - 1))
            X_spl[:, 0] = X.squeeze()
    
            def d(knot_idx, x):
                def ppart(t): return np.maximum(0, t)
    
                def cube(t): return t*t*t
                numerator = (cube(ppart(x - self.knots[knot_idx]))
                             - cube(ppart(x - self.knots[self.n_knots - 1])))
                denominator = self.knots[self.n_knots - 1] - self.knots[knot_idx]
                return numerator / denominator
    
            for i in range(0, self.n_knots - 2):
                X_spl[:, i+1] = (d(i, X) - d(self.n_knots - 2, X)).squeeze()
            return X_spl
    

    关于python - Python自然平滑样条线,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/51321100/

    10-12 21:54