import numpy as np import pytest import pandas.util._test_decorators as td import pandas as pd from pandas import ( Index, MultiIndex, Series, date_range, isna, ) import pandas._testing as tm @pytest.fixture( params=[ "linear", "index", "values", "nearest", "slinear", "zero", "quadratic", "cubic", "barycentric", "krogh", "polynomial", "spline", "piecewise_polynomial", "from_derivatives", "pchip", "akima", "cubicspline", ] ) def nontemporal_method(request): """Fixture that returns an (method name, required kwargs) pair. This fixture does not include method 'time' as a parameterization; that method requires a Series with a DatetimeIndex, and is generally tested separately from these non-temporal methods. """ method = request.param kwargs = {"order": 1} if method in ("spline", "polynomial") else {} return method, kwargs @pytest.fixture( params=[ "linear", "slinear", "zero", "quadratic", "cubic", "barycentric", "krogh", "polynomial", "spline", "piecewise_polynomial", "from_derivatives", "pchip", "akima", "cubicspline", ] ) def interp_methods_ind(request): """Fixture that returns a (method name, required kwargs) pair to be tested for various Index types. This fixture does not include methods - 'time', 'index', 'nearest', 'values' as a parameterization """ method = request.param kwargs = {"order": 1} if method in ("spline", "polynomial") else {} return method, kwargs class TestSeriesInterpolateData: @pytest.mark.xfail(reason="EA.fillna does not handle 'linear' method") def test_interpolate_period_values(self): orig = Series(date_range("2012-01-01", periods=5)) ser = orig.copy() ser[2] = pd.NaT # period cast ser_per = ser.dt.to_period("D") res_per = ser_per.interpolate() expected_per = orig.dt.to_period("D") tm.assert_series_equal(res_per, expected_per) def test_interpolate(self, datetime_series): ts = Series(np.arange(len(datetime_series), dtype=float), datetime_series.index) ts_copy = ts.copy() ts_copy[5:10] = np.nan linear_interp = ts_copy.interpolate(method="linear") tm.assert_series_equal(linear_interp, ts) ord_ts = Series( [d.toordinal() for d in datetime_series.index], index=datetime_series.index ).astype(float) ord_ts_copy = ord_ts.copy() ord_ts_copy[5:10] = np.nan time_interp = ord_ts_copy.interpolate(method="time") tm.assert_series_equal(time_interp, ord_ts) def test_interpolate_time_raises_for_non_timeseries(self): # When method='time' is used on a non-TimeSeries that contains a null # value, a ValueError should be raised. non_ts = Series([0, 1, 2, np.nan]) msg = "time-weighted interpolation only works on Series.* with a DatetimeIndex" with pytest.raises(ValueError, match=msg): non_ts.interpolate(method="time") def test_interpolate_cubicspline(self): pytest.importorskip("scipy") ser = Series([10, 11, 12, 13]) expected = Series( [11.00, 11.25, 11.50, 11.75, 12.00, 12.25, 12.50, 12.75, 13.00], index=Index([1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0]), ) # interpolate at new_index new_index = ser.index.union(Index([1.25, 1.5, 1.75, 2.25, 2.5, 2.75])).astype( float ) result = ser.reindex(new_index).interpolate(method="cubicspline").loc[1:3] tm.assert_series_equal(result, expected) def test_interpolate_pchip(self): pytest.importorskip("scipy") ser = Series(np.sort(np.random.default_rng(2).uniform(size=100))) # interpolate at new_index new_index = ser.index.union( Index([49.25, 49.5, 49.75, 50.25, 50.5, 50.75]) ).astype(float) interp_s = ser.reindex(new_index).interpolate(method="pchip") # does not blow up, GH5977 interp_s.loc[49:51] def test_interpolate_akima(self): pytest.importorskip("scipy") ser = Series([10, 11, 12, 13]) # interpolate at new_index where `der` is zero expected = Series( [11.00, 11.25, 11.50, 11.75, 12.00, 12.25, 12.50, 12.75, 13.00], index=Index([1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0]), ) new_index = ser.index.union(Index([1.25, 1.5, 1.75, 2.25, 2.5, 2.75])).astype( float ) interp_s = ser.reindex(new_index).interpolate(method="akima") tm.assert_series_equal(interp_s.loc[1:3], expected) # interpolate at new_index where `der` is a non-zero int expected = Series( [11.0, 1.0, 1.0, 1.0, 12.0, 1.0, 1.0, 1.0, 13.0], index=Index([1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0]), ) new_index = ser.index.union(Index([1.25, 1.5, 1.75, 2.25, 2.5, 2.75])).astype( float ) interp_s = ser.reindex(new_index).interpolate(method="akima", der=1) tm.assert_series_equal(interp_s.loc[1:3], expected) def test_interpolate_piecewise_polynomial(self): pytest.importorskip("scipy") ser = Series([10, 11, 12, 13]) expected = Series( [11.00, 11.25, 11.50, 11.75, 12.00, 12.25, 12.50, 12.75, 13.00], index=Index([1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0]), ) # interpolate at new_index new_index = ser.index.union(Index([1.25, 1.5, 1.75, 2.25, 2.5, 2.75])).astype( float ) interp_s = ser.reindex(new_index).interpolate(method="piecewise_polynomial") tm.assert_series_equal(interp_s.loc[1:3], expected) def test_interpolate_from_derivatives(self): pytest.importorskip("scipy") ser = Series([10, 11, 12, 13]) expected = Series( [11.00, 11.25, 11.50, 11.75, 12.00, 12.25, 12.50, 12.75, 13.00], index=Index([1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0]), ) # interpolate at new_index new_index = ser.index.union(Index([1.25, 1.5, 1.75, 2.25, 2.5, 2.75])).astype( float ) interp_s = ser.reindex(new_index).interpolate(method="from_derivatives") tm.assert_series_equal(interp_s.loc[1:3], expected) @pytest.mark.parametrize( "kwargs", [ {}, pytest.param( {"method": "polynomial", "order": 1}, marks=td.skip_if_no("scipy") ), ], ) def test_interpolate_corners(self, kwargs): s = Series([np.nan, np.nan]) tm.assert_series_equal(s.interpolate(**kwargs), s) s = Series([], dtype=object).interpolate() tm.assert_series_equal(s.interpolate(**kwargs), s) def test_interpolate_index_values(self): s = Series(np.nan, index=np.sort(np.random.default_rng(2).random(30))) s.loc[::3] = np.random.default_rng(2).standard_normal(10) vals = s.index.values.astype(float) result = s.interpolate(method="index") expected = s.copy() bad = isna(expected.values) good = ~bad expected = Series( np.interp(vals[bad], vals[good], s.values[good]), index=s.index[bad] ) tm.assert_series_equal(result[bad], expected) # 'values' is synonymous with 'index' for the method kwarg other_result = s.interpolate(method="values") tm.assert_series_equal(other_result, result) tm.assert_series_equal(other_result[bad], expected) def test_interpolate_non_ts(self): s = Series([1, 3, np.nan, np.nan, np.nan, 11]) msg = ( "time-weighted interpolation only works on Series or DataFrames " "with a DatetimeIndex" ) with pytest.raises(ValueError, match=msg): s.interpolate(method="time") @pytest.mark.parametrize( "kwargs", [ {}, pytest.param( {"method": "polynomial", "order": 1}, marks=td.skip_if_no("scipy") ), ], ) def test_nan_interpolate(self, kwargs): s = Series([0, 1, np.nan, 3]) result = s.interpolate(**kwargs) expected = Series([0.0, 1.0, 2.0, 3.0]) tm.assert_series_equal(result, expected) def test_nan_irregular_index(self): s = Series([1, 2, np.nan, 4], index=[1, 3, 5, 9]) result = s.interpolate() expected = Series([1.0, 2.0, 3.0, 4.0], index=[1, 3, 5, 9]) tm.assert_series_equal(result, expected) def test_nan_str_index(self): s = Series([0, 1, 2, np.nan], index=list("abcd")) result = s.interpolate() expected = Series([0.0, 1.0, 2.0, 2.0], index=list("abcd")) tm.assert_series_equal(result, expected) def test_interp_quad(self): pytest.importorskip("scipy") sq = Series([1, 4, np.nan, 16], index=[1, 2, 3, 4]) result = sq.interpolate(method="quadratic") expected = Series([1.0, 4.0, 9.0, 16.0], index=[1, 2, 3, 4]) tm.assert_series_equal(result, expected) def test_interp_scipy_basic(self): pytest.importorskip("scipy") s = Series([1, 3, np.nan, 12, np.nan, 25]) # slinear expected = Series([1.0, 3.0, 7.5, 12.0, 18.5, 25.0]) result = s.interpolate(method="slinear") tm.assert_series_equal(result, expected) msg = "The 'downcast' keyword in Series.interpolate is deprecated" with tm.assert_produces_warning(FutureWarning, match=msg): result = s.interpolate(method="slinear", downcast="infer") tm.assert_series_equal(result, expected) # nearest expected = Series([1, 3, 3, 12, 12, 25]) result = s.interpolate(method="nearest") tm.assert_series_equal(result, expected.astype("float")) with tm.assert_produces_warning(FutureWarning, match=msg): result = s.interpolate(method="nearest", downcast="infer") tm.assert_series_equal(result, expected) # zero expected = Series([1, 3, 3, 12, 12, 25]) result = s.interpolate(method="zero") tm.assert_series_equal(result, expected.astype("float")) with tm.assert_produces_warning(FutureWarning, match=msg): result = s.interpolate(method="zero", downcast="infer") tm.assert_series_equal(result, expected) # quadratic # GH #15662. expected = Series([1, 3.0, 6.823529, 12.0, 18.058824, 25.0]) result = s.interpolate(method="quadratic") tm.assert_series_equal(result, expected) with tm.assert_produces_warning(FutureWarning, match=msg): result = s.interpolate(method="quadratic", downcast="infer") tm.assert_series_equal(result, expected) # cubic expected = Series([1.0, 3.0, 6.8, 12.0, 18.2, 25.0]) result = s.interpolate(method="cubic") tm.assert_series_equal(result, expected) def test_interp_limit(self): s = Series([1, 3, np.nan, np.nan, np.nan, 11]) expected = Series([1.0, 3.0, 5.0, 7.0, np.nan, 11.0]) result = s.interpolate(method="linear", limit=2) tm.assert_series_equal(result, expected) @pytest.mark.parametrize("limit", [-1, 0]) def test_interpolate_invalid_nonpositive_limit(self, nontemporal_method, limit): # GH 9217: make sure limit is greater than zero. s = Series([1, 2, np.nan, 4]) method, kwargs = nontemporal_method with pytest.raises(ValueError, match="Limit must be greater than 0"): s.interpolate(limit=limit, method=method, **kwargs) def test_interpolate_invalid_float_limit(self, nontemporal_method): # GH 9217: make sure limit is an integer. s = Series([1, 2, np.nan, 4]) method, kwargs = nontemporal_method limit = 2.0 with pytest.raises(ValueError, match="Limit must be an integer"): s.interpolate(limit=limit, method=method, **kwargs) @pytest.mark.parametrize("invalid_method", [None, "nonexistent_method"]) def test_interp_invalid_method(self, invalid_method): s = Series([1, 3, np.nan, 12, np.nan, 25]) msg = f"method must be one of.* Got '{invalid_method}' instead" if invalid_method is None: msg = "'method' should be a string, not None" with pytest.raises(ValueError, match=msg): s.interpolate(method=invalid_method) # When an invalid method and invalid limit (such as -1) are # provided, the error message reflects the invalid method. with pytest.raises(ValueError, match=msg): s.interpolate(method=invalid_method, limit=-1) def test_interp_invalid_method_and_value(self): # GH#36624 ser = Series([1, 3, np.nan, 12, np.nan, 25]) msg = "'fill_value' is not a valid keyword for Series.interpolate" msg2 = "Series.interpolate with method=pad" with pytest.raises(ValueError, match=msg): with tm.assert_produces_warning(FutureWarning, match=msg2): ser.interpolate(fill_value=3, method="pad") def test_interp_limit_forward(self): s = Series([1, 3, np.nan, np.nan, np.nan, 11]) # Provide 'forward' (the default) explicitly here. expected = Series([1.0, 3.0, 5.0, 7.0, np.nan, 11.0]) result = s.interpolate(method="linear", limit=2, limit_direction="forward") tm.assert_series_equal(result, expected) result = s.interpolate(method="linear", limit=2, limit_direction="FORWARD") tm.assert_series_equal(result, expected) def test_interp_unlimited(self): # these test are for issue #16282 default Limit=None is unlimited s = Series([np.nan, 1.0, 3.0, np.nan, np.nan, np.nan, 11.0, np.nan]) expected = Series([1.0, 1.0, 3.0, 5.0, 7.0, 9.0, 11.0, 11.0]) result = s.interpolate(method="linear", limit_direction="both") tm.assert_series_equal(result, expected) expected = Series([np.nan, 1.0, 3.0, 5.0, 7.0, 9.0, 11.0, 11.0]) result = s.interpolate(method="linear", limit_direction="forward") tm.assert_series_equal(result, expected) expected = Series([1.0, 1.0, 3.0, 5.0, 7.0, 9.0, 11.0, np.nan]) result = s.interpolate(method="linear", limit_direction="backward") tm.assert_series_equal(result, expected) def test_interp_limit_bad_direction(self): s = Series([1, 3, np.nan, np.nan, np.nan, 11]) msg = ( r"Invalid limit_direction: expecting one of \['forward', " r"'backward', 'both'\], got 'abc'" ) with pytest.raises(ValueError, match=msg): s.interpolate(method="linear", limit=2, limit_direction="abc") # raises an error even if no limit is specified. with pytest.raises(ValueError, match=msg): s.interpolate(method="linear", limit_direction="abc") # limit_area introduced GH #16284 def test_interp_limit_area(self): # These tests are for issue #9218 -- fill NaNs in both directions. s = Series([np.nan, np.nan, 3, np.nan, np.nan, np.nan, 7, np.nan, np.nan]) expected = Series([np.nan, np.nan, 3.0, 4.0, 5.0, 6.0, 7.0, np.nan, np.nan]) result = s.interpolate(method="linear", limit_area="inside") tm.assert_series_equal(result, expected) expected = Series( [np.nan, np.nan, 3.0, 4.0, np.nan, np.nan, 7.0, np.nan, np.nan] ) result = s.interpolate(method="linear", limit_area="inside", limit=1) tm.assert_series_equal(result, expected) expected = Series([np.nan, np.nan, 3.0, 4.0, np.nan, 6.0, 7.0, np.nan, np.nan]) result = s.interpolate( method="linear", limit_area="inside", limit_direction="both", limit=1 ) tm.assert_series_equal(result, expected) expected = Series([np.nan, np.nan, 3.0, np.nan, np.nan, np.nan, 7.0, 7.0, 7.0]) result = s.interpolate(method="linear", limit_area="outside") tm.assert_series_equal(result, expected) expected = Series( [np.nan, np.nan, 3.0, np.nan, np.nan, np.nan, 7.0, 7.0, np.nan] ) result = s.interpolate(method="linear", limit_area="outside", limit=1) tm.assert_series_equal(result, expected) expected = Series([np.nan, 3.0, 3.0, np.nan, np.nan, np.nan, 7.0, 7.0, np.nan]) result = s.interpolate( method="linear", limit_area="outside", limit_direction="both", limit=1 ) tm.assert_series_equal(result, expected) expected = Series([3.0, 3.0, 3.0, np.nan, np.nan, np.nan, 7.0, np.nan, np.nan]) result = s.interpolate( method="linear", limit_area="outside", limit_direction="backward" ) tm.assert_series_equal(result, expected) # raises an error even if limit type is wrong. msg = r"Invalid limit_area: expecting one of \['inside', 'outside'\], got abc" with pytest.raises(ValueError, match=msg): s.interpolate(method="linear", limit_area="abc") @pytest.mark.parametrize( "method, limit_direction, expected", [ ("pad", "backward", "forward"), ("ffill", "backward", "forward"), ("backfill", "forward", "backward"), ("bfill", "forward", "backward"), ("pad", "both", "forward"), ("ffill", "both", "forward"), ("backfill", "both", "backward"), ("bfill", "both", "backward"), ], ) def test_interp_limit_direction_raises(self, method, limit_direction, expected): # https://github.com/pandas-dev/pandas/pull/34746 s = Series([1, 2, 3]) msg = f"`limit_direction` must be '{expected}' for method `{method}`" msg2 = "Series.interpolate with method=" with pytest.raises(ValueError, match=msg): with tm.assert_produces_warning(FutureWarning, match=msg2): s.interpolate(method=method, limit_direction=limit_direction) @pytest.mark.parametrize( "data, expected_data, kwargs", ( ( [np.nan, np.nan, 3, np.nan, np.nan, np.nan, 7, np.nan, np.nan], [np.nan, np.nan, 3.0, 3.0, 3.0, 3.0, 7.0, np.nan, np.nan], {"method": "pad", "limit_area": "inside"}, ), ( [np.nan, np.nan, 3, np.nan, np.nan, np.nan, 7, np.nan, np.nan], [np.nan, np.nan, 3.0, 3.0, np.nan, np.nan, 7.0, np.nan, np.nan], {"method": "pad", "limit_area": "inside", "limit": 1}, ), ( [np.nan, np.nan, 3, np.nan, np.nan, np.nan, 7, np.nan, np.nan], [np.nan, np.nan, 3.0, np.nan, np.nan, np.nan, 7.0, 7.0, 7.0], {"method": "pad", "limit_area": "outside"}, ), ( [np.nan, np.nan, 3, np.nan, np.nan, np.nan, 7, np.nan, np.nan], [np.nan, np.nan, 3.0, np.nan, np.nan, np.nan, 7.0, 7.0, np.nan], {"method": "pad", "limit_area": "outside", "limit": 1}, ), ( [np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan], [np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan], {"method": "pad", "limit_area": "outside", "limit": 1}, ), ( range(5), range(5), {"method": "pad", "limit_area": "outside", "limit": 1}, ), ), ) def test_interp_limit_area_with_pad(self, data, expected_data, kwargs): # GH26796 s = Series(data) expected = Series(expected_data) msg = "Series.interpolate with method=pad" with tm.assert_produces_warning(FutureWarning, match=msg): result = s.interpolate(**kwargs) tm.assert_series_equal(result, expected) @pytest.mark.parametrize( "data, expected_data, kwargs", ( ( [np.nan, np.nan, 3, np.nan, np.nan, np.nan, 7, np.nan, np.nan], [np.nan, np.nan, 3.0, 7.0, 7.0, 7.0, 7.0, np.nan, np.nan], {"method": "bfill", "limit_area": "inside"}, ), ( [np.nan, np.nan, 3, np.nan, np.nan, np.nan, 7, np.nan, np.nan], [np.nan, np.nan, 3.0, np.nan, np.nan, 7.0, 7.0, np.nan, np.nan], {"method": "bfill", "limit_area": "inside", "limit": 1}, ), ( [np.nan, np.nan, 3, np.nan, np.nan, np.nan, 7, np.nan, np.nan], [3.0, 3.0, 3.0, np.nan, np.nan, np.nan, 7.0, np.nan, np.nan], {"method": "bfill", "limit_area": "outside"}, ), ( [np.nan, np.nan, 3, np.nan, np.nan, np.nan, 7, np.nan, np.nan], [np.nan, 3.0, 3.0, np.nan, np.nan, np.nan, 7.0, np.nan, np.nan], {"method": "bfill", "limit_area": "outside", "limit": 1}, ), ), ) def test_interp_limit_area_with_backfill(self, data, expected_data, kwargs): # GH26796 s = Series(data) expected = Series(expected_data) msg = "Series.interpolate with method=bfill" with tm.assert_produces_warning(FutureWarning, match=msg): result = s.interpolate(**kwargs) tm.assert_series_equal(result, expected) def test_interp_limit_direction(self): # These tests are for issue #9218 -- fill NaNs in both directions. s = Series([1, 3, np.nan, np.nan, np.nan, 11]) expected = Series([1.0, 3.0, np.nan, 7.0, 9.0, 11.0]) result = s.interpolate(method="linear", limit=2, limit_direction="backward") tm.assert_series_equal(result, expected) expected = Series([1.0, 3.0, 5.0, np.nan, 9.0, 11.0]) result = s.interpolate(method="linear", limit=1, limit_direction="both") tm.assert_series_equal(result, expected) # Check that this works on a longer series of nans. s = Series([1, 3, np.nan, np.nan, np.nan, 7, 9, np.nan, np.nan, 12, np.nan]) expected = Series([1.0, 3.0, 4.0, 5.0, 6.0, 7.0, 9.0, 10.0, 11.0, 12.0, 12.0]) result = s.interpolate(method="linear", limit=2, limit_direction="both") tm.assert_series_equal(result, expected) expected = Series( [1.0, 3.0, 4.0, np.nan, 6.0, 7.0, 9.0, 10.0, 11.0, 12.0, 12.0] ) result = s.interpolate(method="linear", limit=1, limit_direction="both") tm.assert_series_equal(result, expected) def test_interp_limit_to_ends(self): # These test are for issue #10420 -- flow back to beginning. s = Series([np.nan, np.nan, 5, 7, 9, np.nan]) expected = Series([5.0, 5.0, 5.0, 7.0, 9.0, np.nan]) result = s.interpolate(method="linear", limit=2, limit_direction="backward") tm.assert_series_equal(result, expected) expected = Series([5.0, 5.0, 5.0, 7.0, 9.0, 9.0]) result = s.interpolate(method="linear", limit=2, limit_direction="both") tm.assert_series_equal(result, expected) def test_interp_limit_before_ends(self): # These test are for issue #11115 -- limit ends properly. s = Series([np.nan, np.nan, 5, 7, np.nan, np.nan]) expected = Series([np.nan, np.nan, 5.0, 7.0, 7.0, np.nan]) result = s.interpolate(method="linear", limit=1, limit_direction="forward") tm.assert_series_equal(result, expected) expected = Series([np.nan, 5.0, 5.0, 7.0, np.nan, np.nan]) result = s.interpolate(method="linear", limit=1, limit_direction="backward") tm.assert_series_equal(result, expected) expected = Series([np.nan, 5.0, 5.0, 7.0, 7.0, np.nan]) result = s.interpolate(method="linear", limit=1, limit_direction="both") tm.assert_series_equal(result, expected) def test_interp_all_good(self): pytest.importorskip("scipy") s = Series([1, 2, 3]) result = s.interpolate(method="polynomial", order=1) tm.assert_series_equal(result, s) # non-scipy result = s.interpolate() tm.assert_series_equal(result, s) @pytest.mark.parametrize( "check_scipy", [False, pytest.param(True, marks=td.skip_if_no("scipy"))] ) def test_interp_multiIndex(self, check_scipy): idx = MultiIndex.from_tuples([(0, "a"), (1, "b"), (2, "c")]) s = Series([1, 2, np.nan], index=idx) expected = s.copy() expected.loc[2] = 2 result = s.interpolate() tm.assert_series_equal(result, expected) msg = "Only `method=linear` interpolation is supported on MultiIndexes" if check_scipy: with pytest.raises(ValueError, match=msg): s.interpolate(method="polynomial", order=1) def test_interp_nonmono_raise(self): pytest.importorskip("scipy") s = Series([1, np.nan, 3], index=[0, 2, 1]) msg = "krogh interpolation requires that the index be monotonic" with pytest.raises(ValueError, match=msg): s.interpolate(method="krogh") @pytest.mark.parametrize("method", ["nearest", "pad"]) def test_interp_datetime64(self, method, tz_naive_fixture): pytest.importorskip("scipy") df = Series( [1, np.nan, 3], index=date_range("1/1/2000", periods=3, tz=tz_naive_fixture) ) warn = None if method == "nearest" else FutureWarning msg = "Series.interpolate with method=pad is deprecated" with tm.assert_produces_warning(warn, match=msg): result = df.interpolate(method=method) if warn is not None: # check the "use ffill instead" is equivalent alt = df.ffill() tm.assert_series_equal(result, alt) expected = Series( [1.0, 1.0, 3.0], index=date_range("1/1/2000", periods=3, tz=tz_naive_fixture), ) tm.assert_series_equal(result, expected) def test_interp_pad_datetime64tz_values(self): # GH#27628 missing.interpolate_2d should handle datetimetz values dti = date_range("2015-04-05", periods=3, tz="US/Central") ser = Series(dti) ser[1] = pd.NaT msg = "Series.interpolate with method=pad is deprecated" with tm.assert_produces_warning(FutureWarning, match=msg): result = ser.interpolate(method="pad") # check the "use ffill instead" is equivalent alt = ser.ffill() tm.assert_series_equal(result, alt) expected = Series(dti) expected[1] = expected[0] tm.assert_series_equal(result, expected) def test_interp_limit_no_nans(self): # GH 7173 s = Series([1.0, 2.0, 3.0]) result = s.interpolate(limit=1) expected = s tm.assert_series_equal(result, expected) @pytest.mark.parametrize("method", ["polynomial", "spline"]) def test_no_order(self, method): # see GH-10633, GH-24014 pytest.importorskip("scipy") s = Series([0, 1, np.nan, 3]) msg = "You must specify the order of the spline or polynomial" with pytest.raises(ValueError, match=msg): s.interpolate(method=method) @pytest.mark.parametrize("order", [-1, -1.0, 0, 0.0, np.nan]) def test_interpolate_spline_invalid_order(self, order): pytest.importorskip("scipy") s = Series([0, 1, np.nan, 3]) msg = "order needs to be specified and greater than 0" with pytest.raises(ValueError, match=msg): s.interpolate(method="spline", order=order) def test_spline(self): pytest.importorskip("scipy") s = Series([1, 2, np.nan, 4, 5, np.nan, 7]) result = s.interpolate(method="spline", order=1) expected = Series([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]) tm.assert_series_equal(result, expected) def test_spline_extrapolate(self): pytest.importorskip("scipy") s = Series([1, 2, 3, 4, np.nan, 6, np.nan]) result3 = s.interpolate(method="spline", order=1, ext=3) expected3 = Series([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 6.0]) tm.assert_series_equal(result3, expected3) result1 = s.interpolate(method="spline", order=1, ext=0) expected1 = Series([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]) tm.assert_series_equal(result1, expected1) def test_spline_smooth(self): pytest.importorskip("scipy") s = Series([1, 2, np.nan, 4, 5.1, np.nan, 7]) assert ( s.interpolate(method="spline", order=3, s=0)[5] != s.interpolate(method="spline", order=3)[5] ) def test_spline_interpolation(self): # Explicit cast to float to avoid implicit cast when setting np.nan pytest.importorskip("scipy") s = Series(np.arange(10) ** 2, dtype="float") s[np.random.default_rng(2).integers(0, 9, 3)] = np.nan result1 = s.interpolate(method="spline", order=1) expected1 = s.interpolate(method="spline", order=1) tm.assert_series_equal(result1, expected1) def test_interp_timedelta64(self): # GH 6424 df = Series([1, np.nan, 3], index=pd.to_timedelta([1, 2, 3])) result = df.interpolate(method="time") expected = Series([1.0, 2.0, 3.0], index=pd.to_timedelta([1, 2, 3])) tm.assert_series_equal(result, expected) # test for non uniform spacing df = Series([1, np.nan, 3], index=pd.to_timedelta([1, 2, 4])) result = df.interpolate(method="time") expected = Series([1.0, 1.666667, 3.0], index=pd.to_timedelta([1, 2, 4])) tm.assert_series_equal(result, expected) def test_series_interpolate_method_values(self): # GH#1646 rng = date_range("1/1/2000", "1/20/2000", freq="D") ts = Series(np.random.default_rng(2).standard_normal(len(rng)), index=rng) ts[::2] = np.nan result = ts.interpolate(method="values") exp = ts.interpolate() tm.assert_series_equal(result, exp) def test_series_interpolate_intraday(self): # #1698 index = date_range("1/1/2012", periods=4, freq="12D") ts = Series([0, 12, 24, 36], index) new_index = index.append(index + pd.DateOffset(days=1)).sort_values() exp = ts.reindex(new_index).interpolate(method="time") index = date_range("1/1/2012", periods=4, freq="12h") ts = Series([0, 12, 24, 36], index) new_index = index.append(index + pd.DateOffset(hours=1)).sort_values() result = ts.reindex(new_index).interpolate(method="time") tm.assert_numpy_array_equal(result.values, exp.values) @pytest.mark.parametrize( "ind", [ ["a", "b", "c", "d"], pd.period_range(start="2019-01-01", periods=4), pd.interval_range(start=0, end=4), ], ) def test_interp_non_timedelta_index(self, interp_methods_ind, ind): # gh 21662 df = pd.DataFrame([0, 1, np.nan, 3], index=ind) method, kwargs = interp_methods_ind if method == "pchip": pytest.importorskip("scipy") if method == "linear": result = df[0].interpolate(**kwargs) expected = Series([0.0, 1.0, 2.0, 3.0], name=0, index=ind) tm.assert_series_equal(result, expected) else: expected_error = ( "Index column must be numeric or datetime type when " f"using {method} method other than linear. " "Try setting a numeric or datetime index column before " "interpolating." ) with pytest.raises(ValueError, match=expected_error): df[0].interpolate(method=method, **kwargs) def test_interpolate_timedelta_index(self, request, interp_methods_ind): """ Tests for non numerical index types - object, period, timedelta Note that all methods except time, index, nearest and values are tested here. """ # gh 21662 pytest.importorskip("scipy") ind = pd.timedelta_range(start=1, periods=4) df = pd.DataFrame([0, 1, np.nan, 3], index=ind) method, kwargs = interp_methods_ind if method in {"cubic", "zero"}: request.applymarker( pytest.mark.xfail( reason=f"{method} interpolation is not supported for TimedeltaIndex" ) ) result = df[0].interpolate(method=method, **kwargs) expected = Series([0.0, 1.0, 2.0, 3.0], name=0, index=ind) tm.assert_series_equal(result, expected) @pytest.mark.parametrize( "ascending, expected_values", [(True, [1, 2, 3, 9, 10]), (False, [10, 9, 3, 2, 1])], ) def test_interpolate_unsorted_index(self, ascending, expected_values): # GH 21037 ts = Series(data=[10, 9, np.nan, 2, 1], index=[10, 9, 3, 2, 1]) result = ts.sort_index(ascending=ascending).interpolate(method="index") expected = Series(data=expected_values, index=expected_values, dtype=float) tm.assert_series_equal(result, expected) def test_interpolate_asfreq_raises(self): ser = Series(["a", None, "b"], dtype=object) msg2 = "Series.interpolate with object dtype" msg = "Invalid fill method" with pytest.raises(ValueError, match=msg): with tm.assert_produces_warning(FutureWarning, match=msg2): ser.interpolate(method="asfreq") def test_interpolate_fill_value(self): # GH#54920 pytest.importorskip("scipy") ser = Series([np.nan, 0, 1, np.nan, 3, np.nan]) result = ser.interpolate(method="nearest", fill_value=0) expected = Series([np.nan, 0, 1, 1, 3, 0]) tm.assert_series_equal(result, expected)