Skip to content

Commit ab4fd51

Browse files
committed
Generalize NA Compat
1 parent 43a7857 commit ab4fd51

File tree

15 files changed

+109
-39
lines changed

15 files changed

+109
-39
lines changed

doc/source/whatsnew/v0.21.1.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ Bug Fixes
6262
- Bug in ``pd.Series.rolling.skew()`` and ``rolling.kurt()`` with all equal values has floating issue (:issue:`18044`)
6363
- Bug in ``pd.DataFrameGroupBy.count()`` when counting over a datetimelike column (:issue:`13393`)
6464
- Bug in ``pd.concat`` when empty and non-empty DataFrames or Series are concatenated (:issue:`18178` :issue:`18187`)
65-
- Bug in ``IntervalIndex.insert`` when attempting to insert ``NaN`` (:issue:`18295`)
6665

6766
Conversion
6867
^^^^^^^^^^

doc/source/whatsnew/v0.22.0.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ Other API Changes
4646
- :class:`Timestamp` will no longer silently ignore invalid ``freq`` arguments (:issue:`5168`)
4747
- :class:`CacheableOffset` and :class:`WeekDay` are no longer available in the ``pandas.tseries.offsets`` module (:issue:`17830`)
4848
- `tseries.frequencies.get_freq_group()` and `tseries.frequencies.DAYS` are removed from the public API (:issue:`18034`)
49+
- Inserting missing values into indexes will work for all types of indexes and automatically insert the correct type of missing value (``NaN``, ``NaT``, etc.) regardless of the type passed in (:issue:`18295`)
4950

5051
.. _whatsnew_0220.deprecations:
5152

pandas/core/indexes/base.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3728,6 +3728,10 @@ def insert(self, loc, item):
37283728
-------
37293729
new_index : Index
37303730
"""
3731+
if lib.checknull(item):
3732+
# GH 18295
3733+
item = self._na_value
3734+
37313735
_self = np.asarray(self)
37323736
item = self._coerce_scalar_to_index(item)._values
37333737
idx = np.concatenate((_self[:loc], item, _self[loc:]))

pandas/core/indexes/category.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import numpy as np
2-
from pandas._libs import index as libindex
2+
from pandas._libs import index as libindex, lib
33

44
from pandas import compat
55
from pandas.compat.numpy import function as nv
@@ -688,7 +688,7 @@ def insert(self, loc, item):
688688
689689
"""
690690
code = self.categories.get_indexer([item])
691-
if (code == -1):
691+
if (code == -1) and not lib.checknull(item):
692692
raise TypeError("cannot insert an item into a CategoricalIndex "
693693
"that is not already an existing category")
694694

pandas/core/indexes/datetimes.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1751,6 +1751,9 @@ def insert(self, loc, item):
17511751
-------
17521752
new_index : Index
17531753
"""
1754+
if lib.checknull(item):
1755+
# GH 18295
1756+
item = self._na_value
17541757

17551758
freq = None
17561759

@@ -1767,14 +1770,14 @@ def insert(self, loc, item):
17671770
elif (loc == len(self)) and item - self.freq == self[-1]:
17681771
freq = self.freq
17691772
item = _to_m8(item, tz=self.tz)
1773+
17701774
try:
17711775
new_dates = np.concatenate((self[:loc].asi8, [item.view(np.int64)],
17721776
self[loc:].asi8))
17731777
if self.tz is not None:
17741778
new_dates = conversion.tz_convert(new_dates, 'UTC', self.tz)
17751779
return DatetimeIndex(new_dates, name=self.name, freq=freq,
17761780
tz=self.tz)
1777-
17781781
except (AttributeError, TypeError):
17791782

17801783
# fall back to object index

pandas/core/indexes/interval.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
Index, _ensure_index,
2323
default_pprint, _index_shared_docs)
2424

25-
from pandas._libs import Timestamp, Timedelta
25+
from pandas._libs import lib, Timestamp, Timedelta
2626
from pandas._libs.interval import (
2727
Interval, IntervalMixin, IntervalTree,
2828
intervals_to_interval_bounds)
@@ -985,12 +985,8 @@ def insert(self, loc, item):
985985
'side as the index')
986986
left_insert = item.left
987987
right_insert = item.right
988-
elif is_scalar(item) and isna(item):
988+
elif lib.checknull(item):
989989
# GH 18295
990-
if item is not self.left._na_value:
991-
raise TypeError('cannot insert with incompatible NA value: '
992-
'got {item}, expected {na}'
993-
.format(item=item, na=self.left._na_value))
994990
left_insert = right_insert = item
995991
else:
996992
raise ValueError('can only insert Interval objects and NA into '

pandas/core/indexes/timedeltas.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -849,16 +849,18 @@ def insert(self, loc, item):
849849
-------
850850
new_index : Index
851851
"""
852-
853852
# try to convert if possible
854853
if _is_convertible_to_td(item):
855854
try:
856855
item = Timedelta(item)
857856
except Exception:
858857
pass
858+
elif lib.checknull(item):
859+
# GH 18295
860+
item = self._na_value
859861

860862
freq = None
861-
if isinstance(item, Timedelta) or item is NaT:
863+
if isinstance(item, Timedelta) or (item is self._na_value):
862864

863865
# check freq can be preserved on edge cases
864866
if self.freq is not None:

pandas/tests/indexes/datetimes/test_indexing.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,13 @@ def test_insert(self):
145145
assert result.tz == expected.tz
146146
assert result.freq is None
147147

148+
# GH 18295 (test missing)
149+
expected = DatetimeIndex(
150+
['20170101', pd.NaT, '20170102', '20170103', '20170104'])
151+
for na in (np.nan, pd.NaT, None):
152+
result = date_range('20170101', periods=4).insert(1, na)
153+
tm.assert_index_equal(result, expected)
154+
148155
def test_delete(self):
149156
idx = date_range(start='2000-01-01', periods=5, freq='M', name='idx')
150157

pandas/tests/indexes/period/test_period.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,3 +706,11 @@ def test_join_self(self, how):
706706
index = period_range('1/1/2000', periods=10)
707707
joined = index.join(index, how=how)
708708
assert index is joined
709+
710+
def test_insert(self):
711+
# GH 18295 (test missing)
712+
expected = PeriodIndex(
713+
['2017Q1', pd.NaT, '2017Q2', '2017Q3', '2017Q4'], freq='Q')
714+
for na in (np.nan, pd.NaT, None):
715+
result = period_range('2017Q1', periods=4, freq='Q').insert(1, na)
716+
tm.assert_index_equal(result, expected)

pandas/tests/indexes/test_base.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,12 @@ def test_insert(self):
442442
null_index = Index([])
443443
tm.assert_index_equal(Index(['a']), null_index.insert(0, 'a'))
444444

445+
# GH 18295 (test missing)
446+
expected = Index(['a', np.nan, 'b', 'c'])
447+
for na in (np.nan, pd.NaT, None):
448+
result = Index(list('abc')).insert(1, na)
449+
tm.assert_index_equal(result, expected)
450+
445451
def test_delete(self):
446452
idx = Index(['a', 'b', 'c', 'd'], name='idx')
447453

0 commit comments

Comments
 (0)