From 65d443fb88ad157f0b6d65bddb06e387aafa81b4 Mon Sep 17 00:00:00 2001 From: phofl Date: Sat, 20 Nov 2021 23:47:32 +0100 Subject: [PATCH 1/3] BUG: Index with object dtype and negative loc for insert adding none and replacing existing value --- doc/source/whatsnew/v1.4.0.rst | 1 + pandas/core/arrays/interval.py | 2 +- pandas/core/indexes/base.py | 3 ++- pandas/tests/indexes/base_class/test_reshape.py | 12 ++++++++++++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index b962add29db33..c49767e80f535 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -546,6 +546,7 @@ Datetimelike - Bug in constructing a :class:`Series` from datetime-like strings with mixed timezones incorrectly partially-inferring datetime values (:issue:`40111`) - Bug in addition with a :class:`Tick` object and a ``np.timedelta64`` object incorrectly raising instead of returning :class:`Timedelta` (:issue:`44474`) - Bug in adding a ``np.timedelta64`` object to a :class:`BusinessDay` or :class:`CustomBusinessDay` object incorrectly raising (:issue:`44532`) +- Bug in :meth:`Index.insert` for inserting ``np.datetime64``, ``np.timedelta64`` or ``tuple`` into :class:`Index` with ``dtype='object'`` with negative loc addine ``None`` and replacing existing value (:issue:`44509`) - Timedelta diff --git a/pandas/core/arrays/interval.py b/pandas/core/arrays/interval.py index 01bf5ec0633b5..03756aad5be71 100644 --- a/pandas/core/arrays/interval.py +++ b/pandas/core/arrays/interval.py @@ -1496,7 +1496,7 @@ def _putmask(self, mask: npt.NDArray[np.bool_], value) -> None: def insert(self: IntervalArrayT, loc: int, item: Interval) -> IntervalArrayT: """ Return a new IntervalArray inserting new item at location. Follows - Python list.append semantics for negative values. Only Interval + Python np.insert semantics for negative values. Only Interval objects and NA can be inserted into an IntervalIndex Parameters diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index a49c303e735ab..64cf88434e37e 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -6433,7 +6433,7 @@ def insert(self, loc: int, item) -> Index: """ Make new Index inserting new item at location. - Follows Python list.append semantics for negative values. + Follows Python np.insert semantics for negative values. Parameters ---------- @@ -6476,6 +6476,7 @@ def insert(self, loc: int, item) -> Index: else: new_values = np.insert(arr, loc, None) + loc = loc if loc >= 0 else loc - 1 new_values[loc] = item # Use self._constructor instead of Index to retain NumericIndex GH#43921 diff --git a/pandas/tests/indexes/base_class/test_reshape.py b/pandas/tests/indexes/base_class/test_reshape.py index acb6936f70d0f..b5c80b56badf3 100644 --- a/pandas/tests/indexes/base_class/test_reshape.py +++ b/pandas/tests/indexes/base_class/test_reshape.py @@ -1,6 +1,7 @@ """ Tests for ndarray-like method on the base Index class """ +import numpy as np import pytest from pandas import Index @@ -42,6 +43,17 @@ def test_insert_missing(self, nulls_fixture): result = Index(list("abc")).insert(1, nulls_fixture) tm.assert_index_equal(result, expected) + @pytest.mark.parametrize( + "val", [(1, 2), np.datetime64("2019-12-31"), np.timedelta64(1, "D")] + ) + @pytest.mark.parametrize("loc", [-1, 2]) + def test_insert_datetime_into_object(self, loc, val): + # GH#44509 + idx = Index(["1", "2", "3"]) + result = idx.insert(loc, val) + expected = Index(["1", "2", val, "3"]) + tm.assert_index_equal(result, expected) + @pytest.mark.parametrize( "pos,expected", [ From aec2c365487478f8acc90881c05f06a44b98e2e1 Mon Sep 17 00:00:00 2001 From: phofl Date: Sat, 20 Nov 2021 23:49:21 +0100 Subject: [PATCH 2/3] Replace np with numpy in docstring --- pandas/core/arrays/interval.py | 2 +- pandas/core/indexes/base.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/core/arrays/interval.py b/pandas/core/arrays/interval.py index 03756aad5be71..96a76aa930f86 100644 --- a/pandas/core/arrays/interval.py +++ b/pandas/core/arrays/interval.py @@ -1496,7 +1496,7 @@ def _putmask(self, mask: npt.NDArray[np.bool_], value) -> None: def insert(self: IntervalArrayT, loc: int, item: Interval) -> IntervalArrayT: """ Return a new IntervalArray inserting new item at location. Follows - Python np.insert semantics for negative values. Only Interval + Python numpy.insert semantics for negative values. Only Interval objects and NA can be inserted into an IntervalIndex Parameters diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 64cf88434e37e..220b43f323a5f 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -6433,7 +6433,7 @@ def insert(self, loc: int, item) -> Index: """ Make new Index inserting new item at location. - Follows Python np.insert semantics for negative values. + Follows Python numpy.insert semantics for negative values. Parameters ---------- From c1e6b1a0d26f78c11139fec2d53b4eab84a3d23d Mon Sep 17 00:00:00 2001 From: phofl Date: Sun, 21 Nov 2021 00:00:45 +0100 Subject: [PATCH 3/3] Add type check --- pandas/tests/indexes/base_class/test_reshape.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pandas/tests/indexes/base_class/test_reshape.py b/pandas/tests/indexes/base_class/test_reshape.py index b5c80b56badf3..547d62669943c 100644 --- a/pandas/tests/indexes/base_class/test_reshape.py +++ b/pandas/tests/indexes/base_class/test_reshape.py @@ -53,6 +53,7 @@ def test_insert_datetime_into_object(self, loc, val): result = idx.insert(loc, val) expected = Index(["1", "2", val, "3"]) tm.assert_index_equal(result, expected) + assert type(expected[2]) is type(val) @pytest.mark.parametrize( "pos,expected",