Skip to content

Commit ff0deec

Browse files
mroeschkejreback
authored andcommitted
Bug: Raise ValueError with interpolate & fillna limit = 0 (#9217)
closes #9217 Author: Matt Roeschke <[email protected]> Closes #14994 from mroeschke/fix_9217 and squashes the following commits: c1790ee [Matt Roeschke] Unify ValueError message and correct cython limits 6f041e6 [Matt Roeschke] Bug: Raise ValueError with interpolate limit = 0
1 parent 86ca84d commit ff0deec

File tree

6 files changed

+56
-17
lines changed

6 files changed

+56
-17
lines changed

doc/source/whatsnew/v0.20.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,7 @@ Other API Changes
421421
- ``SparseArray.cumsum()`` and ``SparseSeries.cumsum()`` will now always return ``SparseArray`` and ``SparseSeries`` respectively (:issue:`12855`)
422422
- ``DataFrame.applymap()`` with an empty ``DataFrame`` will return a copy of the empty ``DataFrame`` instead of a ``Series`` (:issue:`8222`)
423423
- ``.loc`` has compat with ``.ix`` for accepting iterators, and NamedTuples (:issue:`15120`)
424+
- ``interpolate()`` and ``fillna()`` will raise a ``ValueError`` if the ``limit`` keyword argument is not greater than 0. (:issue:`9217`)
424425
- ``pd.read_csv()`` will now issue a ``ParserWarning`` whenever there are conflicting values provided by the ``dialect`` parameter and the user (:issue:`14898`)
425426
- ``pd.read_csv()`` will now raise a ``ValueError`` for the C engine if the quote character is larger than than one byte (:issue:`11592`)
426427
- ``inplace`` arguments now require a boolean value, else a ``ValueError`` is thrown (:issue:`14189`)

pandas/core/generic.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -3262,7 +3262,7 @@ def convert_objects(self, convert_dates=True, convert_numeric=False,
32623262
a gap with more than this number of consecutive NaNs, it will only
32633263
be partially filled. If method is not specified, this is the
32643264
maximum number of entries along the entire axis where NaNs will be
3265-
filled.
3265+
filled. Must be greater than 0 if not None.
32663266
downcast : dict, default is None
32673267
a dict of item->dtype of what to downcast if possible,
32683268
or the string 'infer' which will try to downcast to an appropriate
@@ -3281,6 +3281,7 @@ def convert_objects(self, convert_dates=True, convert_numeric=False,
32813281
def fillna(self, value=None, method=None, axis=None, inplace=False,
32823282
limit=None, downcast=None):
32833283
inplace = validate_bool_kwarg(inplace, 'inplace')
3284+
32843285
if isinstance(value, (list, tuple)):
32853286
raise TypeError('"value" parameter must be a scalar or dict, but '
32863287
'you passed a "{0}"'.format(type(value).__name__))
@@ -3292,7 +3293,6 @@ def fillna(self, value=None, method=None, axis=None, inplace=False,
32923293
axis = 0
32933294
axis = self._get_axis_number(axis)
32943295
method = missing.clean_fill_method(method)
3295-
32963296
from pandas import DataFrame
32973297
if value is None:
32983298
if method is None:
@@ -3687,7 +3687,7 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None,
36873687
* 0: fill column-by-column
36883688
* 1: fill row-by-row
36893689
limit : int, default None.
3690-
Maximum number of consecutive NaNs to fill.
3690+
Maximum number of consecutive NaNs to fill. Must be greater than 0.
36913691
limit_direction : {'forward', 'backward', 'both'}, default 'forward'
36923692
If limit is specified, consecutive NaNs will be filled in this
36933693
direction.

pandas/core/internals.py

+4
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,10 @@ def fillna(self, value, limit=None, inplace=False, downcast=None,
372372
original_value = value
373373
mask = isnull(self.values)
374374
if limit is not None:
375+
if not is_integer(limit):
376+
raise ValueError('Limit must be an integer')
377+
if limit < 1:
378+
raise ValueError('Limit must be greater than 0')
375379
if self.ndim > 2:
376380
raise NotImplementedError("number of dimensions for 'fillna' "
377381
"is currently limited to 2")

pandas/core/missing.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
is_float_dtype, is_datetime64_dtype,
1313
is_datetime64tz_dtype, is_integer_dtype,
1414
_ensure_float64, is_scalar,
15-
needs_i8_conversion)
15+
needs_i8_conversion, is_integer)
1616
from pandas.types.missing import isnull
1717

1818

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

172-
if limit:
172+
if limit is not None:
173+
if not is_integer(limit):
174+
raise ValueError('Limit must be an integer')
175+
if limit < 1:
176+
raise ValueError('Limit must be greater than 0')
173177
if limit_direction == 'forward':
174178
violate_limit = sorted(start_nans | set(_interp_limit(invalid,
175179
limit, 0)))

pandas/src/algos_common_helper.pxi.in

+24-12
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,10 @@ def pad_{{name}}(ndarray[{{c_type}}] old, ndarray[{{c_type}}] new,
8383
if limit is None:
8484
lim = nright
8585
else:
86-
if limit < 0:
87-
raise ValueError('Limit must be non-negative')
86+
if not util.is_integer_object(limit):
87+
raise ValueError('Limit must be an integer')
88+
if limit < 1:
89+
raise ValueError('Limit must be greater than 0')
8890
lim = limit
8991

9092
if nleft == 0 or nright == 0 or new[nright - 1] < old[0]:
@@ -146,8 +148,10 @@ def pad_inplace_{{name}}(ndarray[{{c_type}}] values,
146148
if limit is None:
147149
lim = N
148150
else:
149-
if limit < 0:
150-
raise ValueError('Limit must be non-negative')
151+
if not util.is_integer_object(limit):
152+
raise ValueError('Limit must be an integer')
153+
if limit < 1:
154+
raise ValueError('Limit must be greater than 0')
151155
lim = limit
152156

153157
val = values[0]
@@ -180,8 +184,10 @@ def pad_2d_inplace_{{name}}(ndarray[{{c_type}}, ndim=2] values,
180184
if limit is None:
181185
lim = N
182186
else:
183-
if limit < 0:
184-
raise ValueError('Limit must be non-negative')
187+
if not util.is_integer_object(limit):
188+
raise ValueError('Limit must be an integer')
189+
if limit < 1:
190+
raise ValueError('Limit must be greater than 0')
185191
lim = limit
186192

187193
for j in range(K):
@@ -240,8 +246,10 @@ def backfill_{{name}}(ndarray[{{c_type}}] old, ndarray[{{c_type}}] new,
240246
if limit is None:
241247
lim = nright
242248
else:
243-
if limit < 0:
244-
raise ValueError('Limit must be non-negative')
249+
if not util.is_integer_object(limit):
250+
raise ValueError('Limit must be an integer')
251+
if limit < 1:
252+
raise ValueError('Limit must be greater than 0')
245253
lim = limit
246254

247255
if nleft == 0 or nright == 0 or new[0] > old[nleft - 1]:
@@ -304,8 +312,10 @@ def backfill_inplace_{{name}}(ndarray[{{c_type}}] values,
304312
if limit is None:
305313
lim = N
306314
else:
307-
if limit < 0:
308-
raise ValueError('Limit must be non-negative')
315+
if not util.is_integer_object(limit):
316+
raise ValueError('Limit must be an integer')
317+
if limit < 1:
318+
raise ValueError('Limit must be greater than 0')
309319
lim = limit
310320

311321
val = values[N - 1]
@@ -338,8 +348,10 @@ def backfill_2d_inplace_{{name}}(ndarray[{{c_type}}, ndim=2] values,
338348
if limit is None:
339349
lim = N
340350
else:
341-
if limit < 0:
342-
raise ValueError('Limit must be non-negative')
351+
if not util.is_integer_object(limit):
352+
raise ValueError('Limit must be an integer')
353+
if limit < 1:
354+
raise ValueError('Limit must be greater than 0')
343355
lim = limit
344356

345357
for j in range(K):

pandas/tests/series/test_missing.py

+18
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,13 @@ def test_fillna_raise(self):
295295
self.assertRaises(TypeError, s.fillna, [1, 2])
296296
self.assertRaises(TypeError, s.fillna, (1, 2))
297297

298+
# related GH 9217, make sure limit is an int and greater than 0
299+
s = Series([1, 2, 3, None])
300+
for limit in [-1, 0, 1., 2.]:
301+
for method in ['backfill', 'bfill', 'pad', 'ffill', None]:
302+
with tm.assertRaises(ValueError):
303+
s.fillna(1, limit=limit, method=method)
304+
298305
def test_fillna_nat(self):
299306
series = Series([0, 1, 2, tslib.iNaT], dtype='M8[ns]')
300307

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

875+
# GH 9217, make sure limit is an int and greater than 0
876+
methods = ['linear', 'time', 'index', 'values', 'nearest', 'zero',
877+
'slinear', 'quadratic', 'cubic', 'barycentric', 'krogh',
878+
'polynomial', 'spline', 'piecewise_polynomial', None,
879+
'from_derivatives', 'pchip', 'akima']
880+
s = pd.Series([1, 2, np.nan, np.nan, 5])
881+
for limit in [-1, 0, 1., 2.]:
882+
for method in methods:
883+
with tm.assertRaises(ValueError):
884+
s.interpolate(limit=limit, method=method)
885+
868886
def test_interp_limit_forward(self):
869887
s = Series([1, 3, np.nan, np.nan, np.nan, 11])
870888

0 commit comments

Comments
 (0)