Skip to content

Bug: Raise ValueError with interpolate & fillna limit = 0 (#9217) #14994

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.20.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ Other API Changes
- ``SparseArray.cumsum()`` and ``SparseSeries.cumsum()`` will now always return ``SparseArray`` and ``SparseSeries`` respectively (:issue:`12855`)
- ``DataFrame.applymap()`` with an empty ``DataFrame`` will return a copy of the empty ``DataFrame`` instead of a ``Series`` (:issue:`8222`)
- ``.loc`` has compat with ``.ix`` for accepting iterators, and NamedTuples (:issue:`15120`)
- ``interpolate()`` and ``fillna()`` will raise a ``ValueError`` if the ``limit`` keyword argument is not greater than 0. (:issue:`9217`)
- ``pd.read_csv()`` will now issue a ``ParserWarning`` whenever there are conflicting values provided by the ``dialect`` parameter and the user (:issue:`14898`)
- ``pd.read_csv()`` will now raise a ``ValueError`` for the C engine if the quote character is larger than than one byte (:issue:`11592`)
- ``inplace`` arguments now require a boolean value, else a ``ValueError`` is thrown (:issue:`14189`)
Expand Down
6 changes: 3 additions & 3 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -3262,7 +3262,7 @@ def convert_objects(self, convert_dates=True, convert_numeric=False,
a gap with more than this number of consecutive NaNs, it will only
be partially filled. If method is not specified, this is the
maximum number of entries along the entire axis where NaNs will be
filled.
filled. Must be greater than 0 if not None.
downcast : dict, default is None
a dict of item->dtype of what to downcast if possible,
or the string 'infer' which will try to downcast to an appropriate
Expand All @@ -3281,6 +3281,7 @@ def convert_objects(self, convert_dates=True, convert_numeric=False,
def fillna(self, value=None, method=None, axis=None, inplace=False,
limit=None, downcast=None):
inplace = validate_bool_kwarg(inplace, 'inplace')

if isinstance(value, (list, tuple)):
raise TypeError('"value" parameter must be a scalar or dict, but '
'you passed a "{0}"'.format(type(value).__name__))
Expand All @@ -3292,7 +3293,6 @@ def fillna(self, value=None, method=None, axis=None, inplace=False,
axis = 0
axis = self._get_axis_number(axis)
method = missing.clean_fill_method(method)

from pandas import DataFrame
if value is None:
if method is None:
Expand Down Expand Up @@ -3687,7 +3687,7 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None,
* 0: fill column-by-column
* 1: fill row-by-row
limit : int, default None.
Maximum number of consecutive NaNs to fill.
Maximum number of consecutive NaNs to fill. Must be greater than 0.
limit_direction : {'forward', 'backward', 'both'}, default 'forward'
If limit is specified, consecutive NaNs will be filled in this
direction.
Expand Down
4 changes: 4 additions & 0 deletions pandas/core/internals.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,10 @@ def fillna(self, value, limit=None, inplace=False, downcast=None,
original_value = value
mask = isnull(self.values)
if limit is not None:
if not is_integer(limit):
raise ValueError('Limit must be an integer')
if limit < 1:
raise ValueError('Limit must be greater than 0')
if self.ndim > 2:
raise NotImplementedError("number of dimensions for 'fillna' "
"is currently limited to 2")
Expand Down
8 changes: 6 additions & 2 deletions pandas/core/missing.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
is_float_dtype, is_datetime64_dtype,
is_datetime64tz_dtype, is_integer_dtype,
_ensure_float64, is_scalar,
needs_i8_conversion)
needs_i8_conversion, is_integer)
from pandas.types.missing import isnull


Expand Down Expand Up @@ -169,7 +169,11 @@ def _interp_limit(invalid, fw_limit, bw_limit):
# the beginning (see issues #9218 and #10420)
violate_limit = sorted(start_nans)

if limit:
if limit is not None:
if not is_integer(limit):
raise ValueError('Limit must be an integer')
if limit < 1:
raise ValueError('Limit must be greater than 0')
if limit_direction == 'forward':
violate_limit = sorted(start_nans | set(_interp_limit(invalid,
limit, 0)))
Expand Down
36 changes: 24 additions & 12 deletions pandas/src/algos_common_helper.pxi.in
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,10 @@ def pad_{{name}}(ndarray[{{c_type}}] old, ndarray[{{c_type}}] new,
if limit is None:
lim = nright
else:
if limit < 0:
raise ValueError('Limit must be non-negative')
if not util.is_integer_object(limit):
raise ValueError('Limit must be an integer')
if limit < 1:
raise ValueError('Limit must be greater than 0')
lim = limit

if nleft == 0 or nright == 0 or new[nright - 1] < old[0]:
Expand Down Expand Up @@ -146,8 +148,10 @@ def pad_inplace_{{name}}(ndarray[{{c_type}}] values,
if limit is None:
lim = N
else:
if limit < 0:
raise ValueError('Limit must be non-negative')
if not util.is_integer_object(limit):
raise ValueError('Limit must be an integer')
if limit < 1:
raise ValueError('Limit must be greater than 0')
lim = limit

val = values[0]
Expand Down Expand Up @@ -180,8 +184,10 @@ def pad_2d_inplace_{{name}}(ndarray[{{c_type}}, ndim=2] values,
if limit is None:
lim = N
else:
if limit < 0:
raise ValueError('Limit must be non-negative')
if not util.is_integer_object(limit):
raise ValueError('Limit must be an integer')
if limit < 1:
raise ValueError('Limit must be greater than 0')
lim = limit

for j in range(K):
Expand Down Expand Up @@ -240,8 +246,10 @@ def backfill_{{name}}(ndarray[{{c_type}}] old, ndarray[{{c_type}}] new,
if limit is None:
lim = nright
else:
if limit < 0:
raise ValueError('Limit must be non-negative')
if not util.is_integer_object(limit):
raise ValueError('Limit must be an integer')
if limit < 1:
raise ValueError('Limit must be greater than 0')
lim = limit

if nleft == 0 or nright == 0 or new[0] > old[nleft - 1]:
Expand Down Expand Up @@ -304,8 +312,10 @@ def backfill_inplace_{{name}}(ndarray[{{c_type}}] values,
if limit is None:
lim = N
else:
if limit < 0:
raise ValueError('Limit must be non-negative')
if not util.is_integer_object(limit):
raise ValueError('Limit must be an integer')
if limit < 1:
raise ValueError('Limit must be greater than 0')
lim = limit

val = values[N - 1]
Expand Down Expand Up @@ -338,8 +348,10 @@ def backfill_2d_inplace_{{name}}(ndarray[{{c_type}}, ndim=2] values,
if limit is None:
lim = N
else:
if limit < 0:
raise ValueError('Limit must be non-negative')
if not util.is_integer_object(limit):
raise ValueError('Limit must be an integer')
if limit < 1:
raise ValueError('Limit must be greater than 0')
lim = limit

for j in range(K):
Expand Down
18 changes: 18 additions & 0 deletions pandas/tests/series/test_missing.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,13 @@ def test_fillna_raise(self):
self.assertRaises(TypeError, s.fillna, [1, 2])
self.assertRaises(TypeError, s.fillna, (1, 2))

# related GH 9217, make sure limit is an int and greater than 0
s = Series([1, 2, 3, None])
for limit in [-1, 0, 1., 2.]:
for method in ['backfill', 'bfill', 'pad', 'ffill', None]:
with tm.assertRaises(ValueError):
s.fillna(1, limit=limit, method=method)

def test_fillna_nat(self):
series = Series([0, 1, 2, tslib.iNaT], dtype='M8[ns]')

Expand Down Expand Up @@ -865,6 +872,17 @@ def test_interp_limit(self):
result = s.interpolate(method='linear', limit=2)
assert_series_equal(result, expected)

# GH 9217, make sure limit is an int and greater than 0
methods = ['linear', 'time', 'index', 'values', 'nearest', 'zero',
'slinear', 'quadratic', 'cubic', 'barycentric', 'krogh',
'polynomial', 'spline', 'piecewise_polynomial', None,
'from_derivatives', 'pchip', 'akima']
s = pd.Series([1, 2, np.nan, np.nan, 5])
for limit in [-1, 0, 1., 2.]:
for method in methods:
with tm.assertRaises(ValueError):
s.interpolate(limit=limit, method=method)

def test_interp_limit_forward(self):
s = Series([1, 3, np.nan, np.nan, np.nan, 11])

Expand Down