Skip to content

Commit 910207f

Browse files
mgasvodajreback
authored andcommitted
BUG: clip should handle null values
closes #17276 Author: Michael Gasvoda <mgasvoda@mercatus.gmu.edu> Author: mgasvoda <mgasvoda01@gmail.com> Closes #17288 from mgasvoda/master and squashes the following commits: a1dbdf2 [mgasvoda] Merge branch 'master' into master 9333952 [Michael Gasvoda] Checking output of tests 4e0464e [Michael Gasvoda] fixing whatsnew text c442040 [Michael Gasvoda] formatting fixes 7e23678 [Michael Gasvoda] formatting updates 781ea72 [Michael Gasvoda] whatsnew entry d9627fe [Michael Gasvoda] adding clip tests 9aa0159 [Michael Gasvoda] Treating na values as none for clips
1 parent eff1f88 commit 910207f

File tree

4 files changed

+30
-21
lines changed

4 files changed

+30
-21
lines changed

doc/source/whatsnew/v0.21.0.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,6 @@ Other Enhancements
132132

133133

134134

135-
136135
.. _whatsnew_0210.api_breaking:
137136

138137
Backwards incompatible API changes
@@ -384,6 +383,7 @@ Reshaping
384383
Numeric
385384
^^^^^^^
386385
- Bug in ``.clip()`` with ``axis=1`` and a list-like for ``threshold`` is passed; previously this raised ``ValueError`` (:issue:`15390`)
386+
- :func:`Series.clip()` and :func:`DataFrame.clip()` now treat NA values for upper and lower arguments as ``None`` instead of raising ``ValueError`` (:issue:`17276`).
387387

388388

389389
Categorical

pandas/core/generic.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -4741,9 +4741,6 @@ def _clip_with_one_bound(self, threshold, method, axis, inplace):
47414741
if axis is not None:
47424742
axis = self._get_axis_number(axis)
47434743

4744-
if np.any(isna(threshold)):
4745-
raise ValueError("Cannot use an NA value as a clip threshold")
4746-
47474744
# method is self.le for upper bound and self.ge for lower bound
47484745
if is_scalar(threshold) and is_number(threshold):
47494746
if method.__name__ == 'le':
@@ -4823,6 +4820,14 @@ def clip(self, lower=None, upper=None, axis=None, inplace=False,
48234820

48244821
axis = nv.validate_clip_with_axis(axis, args, kwargs)
48254822

4823+
# GH 17276
4824+
# numpy doesn't like NaN as a clip value
4825+
# so ignore
4826+
if np.any(pd.isnull(lower)):
4827+
lower = None
4828+
if np.any(pd.isnull(upper)):
4829+
upper = None
4830+
48264831
# GH 2747 (arguments were reversed)
48274832
if lower is not None and upper is not None:
48284833
if is_scalar(lower) and is_scalar(upper):
@@ -4839,7 +4844,6 @@ def clip(self, lower=None, upper=None, axis=None, inplace=False,
48394844
if upper is not None:
48404845
if inplace:
48414846
result = self
4842-
48434847
result = result.clip_upper(upper, axis, inplace=inplace)
48444848

48454849
return result

pandas/tests/frame/test_analytics.py

+10-16
Original file line numberDiff line numberDiff line change
@@ -1931,22 +1931,16 @@ def test_clip_against_frame(self, axis):
19311931
tm.assert_frame_equal(clipped_df[ub_mask], ub[ub_mask])
19321932
tm.assert_frame_equal(clipped_df[mask], df[mask])
19331933

1934-
def test_clip_na(self):
1935-
msg = "Cannot use an NA"
1936-
with tm.assert_raises_regex(ValueError, msg):
1937-
self.frame.clip(lower=np.nan)
1938-
1939-
with tm.assert_raises_regex(ValueError, msg):
1940-
self.frame.clip(lower=[np.nan])
1941-
1942-
with tm.assert_raises_regex(ValueError, msg):
1943-
self.frame.clip(upper=np.nan)
1944-
1945-
with tm.assert_raises_regex(ValueError, msg):
1946-
self.frame.clip(upper=[np.nan])
1947-
1948-
with tm.assert_raises_regex(ValueError, msg):
1949-
self.frame.clip(lower=np.nan, upper=np.nan)
1934+
def test_clip_with_na_args(self):
1935+
"""Should process np.nan argument as None """
1936+
# GH # 17276
1937+
tm.assert_frame_equal(self.frame.clip(np.nan), self.frame)
1938+
tm.assert_frame_equal(self.frame.clip(upper=[1, 2, np.nan]),
1939+
self.frame)
1940+
tm.assert_frame_equal(self.frame.clip(lower=[1, np.nan, 3]),
1941+
self.frame)
1942+
tm.assert_frame_equal(self.frame.clip(upper=np.nan, lower=np.nan),
1943+
self.frame)
19501944

19511945
# Matrix-like
19521946

pandas/tests/series/test_analytics.py

+11
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,17 @@ def test_clip_types_and_nulls(self):
10001000
assert list(isna(s)) == list(isna(l))
10011001
assert list(isna(s)) == list(isna(u))
10021002

1003+
def test_clip_with_na_args(self):
1004+
"""Should process np.nan argument as None """
1005+
# GH # 17276
1006+
s = Series([1, 2, 3])
1007+
1008+
assert_series_equal(s.clip(np.nan), Series([1, 2, 3]))
1009+
assert_series_equal(s.clip(upper=[1, 1, np.nan]), Series([1, 2, 3]))
1010+
assert_series_equal(s.clip(lower=[1, np.nan, 1]), Series([1, 2, 3]))
1011+
assert_series_equal(s.clip(upper=np.nan, lower=np.nan),
1012+
Series([1, 2, 3]))
1013+
10031014
def test_clip_against_series(self):
10041015
# GH #6966
10051016

0 commit comments

Comments
 (0)