diff --git a/doc/source/release.rst b/doc/source/release.rst index 0b25d0f6aa61a..95f7f7e71b89d 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -73,6 +73,7 @@ Bug Fixes ~~~~~~~~~ - Bug in Series replace with timestamp dict (:issue:`5797`) - read_csv/read_table now respects the `prefix` kwarg (:issue:`5732`). + - Bug with insert of strings into DatetimeIndex (:issue:`5818`, :issue:`5819`) pandas 0.13.0 ------------- diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index bfddd2e78c322..9a9e3caa96c5b 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -1455,7 +1455,6 @@ def _safe_append_to_index(index, key): # raise here as this is basically an unsafe operation and we want # it to be obvious that you are doing something wrong - raise ValueError("unsafe appending to index of type {0} with a key " "{1}".format(index.__class__.__name__, key)) diff --git a/pandas/sparse/tests/test_sparse.py b/pandas/sparse/tests/test_sparse.py index bd05a7093fd7c..70f1e50f475bb 100644 --- a/pandas/sparse/tests/test_sparse.py +++ b/pandas/sparse/tests/test_sparse.py @@ -1084,8 +1084,10 @@ def test_icol(self): def test_set_value(self): - # this is invalid because it is not a valid type for this index - self.assertRaises(ValueError, self.frame.set_value, 'foobar', 'B', 1.5) + # ok as the index gets conver to object + frame = self.frame.copy() + res = frame.set_value('foobar', 'B', 1.5) + self.assert_(res.index.dtype == 'object') res = self.frame res.index = res.index.astype(object) diff --git a/pandas/tests/test_frame.py b/pandas/tests/test_frame.py index a1ef94d8400da..548f49e23a035 100644 --- a/pandas/tests/test_frame.py +++ b/pandas/tests/test_frame.py @@ -10897,6 +10897,19 @@ def test_reset_index_multiindex_col(self): ['a', 'mean', 'median', 'mean']]) assert_frame_equal(rs, xp) + def test_reset_index_with_datetimeindex_cols(self): + # GH5818 + # + df = pd.DataFrame([[1, 2], [3, 4]], + columns=pd.date_range('1/1/2013', '1/2/2013'), + index=['A', 'B']) + + result = df.reset_index() + expected = pd.DataFrame([['A', 1, 2], ['B', 3, 4]], + columns=['index', datetime(2013, 1, 1), + datetime(2013, 1, 2)]) + assert_frame_equal(result, expected) + #---------------------------------------------------------------------- # Tests to cope with refactored internals def test_as_matrix_numeric_cols(self): diff --git a/pandas/tests/test_indexing.py b/pandas/tests/test_indexing.py index fe3aac0e9eeaa..ee57902bdeb5f 100644 --- a/pandas/tests/test_indexing.py +++ b/pandas/tests/test_indexing.py @@ -1665,15 +1665,13 @@ def test_partial_set_invalid(self): df = tm.makeTimeDataFrame() + # don't allow not string inserts def f(): df.loc[100.0, :] = df.ix[0] self.assertRaises(ValueError, f) def f(): df.loc[100,:] = df.ix[0] self.assertRaises(ValueError, f) - def f(): - df.loc['a',:] = df.ix[0] - self.assertRaises(ValueError, f) def f(): df.ix[100.0, :] = df.ix[0] @@ -1682,6 +1680,9 @@ def f(): df.ix[100,:] = df.ix[0] self.assertRaises(ValueError, f) + # allow object conversion here + df.loc['a',:] = df.ix[0] + def test_partial_set_empty(self): # GH5226 diff --git a/pandas/tseries/index.py b/pandas/tseries/index.py index 6779e1a61c081..8cf11dd921abf 100644 --- a/pandas/tseries/index.py +++ b/pandas/tseries/index.py @@ -1533,6 +1533,8 @@ def insert(self, loc, item): ---------- loc : int item : object + if not either a Python datetime or a numpy integer-like, returned + Index dtype will be object rather than datetime. Returns ------- @@ -1540,11 +1542,17 @@ def insert(self, loc, item): """ if isinstance(item, datetime): item = _to_m8(item, tz=self.tz) - - new_index = np.concatenate((self[:loc].asi8, + try: + new_index = np.concatenate((self[:loc].asi8, [item.view(np.int64)], self[loc:].asi8)) - return DatetimeIndex(new_index, freq='infer') + return DatetimeIndex(new_index, freq='infer') + except (AttributeError, TypeError): + + # fall back to object index + if isinstance(item,compat.string_types): + return self.asobject.insert(loc, item) + raise TypeError("cannot insert DatetimeIndex with incompatible label") def delete(self, loc): """ @@ -1585,7 +1593,7 @@ def tz_convert(self, tz): def tz_localize(self, tz, infer_dst=False): """ Localize tz-naive DatetimeIndex to given time zone (using pytz) - + Parameters ---------- tz : string or pytz.timezone diff --git a/pandas/tseries/tests/test_timeseries.py b/pandas/tseries/tests/test_timeseries.py index f4dcdb7a44a3e..4dfe05e38458a 100644 --- a/pandas/tseries/tests/test_timeseries.py +++ b/pandas/tseries/tests/test_timeseries.py @@ -2099,6 +2099,13 @@ def test_insert(self): '2000-01-02']) self.assert_(result.equals(exp)) + # insertion of non-datetime should coerce to object index + result = idx.insert(1, 'inserted') + expected = Index([datetime(2000, 1, 4), 'inserted', datetime(2000, 1, 1), + datetime(2000, 1, 2)]) + self.assert_(not isinstance(result, DatetimeIndex)) + tm.assert_index_equal(result, expected) + idx = date_range('1/1/2000', periods=3, freq='M') result = idx.insert(3, datetime(2000, 4, 30)) self.assert_(result.freqstr == 'M')