Repository: scipy/scipy
File: scipy/interpolate/_ndbspline.py (Python)

Code before:
        # The strides of the coeffs array: the computation is equivalent to
        # >>> cstrides = [s // 8 for s in np.empty(c_shape).strides]
        cs = c_shape[1:] + (1,)
        cstrides = np.cumprod(cs[::-1], dtype=np.int64)[::-1].copy()

        # heavy lifting happens here
        data, indices, indptr = _dierckx._coloc_nd(xvals,
                _t, len_t, k, _indices_k1d, cstrides)

        return csr_array((data, indices, indptr))

    def _bspline_derivative_along_axis(self, c, t, k, axis):
        # Move the selected axis to front
        c = np.moveaxis(c, axis, 0)
        shape = c.shape
        n = shape[0]
        trailing_shape = shape[1:]
        c_flat = c.reshape(n, -1)

        new_c_list = []
        new_t = None

        for i in range(c_flat.shape[1]):
            b = BSpline(t, c_flat[:, i], k)
            db = b.derivative(1)
            db.c = db.c[:len(db.t) - db.k - 1]

            if new_t is None:
                new_t = db.t

            new_c_list.append(db.c)

        new_c = np.stack(new_c_list, axis=1).reshape(
            (len(new_c_list[0]),) + trailing_shape)
        new_c = np.moveaxis(new_c, 0, axis)

        return new_c, new_t

    def derivative(self, nu=1):
        """
        Construct a new NdBSpline representing the derivative.

        Parameters
        ----------
        nu : int or array_like of shape (ndim,)
            Order(s) of the derivative to compute along each dimension.
            A single integer means the same order for all dimensions.

        Returns
        -------
        NdBSpline

Review comment (suggestion):
This is to undo an extension of the `c` array which `BSpline.derivative` does before calling `splder`? Looks awkward, I wonder if we can avoid it, maybe call `splder` directly

Code after:
        # The strides of the coeffs array: the computation is equivalent to
        # >>> cstrides = [s // 8 for s in np.empty(c_shape).strides]
        cs = c_shape[1:] + (1,)
        cstrides = np.cumprod(cs[::-1], dtype=np.int64)[::-1].copy()

        # heavy lifting happens here
        data, indices, indptr = _dierckx._coloc_nd(xvals,
                _t, len_t, k, _indices_k1d, cstrides)

        return csr_array((data, indices, indptr))

    def _bspline_derivative_along_axis(self, c, t, k, axis, nu=1):
        # Move the selected axis to front
        c = np.moveaxis(c, axis, 0)
        n = c.shape[0]
        trailing_shape = c.shape[1:]
        c_flat = c.reshape(n, -1)

        new_c_list = []
        new_t = None

        for i in range(c_flat.shape[1]):
            if k >= nu:
                b = BSpline.construct_fast(t, c_flat[:, i], k)
                db = b.derivative(nu)
                # truncate coefficients to match new knot/degree size
                db.c = db.c[:len(db.t) - db.k - 1]
            else:
                db = BSpline.construct_fast(t, np.zeros(len(t) - 1), 0)

            if new_t is None:
                new_t = db.t

            new_c_list.append(db.c)

        new_c = np.stack(new_c_list, axis=1).reshape(
            (len(new_c_list[0]),) + trailing_shape)
        new_c = np.moveaxis(new_c, 0, axis)

        return new_c, new_t

    def derivative(self, nu=1):
        """
        Construct a new NdBSpline representing the derivative.

        Parameters
        ----------
        nu : array_like of shape (ndim,)
            Order(s) of the derivative to compute along each dimension.

        Returns