diff --git a/doc/source/whatsnew/v0.16.2.txt b/doc/source/whatsnew/v0.16.2.txt index eba8f8af7c00e..b5fb8218cb771 100644 --- a/doc/source/whatsnew/v0.16.2.txt +++ b/doc/source/whatsnew/v0.16.2.txt @@ -139,6 +139,7 @@ Bug Fixes - Bug in getting timezone data with ``dateutil`` on various platforms ( :issue:`9059`, :issue:`8639`, :issue:`9663`, :issue:`10121`) - Bug in display datetimes with mixed frequencies uniformly; display 'ms' datetimes to the proper precision. (:issue:`10170`) +- Bug in ``setitem`` where type pormotion is applied to entire block (:issue:`10280`) - Bug in ``Series`` arithmetic methods may incorrectly hold names (:issue:`10068`) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index c23b691e0fe3a..02309e6e4e3b5 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -204,6 +204,15 @@ def _setitem_with_indexer(self, indexer, value): # maybe partial set take_split_path = self.obj._is_mixed_type + + # if there is only one block/type, still have to take split path + # unless the block is one-dimensional or it can hold the value + if not take_split_path and self.obj._data.blocks: + blk, = self.obj._data.blocks + if 1 < blk.ndim: # in case of dict, keys are indices + val = list(value.values()) if isinstance(value,dict) else value + take_split_path = not blk._can_hold_element(val) + if isinstance(indexer, tuple): nindexer = [] for i, idx in enumerate(indexer): diff --git a/pandas/tests/test_indexing.py b/pandas/tests/test_indexing.py index 24741d99691a3..3980fb6938f93 100644 --- a/pandas/tests/test_indexing.py +++ b/pandas/tests/test_indexing.py @@ -2330,6 +2330,31 @@ def test_setitem_dtype_upcast(self): expected = DataFrame([{"a": 1, "c" : 'foo'}, {"a": 3, "b": 2, "c" : np.nan}]) assert_frame_equal(df,expected) + # GH10280 + df = DataFrame(np.arange(6).reshape(2, 3), index=list('ab'), + columns=['foo', 'bar', 'baz']) + + for val in [3.14, 'wxyz']: + left = df.copy() + left.loc['a', 'bar'] = val + right = DataFrame([[0, val, 2], [3, 4, 5]], index=list('ab'), + columns=['foo', 'bar', 'baz']) + + assert_frame_equal(left, right) + self.assertTrue(com.is_integer_dtype(left['foo'])) + self.assertTrue(com.is_integer_dtype(left['baz'])) + + left = DataFrame(np.arange(6).reshape(2, 3) / 10.0, index=list('ab'), + columns=['foo', 'bar', 'baz']) + left.loc['a', 'bar'] = 'wxyz' + + right = DataFrame([[0, 'wxyz', .2], [.3, .4, .5]], index=list('ab'), + columns=['foo', 'bar', 'baz']) + + assert_frame_equal(left, right) + self.assertTrue(com.is_float_dtype(left['foo'])) + self.assertTrue(com.is_float_dtype(left['baz'])) + def test_setitem_iloc(self):