Skip to content

Commit c99b2f2

Browse files
committed
Solve missing interpolation method (cubicspline)
By commit 8bb2cc1 scipy.interpolate.CubicSpline method is referenced in the pandas documentation (see pandas/core/generic.py) but it is not wrapped by any interpolation method. This commit solves this adding the corresponding wrapper. SciPy's CubicSpline is a cubic spline data interpolator that allows explicit control of the boundary conditions for the interval. Changes to be committed: modified: ../../../core/missing.py modified: test_interpolate.py
1 parent 9376960 commit c99b2f2

File tree

2 files changed

+108
-3
lines changed

2 files changed

+108
-3
lines changed

pandas/core/missing.py

+90-3
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ def clean_interp_method(method, **kwargs):
112112
"akima",
113113
"spline",
114114
"from_derivatives",
115+
"cubicspline",
115116
]
116117
if method in ("spline", "polynomial") and order is None:
117118
raise ValueError("You must specify the order of the spline or polynomial.")
@@ -293,6 +294,7 @@ def interpolate_1d(
293294
"piecewise_polynomial",
294295
"pchip",
295296
"akima",
297+
"cubicspline",
296298
]
297299

298300
if method in sp_methods:
@@ -349,6 +351,13 @@ def _interpolate_scipy_wrapper(
349351
) from err
350352
elif method == "akima":
351353
alt_methods["akima"] = _akima_interpolate
354+
elif method == "cubicspline":
355+
try:
356+
alt_methods["cubicspline"] = _cubicspline_interpolate
357+
except AttributeError as err:
358+
raise ImportError(
359+
"Your version of Scipy does not support CubicSpline."
360+
) from err
352361

