Skip to content

Commit dea03f1

Browse files
committed
BUG: bug in partial setting of with a DatetimeIndex (GH9478)
BUG: bug in taking a view with an Index (and addtl testing)
1 parent c37f8df commit dea03f1

File tree

6 files changed

+69
-7
lines changed

6 files changed

+69
-7
lines changed

doc/source/whatsnew/v0.16.0.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ Bug Fixes
186186
- Bug in Panel indexing with an object-like (:issue:`9140`)
187187
- Bug in the returned ``Series.dt.components`` index was reset to the default index (:issue:`9247`)
188188
- Bug in ``Categorical.__getitem__/__setitem__`` with listlike input getting incorrect results from indexer coercion (:issue:`9469`)
189-
189+
- Bug in partial setting with a DatetimeIndex (:issue:`9478`)
190190
- Fixed bug in ``to_sql`` when mapping a ``Timestamp`` object column (datetime
191191
column with timezone info) to the according sqlalchemy type (:issue:`9085`).
192192
- Fixed bug in ``to_sql`` ``dtype`` argument not accepting an instantiated

pandas/core/common.py

+21
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,27 @@ def conv(r, dtype):
10111011
return [conv(r, dtype) for r, dtype in zip(result, dtypes)]
10121012

10131013

1014+
def _infer_fill_value(val):
1015+
"""
1016+
infer the fill value for the nan/NaT from the provided scalar/ndarray/list-like
1017+
if we are a NaT, return the correct dtyped element to provide proper block construction
1018+
1019+
"""
1020+
1021+
if not is_list_like(val):
1022+
val = [val]
1023+
val = np.array(val,copy=False)
1024+
if is_datetimelike(val):
1025+
return np.array('NaT',dtype=val.dtype)
1026+
elif is_object_dtype(val.dtype):
1027+
dtype = lib.infer_dtype(_ensure_object(val))
1028+
if dtype in ['datetime','datetime64']:
1029+
return np.array('NaT',dtype=_NS_DTYPE)
1030+
elif dtype in ['timedelta','timedelta64']:
1031+
return np.array('NaT',dtype=_TD_DTYPE)
1032+
return np.nan
1033+
1034+
10141035
def _infer_dtype_from_scalar(val):
10151036
""" interpret the dtype from a scalar, upcast floats and ints
10161037
return the new value and the dtype """

pandas/core/index.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,10 @@ def _get_attributes_dict(self):
345345
return dict([ (k,getattr(self,k,None)) for k in self._attributes])
346346

347347
def view(self, cls=None):
348-
if cls is not None and not issubclass(cls, Index):
348+
349+
# we need to see if we are subclassing an
350+
# index type here
351+
if cls is not None and not hasattr(cls,'_typ'):
349352
result = self._data.view(cls)
350353
else:
351354
result = self._shallow_copy()

pandas/core/indexing.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from pandas.core.common import (_is_bool_indexer, is_integer_dtype,
99
_asarray_tuplesafe, is_list_like, isnull,
1010
ABCSeries, ABCDataFrame, ABCPanel, is_float,
11-
_values_from_object)
11+
_values_from_object, _infer_fill_value)
1212
import pandas.lib as lib
1313

1414
import numpy as np
@@ -238,7 +238,9 @@ def _setitem_with_indexer(self, indexer, value):
238238
self.obj[key] = value
239239
return self.obj
240240

241-
self.obj[key] = np.nan
241+
242+
# add a new item with the dtype setup
243+
self.obj[key] = _infer_fill_value(value)
242244

243245
new_indexer = _convert_from_missing_indexer_tuple(
244246
indexer, self.obj.axes)

pandas/tests/test_index.py

+26-3
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,9 @@ def test_view(self):
297297
i_view = i.view()
298298
self.assertEqual(i_view.name, 'Foo')
299299

300+
# with arguments
301+
self.assertRaises(TypeError, lambda : i.view('i8'))
302+
300303
def test_legacy_pickle_identity(self):
301304

302305
# GH 8431
@@ -1469,6 +1472,12 @@ def test_view(self):
14691472
i_view = i.view()
14701473
self.assertEqual(i_view.name, 'Foo')
14711474

1475+
i_view = i.view('i8')
1476+
tm.assert_index_equal(i, Int64Index(i_view))
1477+
1478+
i_view = i.view(Int64Index)
1479+
tm.assert_index_equal(i, Int64Index(i_view))
1480+
14721481
def test_coerce_list(self):
14731482
# coerce things
14741483
arr = Index([1, 2, 3, 4])
@@ -1856,7 +1865,21 @@ def test_slice_keep_name(self):
18561865
idx = Int64Index([1, 2], name='asdf')
18571866
self.assertEqual(idx.name, idx[1:].name)
18581867

1859-
class TestDatetimeIndex(Base, tm.TestCase):
1868+
class DatetimeLike(Base):
1869+
1870+
def test_view(self):
1871+
1872+
i = self.create_index()
1873+
1874+
i_view = i.view('i8')
1875+
result = self._holder(i)
1876+
tm.assert_index_equal(result, i)
1877+
1878+
i_view = i.view(self._holder)
1879+
result = self._holder(i)
1880+
tm.assert_index_equal(result, i)
1881+
1882+
class TestDatetimeIndex(DatetimeLike, tm.TestCase):
18601883
_holder = DatetimeIndex
18611884
_multiprocess_can_split_ = True
18621885

@@ -1926,7 +1949,7 @@ def test_time_overflow_for_32bit_machines(self):
19261949
self.assertEqual(len(idx2), periods)
19271950

19281951

1929-
class TestPeriodIndex(Base, tm.TestCase):
1952+
class TestPeriodIndex(DatetimeLike, tm.TestCase):
19301953
_holder = PeriodIndex
19311954
_multiprocess_can_split_ = True
19321955

@@ -1936,7 +1959,7 @@ def create_index(self):
19361959
def test_pickle_compat_construction(self):
19371960
pass
19381961

1939-
class TestTimedeltaIndex(Base, tm.TestCase):
1962+
class TestTimedeltaIndex(DatetimeLike, tm.TestCase):
19401963
_holder = TimedeltaIndex
19411964
_multiprocess_can_split_ = True
19421965

pandas/tests/test_indexing.py

+13
Original file line numberDiff line numberDiff line change
@@ -3158,6 +3158,19 @@ def f():
31583158
df.loc[3] = [6,7]
31593159
assert_frame_equal(df,DataFrame([[6,7]],index=[3],columns=['A','B'],dtype='float64'))
31603160

3161+
def test_partial_setting_with_datetimelike_dtype(self):
3162+
3163+
# GH9478
3164+
# a datetimeindex alignment issue with partial setting
3165+
df = pd.DataFrame(np.arange(6.).reshape(3,2), columns=list('AB'),
3166+
index=pd.date_range('1/1/2000', periods=3, freq='1H'))
3167+
expected = df.copy()
3168+
expected['C'] = [expected.index[0]] + [pd.NaT,pd.NaT]
3169+
3170+
mask = df.A < 1
3171+
df.loc[mask, 'C'] = df.loc[mask].index
3172+
assert_frame_equal(df, expected)
3173+
31613174
def test_series_partial_set(self):
31623175
# partial set with new index
31633176
# Regression from GH4825

0 commit comments

Comments
 (0)