Skip to content

Commit 7d6afc4

Browse files
committed
ENH: .isnull and .notnull have been added as methods to Index to make this more consistent with the Series API
Author: Jeff Reback <[email protected]> Closes #15300 from jreback/null and squashes the following commits: 8c35656 [Jeff Reback] DOC: move Index.where to shared_docs e4502bf [Jeff Reback] ENH: .isnull and .notnull have been added as methods to Index to make this more consistent with the Series API
1 parent 72992df commit 7d6afc4

File tree

6 files changed

+73
-28
lines changed

6 files changed

+73
-28
lines changed

doc/source/api.rst

+8
Original file line numberDiff line numberDiff line change
@@ -1356,8 +1356,16 @@ Modifying and Computations
13561356
Index.unique
13571357
Index.nunique
13581358
Index.value_counts
1359+
1360+
Missing Values
1361+
~~~~~~~~~~~~~~
1362+
.. autosummary::
1363+
:toctree: generated/
1364+
13591365
Index.fillna
13601366
Index.dropna
1367+
Index.isnull
1368+
Index.notnull
13611369

13621370
Conversion
13631371
~~~~~~~~~~

doc/source/whatsnew/v0.20.0.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ Other enhancements
125125

126126
- ``pd.read_excel`` now preserves sheet order when using ``sheetname=None`` (:issue:`9930`)
127127
- Multiple offset aliases with decimal points are now supported (e.g. '0.5min' is parsed as '30s') (:issue:`8419`)
128-
128+
- ``.isnull()`` and ``.notnull()`` have been added to ``Index`` object to make them more consistent with the ``Series`` API (:issue:`15300`)
129129
- ``pd.read_gbq`` method now allows query configuration preferences (:issue:`14742`)
130130

131131
- New ``UnsortedIndexError`` (subclass of ``KeyError``) raised when indexing/slicing into an

pandas/indexes/base.py

+36-2
Original file line numberDiff line numberDiff line change
@@ -564,8 +564,7 @@ def repeat(self, repeats, *args, **kwargs):
564564
nv.validate_repeat(args, kwargs)
565565
return self._shallow_copy(self._values.repeat(repeats))
566566

567-
def where(self, cond, other=None):
568-
"""
567+
_index_shared_docs['where'] = """
569568
.. versionadded:: 0.19.0
570569
571570
Return an Index of same shape as self and whose corresponding
@@ -577,6 +576,9 @@ def where(self, cond, other=None):
577576
cond : boolean same length as self
578577
other : scalar, or array-like
579578
"""
579+
580+
@Appender(_index_shared_docs['where'])
581+
def where(self, cond, other=None):
580582
if other is None:
581583
other = self._na_value
582584
values = np.where(cond, self.values, other)
@@ -1662,6 +1664,38 @@ def hasnans(self):
16621664
else:
16631665
return False
16641666

1667+
def isnull(self):
1668+
"""
1669+
Detect missing values
1670+
1671+
.. versionadded:: 0.20.0
1672+
1673+
Returns
1674+
-------
1675+
a boolean array of whether my values are null
1676+
1677+
See also
1678+
--------
1679+
pandas.isnull : pandas version
1680+
"""
1681+
return self._isnan
1682+
1683+
def notnull(self):
1684+
"""
1685+
Reverse of isnull
1686+
1687+
.. versionadded:: 0.20.0
1688+
1689+
Returns
1690+
-------
1691+
a boolean array of whether my values are not null
1692+
1693+
See also
1694+
--------
1695+
pandas.notnull : pandas version
1696+
"""
1697+
return ~self.isnull()
1698+
16651699
def putmask(self, mask, value):
16661700
"""
16671701
return a new Index of the values set with the mask

pandas/indexes/category.py

+1-12
Original file line numberDiff line numberDiff line change
@@ -332,19 +332,8 @@ def _can_reindex(self, indexer):
332332
""" always allow reindexing """
333333
pass
334334

335+
@Appender(_index_shared_docs['where'])
335336
def where(self, cond, other=None):
336-
"""
337-
.. versionadded:: 0.19.0
338-
339-
Return an Index of same shape as self and whose corresponding
340-
entries are from self where cond is True and otherwise are from
341-
other.
342-
343-
Parameters
344-
----------
345-
cond : boolean same length as self
346-
other : scalar, or array-like
347-
"""
348337
if other is None:
349338
other = self._na_value
350339
values = np.where(cond, self.values, other)

pandas/tests/indexes/common.py

+26-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from pandas import (Series, Index, Float64Index, Int64Index, UInt64Index,
99
RangeIndex, MultiIndex, CategoricalIndex, DatetimeIndex,
10-
TimedeltaIndex, PeriodIndex, notnull)
10+
TimedeltaIndex, PeriodIndex, notnull, isnull)
1111
from pandas.types.common import needs_i8_conversion
1212
from pandas.util.testing import assertRaisesRegexp
1313

@@ -879,3 +879,28 @@ def test_fillna(self):
879879
expected[1] = True
880880
self.assert_numpy_array_equal(idx._isnan, expected)
881881
self.assertTrue(idx.hasnans)
882+
883+
def test_nulls(self):
884+
# this is really a smoke test for the methods
885+
# as these are adequantely tested for function elsewhere
886+
887+
for name, index in self.indices.items():
888+
if len(index) == 0:
889+
self.assert_numpy_array_equal(
890+
index.isnull(), np.array([], dtype=bool))
891+
elif isinstance(index, MultiIndex):
892+
idx = index.copy()
893+
msg = "isnull is not defined for MultiIndex"
894+
with self.assertRaisesRegexp(NotImplementedError, msg):
895+
idx.isnull()
896+
else:
897+
898+
if not index.hasnans:
899+
self.assert_numpy_array_equal(
900+
index.isnull(), np.zeros(len(index), dtype=bool))
901+
self.assert_numpy_array_equal(
902+
index.notnull(), np.ones(len(index), dtype=bool))
903+
else:
904+
result = isnull(index)
905+
self.assert_numpy_array_equal(index.isnull(), result)
906+
self.assert_numpy_array_equal(index.notnull(), ~result)

pandas/tseries/base.py

+1-12
Original file line numberDiff line numberDiff line change
@@ -786,19 +786,8 @@ def repeat(self, repeats, *args, **kwargs):
786786
return self._shallow_copy(self.asi8.repeat(repeats),
787787
freq=freq)
788788

789+
@Appender(_index_shared_docs['where'])
789790
def where(self, cond, other=None):
790-
"""
791-
.. versionadded:: 0.19.0
792-
793-
Return an Index of same shape as self and whose corresponding
794-
entries are from self where cond is True and otherwise are from
795-
other.
796-
797-
Parameters
798-
----------
799-
cond : boolean same length as self
800-
other : scalar, or array-like
801-
"""
802791
other = _ensure_datetimelike_to_i8(other)
803792
values = _ensure_datetimelike_to_i8(self)
804793
result = np.where(cond, values, other).astype('i8')

0 commit comments

Comments
 (0)