353362
interp1d_methods = [
354363
"nearest",
@@ -406,7 +415,7 @@ def _from_derivatives(xi, yi, x, order=None, der=0, extrapolate=False):
406415
der : int or list
407416
How many derivatives to extract; None for all potentially nonzero
408417
derivatives (that is a number equal to the number of points), or a
409-
list of derivatives to extract. This numberincludes the function
418+
list of derivatives to extract. This number includes the function
410419
value as 0th derivative.
411420
extrapolate : bool, optional
412421
Whether to extrapolate to ouf-of-bounds points based on first and last
@@ -446,8 +455,7 @@ def _akima_interpolate(xi, yi, x, der=0, axis=0):
446455
A 1-D array of real values. `yi`'s length along the interpolation
447456
axis must be equal to the length of `xi`. If N-D array, use axis
448457
parameter to select correct axis.
449-
x : scalar or array_like
450-
Of length M.
458+
x : scalar or array_like of length M.
451459
der : int or list, optional
452460
How many derivatives to extract; None for all potentially
453461
nonzero derivatives (that is a number equal to the number
@@ -478,6 +486,85 @@ def _akima_interpolate(xi, yi, x, der=0, axis=0):
478486
return [P(x, nu) for nu in der]
479487

480488

489+
def _cubicspline_interpolate(xi, yi, x, axis=0, bc_type="not-a-knot", extrapolate=None):
490+
"""
491+
Convenience function for cubic spline data interpolator.
492+
493+
See `scipy.interpolate.CubicSpline` for details.
494+
495+
Parameters
496+
----------
497+
xi : array_like, shape (n,)
498+
1-d array containing values of the independent variable.
499+
Values must be real, finite and in strictly increasing order.
500+
yi : array_like
501+
Array containing values of the dependent variable. It can have
502+
arbitrary number of dimensions, but the length along ``axis``
503+
(see below) must match the length of ``x``. Values must be finite.
504+
x : scalar or array_like, shape (m,)
505+
axis : int, optional
506+
Axis along which `y` is assumed to be varying. Meaning that for
507+
``x[i]`` the corresponding values are ``np.take(y, i, axis=axis)``.
508+
Default is 0.
509+
bc_type : string or 2-tuple, optional
510+
Boundary condition type. Two additional equations, given by the
511+
boundary conditions, are required to determine all coefficients of
512+
polynomials on each segment [2]_.
513+
If `bc_type` is a string, then the specified condition will be applied
514+
at both ends of a spline. Available conditions are:
515+
* 'not-a-knot' (default): The first and second segment at a curve end
516+
are the same polynomial. It is a good default when there is no
517+
information on boundary conditions.
518+
* 'periodic': The interpolated functions is assumed to be periodic
519+
of period ``x[-1] - x[0]``. The first and last value of `y` must be
520+
identical: ``y[0] == y[-1]``. This boundary condition will result in
521+
``y'[0] == y'[-1]`` and ``y''[0] == y''[-1]``.
522+
* 'clamped': The first derivative at curves ends are zero. Assuming
523+
a 1D `y`, ``bc_type=((1, 0.0), (1, 0.0))`` is the same condition.
524+
* 'natural': The second derivative at curve ends are zero. Assuming
525+
a 1D `y`, ``bc_type=((2, 0.0), (2, 0.0))`` is the same condition.
526+
If `bc_type` is a 2-tuple, the first and the second value will be
527+
applied at the curve start and end respectively. The tuple values can
528+
be one of the previously mentioned strings (except 'periodic') or a
529+
tuple `(order, deriv_values)` allowing to specify arbitrary
530+
derivatives at curve ends:
531+
* `order`: the derivative order, 1 or 2.
532+
* `deriv_value`: array_like containing derivative values, shape must
533+
be the same as `y`, excluding ``axis`` dimension. For example, if
534+
`y` is 1D, then `deriv_value` must be a scalar. If `y` is 3D with
535+
the shape (n0, n1, n2) and axis=2, then `deriv_value` must be 2D
536+
and have the shape (n0, n1).
537+
extrapolate : {bool, 'periodic', None}, optional
538+
If bool, determines whether to extrapolate to out-of-bounds points
539+
based on first and last intervals, or to return NaNs. If 'periodic',
540+
periodic extrapolation is used. If None (default), ``extrapolate`` is
541+
set to 'periodic' for ``bc_type='periodic'`` and to True otherwise.
542+
543+
See Also
544+
--------
545+
scipy.interpolate.CubicHermiteSpline
546+
547+
Returns
548+
-------
549+
y : scalar or array_like
550+
The result, of shape (m,)
551+
552+
References
553+
----------
554+
.. [1] `Cubic Spline Interpolation
555+
<https://en.wikiversity.org/wiki/Cubic_Spline_Interpolation>`_
556+
on Wikiversity.
557+
.. [2] Carl de Boor, "A Practical Guide to Splines", Springer-Verlag, 1978.
558+
"""
559+
from scipy import interpolate
560+
561+
P = interpolate.CubicSpline(
562+
xi, yi, axis=axis, bc_type=bc_type, extrapolate=extrapolate
563+
)
564+
565+
return P(x)
566+
567+
481568
def interpolate_2d(
482569
values, method="pad", axis=0, limit=None, fill_value=None, dtype=None
483570
):

pandas/tests/series/methods/test_interpolate.py

+18
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"from_derivatives",
2727
"pchip",
2828
"akima",
29+
"cubicspline",
2930
]
3031
)
3132
def nontemporal_method(request):
@@ -55,6 +56,7 @@ def nontemporal_method(request):
5556
"from_derivatives",
5657
"pchip",
5758
"akima",
59+
"cubicspline",
5860
]
5961
)
6062
def interp_methods_ind(request):
@@ -97,6 +99,22 @@ def test_interpolate_time_raises_for_non_timeseries(self):
9799
with pytest.raises(ValueError, match=msg):
98100
non_ts.interpolate(method="time")
99101

102+
@td.skip_if_no_scipy
103+
def test_interpolate_cubicspline(self):
104+
105+
ser = Series([10, 11, 12, 13])
106+
107+
expected = Series(
108+
[11.00, 11.25, 11.50, 11.75, 12.00, 12.25, 12.50, 12.75, 13.00],
109+
index=Index([1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0]),
110+
)
111+
# interpolate at new_index
112+
new_index = ser.index.union(Index([1.25, 1.5, 1.75, 2.25, 2.5, 2.75])).astype(
113+
float
114+
)
115+
interp_s = ser.reindex(new_index).interpolate(method="cubicspline")
116+
tm.assert_series_equal(interp_s[1:3], expected)
117+
100118
@td.skip_if_no_scipy
101119
def test_interpolate_pchip(self):
102120

0 commit comments

Comments
 (0)