Skip to main content
`Ctrl`+`K`
scipy.interpolate.

# BSpline

class scipy.interpolate.BSpline(t, c, k, extrapolate=True, axis=0)

  Univariate spline in the B-spline basis.

  \[S(x) = \sum_{j=0}^{n-1} c_j B_{j, k; t}(x)\]

  where \(B_{j, k; t}\) are B-spline basis functions of degree `k` and knots `t`.

  Parameters:
    t ndarray, shape (n+k+1,)

      knots

    c ndarray, shape (>=n, …)

      spline coefficients

    k int

      B-spline degree

    extrapolate bool or ‘periodic’, optional

      whether to extrapolate beyond the base interval, `t[k] .. t[n]`, or to return nans. If True, extrapolates the first and last polynomial pieces of b-spline functions active on the base interval. If ‘periodic’, periodic extrapolation is used. Default is True.

    axis int, optional

      Interpolation axis. Default is zero.

  Attributes:
    t ndarray

      knot vector

    c ndarray

      spline coefficients

    k int

      spline degree

    extrapolate bool

      If True, extrapolates the first and last polynomial pieces of b-spline functions active on the base interval.

    axis int

      Interpolation axis.

    `tck` tuple

      Equivalent to `(self.t, self.c, self.k)` (read-only).

  Methods

  `__call__`(x[, nu, extrapolate]) | Evaluate a spline function.
  `basis_element`(t[, extrapolate]) | Return a B-spline basis element `B(x | t[0], ..., t[k+1])`.
  `derivative`([nu]) | Return a B-spline representing the derivative.
  `antiderivative`([nu]) | Return a B-spline representing the antiderivative.
  `integrate`(a, b[, extrapolate]) | Compute a definite integral of the spline.
  `insert_knot`(x[, m]) | Insert a new knot at x of multiplicity m.
  `construct_fast`(t, c, k[, extrapolate, axis]) | Construct a spline without making checks.
  `design_matrix`(x, t, k[, extrapolate]) | Returns a design matrix as a CSR format sparse array.
  `from_power_basis`(pp[, bc_type]) | Construct a polynomial in the B-spline basis from a piecewise polynomial in the power basis.

  Notes

  B-spline basis elements are defined via

  \[ \begin{align}\begin{aligned}B_{i, 0}(x) = 1, \textrm{if $t_i \le x < t_{i+1}$, otherwise $0$,}\\B_{i, k}(x) = \frac{x - t_i}{t_{i+k} - t_i} B_{i, k-1}(x) + \frac{t_{i+k+1} - x}{t_{i+k+1} - t_{i+1}} B_{i+1, k-1}(x)\end{aligned}\end{align} \]

  Implementation details

  - At least `k+1` coefficients are required for a spline of degree `k`, so that `n >= k+1`. Additional coefficients, `c[j]` with `j > n`, are ignored.

  - B-spline basis elements of degree `k` form a partition of unity on the base interval, `t[k] <= x <= t[n]`.

  Array API Standard Support

  `BSpline` has experimental support for Python Array API Standard compatible backends in addition to NumPy. Please consider testing these features by setting an environment variable `SCIPY_ARRAY_API=1` and providing CuPy, PyTorch, JAX, or Dask arrays as array arguments. The following combinations of backend and device (or other capability) are supported.

  Library | CPU | GPU
  --- | --- | ---
  NumPy | ✅ | n/a
  CuPy | n/a | ✅
  PyTorch | ✅ | ⛔
  JAX | ⚠️ no JIT | ⛔
  Dask | ⛔ | n/a

  The methods `design_matrix` and `from_power_basis` are currently NumPy only. `insert_knot` is currently not supported with CuPy.

  If the spline is called on an array `x` with namespace different from the namespace `xp` of the knots `t` and coefficients `c`, an attempt will be made to coerce `x` to the `xp` namespace. Mixing namespaces in this way is not recommended.

  See Support for the array API standard for more information.

  References

  [1]

  Michael Floater, An introduction to spline theory, 2025, https://www.uio.no/studier/emner/matnat/math/MAT4170/v25/undervisningsmateriale/spline_notes.pdf

  [2]

  Carl de Boor, A practical guide to splines, Springer, 2001.

  Examples

  Translating the recursive definition of B-splines into Python code, we have:

      >>> def B(x, k, i, t):
      ...    if k == 0:
      ...       return 1.0 if t[i] <= x < t[i+1] else 0.0
      ...    if t[i+k] == t[i]:
      ...       c1 = 0.0
      ...    else:
      ...       c1 = (x - t[i])/(t[i+k] - t[i]) * B(x, k-1, i, t)
      ...    if t[i+k+1] == t[i+1]:
      ...       c2 = 0.0
      ...    else:
      ...       c2 = (t[i+k+1] - x)/(t[i+k+1] - t[i+1]) * B(x, k-1, i+1, t)
      ...    return c1 + c2

      >>> def bspline(x, t, c, k):
      ...    n = len(t) - k - 1
      ...    assert (n >= k+1) and (len(c) >= n)
      ...    return sum(c[i] * B(x, k, i, t) for i in range(n))

  Note that this is an inefficient (if straightforward) way to evaluate B-splines — this spline class does it in an equivalent, but much more efficient way.

  Here we construct a quadratic spline function on the base interval `2 <= x <= 4` and compare with the naive way of evaluating the spline:

      >>> from scipy.interpolate import BSpline
      >>> k = 2
      >>> t = [0, 1, 2, 3, 4, 5, 6]
      >>> c = [-1, 2, 0, -1]
      >>> spl = BSpline(t, c, k)
      >>> spl(2.5)
      array(1.375)
      >>> bspline(2.5, t, c, k)
      1.375

  Note that outside of the base interval results differ. This is because `BSpline` extrapolates the first and last polynomial pieces of B-spline functions active on the base interval.

      >>> import matplotlib.pyplot as plt
      >>> import numpy as np
      >>> fig, ax = plt.subplots()
      >>> xx = np.linspace(1.5, 4.5, 50)
      >>> ax.plot(xx, [bspline(x, t, c ,k) for x in xx], 'r-', lw=3, label='naive')
      >>> ax.plot(xx, spl(xx), 'b-', lw=4, alpha=0.7, label='BSpline')
      >>> ax.grid(True)
      >>> ax.legend(loc='best')
      >>> plt.show()