diff --git a/doc/source/release.rst b/doc/source/release.rst index 7a271688c318b..893f753d0b7a2 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -318,6 +318,8 @@ pandas 0.12 iterated over when regex=False (:issue:`4115`) - Fixed bug in ``convert_objects(convert_numeric=True)`` where a mixed numeric and object Series/Frame was not converting properly (:issue:`4119`) + - Bug in Series update where the parent frame is not updating its blocks based on + dtype changes (:issue:`4080`) pandas 0.11.0 diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 6be5f456b50e6..3c85b30b2be88 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -1,5 +1,6 @@ # pylint: disable=W0231,E1101 +import weakref import numpy as np import pandas.lib as lib from pandas.core.base import PandasObject @@ -667,11 +668,17 @@ def _get_item_cache(self, item): values = self._data.get(item) res = self._box_item_values(item, values) cache[item] = res + res._cacher = (item,weakref.ref(self)) return res def _box_item_values(self, key, values): raise NotImplementedError + def _maybe_cache_changed(self, item, value): + """ the object has called back to us saying + maybe it has changed """ + self._data.maybe_changed(item, value) + def _clear_item_cache(self): self._item_cache.clear() diff --git a/pandas/core/internals.py b/pandas/core/internals.py index 99af2d7becb39..adbc14864d1a8 100644 --- a/pandas/core/internals.py +++ b/pandas/core/internals.py @@ -1554,6 +1554,12 @@ def _interleave(self, items): return result + def maybe_changed(self, item, value): + """ our obj may have changed dtype """ + current = self.get(item) + if current.dtype != value.dtype: + self.set(item, value) + def xs(self, key, axis=1, copy=True): if axis < 1: raise AssertionError('Can only take xs across axis >= 1, got %d' diff --git a/pandas/core/series.py b/pandas/core/series.py index 06abd1d5b4127..e2e11d2a7abab 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -557,6 +557,12 @@ def __setstate__(self, state): self.index = _handle_legacy_indexes([index])[0] self.name = name + def _maybe_update_cacher(self): + """ see if we need to update our parent cacher """ + cacher = getattr(self,'_cacher',None) + if cacher is not None: + cacher[1]()._maybe_cache_changed(cacher[0],self) + # indexers @property def axes(self): @@ -2157,7 +2163,8 @@ def update(self, other): """ other = other.reindex_like(self) mask = notnull(other) - com._maybe_upcast_putmask(self.values,mask,other,change=self.values) + com._maybe_upcast_putmask(self.values,mask,other,change=self) + self._maybe_update_cacher() #---------------------------------------------------------------------- # Reindexing, sorting diff --git a/pandas/tests/test_frame.py b/pandas/tests/test_frame.py index 915509bac9059..3fe765d051992 100644 --- a/pandas/tests/test_frame.py +++ b/pandas/tests/test_frame.py @@ -8707,6 +8707,16 @@ def test_update_dtypes(self): columns=['A','B','bool1','bool2']) assert_frame_equal(df, expected) + # GH 4080, int conversions + df = DataFrame(dict((c, [1,2,3]) for c in ['a', 'b', 'c'])) + df.set_index(['a', 'b', 'c'], inplace=True) + s = Series([1], index=[(2,2,2)]) + df['val'] = 0 + df['val'].update(s) + expected = DataFrame(dict(a = [1,2,3], b = [1,2,3], c = [1,2,3], val = [0.0,1.0,0.0])) + expected.set_index(['a', 'b', 'c'], inplace=True) + assert_frame_equal(df,expected) + def test_update_nooverwrite(self): df = DataFrame([[1.5, nan, 3.], [1.5, nan, 3.],