Repository: pandas-dev/pandas

Issue:
ix setting with enlargement results in stale caches.
When doing setting with enlargement like:

``` python
import pandas as pd
panel = pd.util.testing.makePanel()
pd.ix[0] # get first item into cache
panel.ix[:, :, 'A+1'] = panel.ix[:, :, 'A'] + 1
assert "A+1" in panel.ix[0].columns # fails due to stale item cache
assert "A+1" in panel.ix[1].columns # succeeds
```

The same thing happens with DataFrames. 

``` python
df = pd.util.testing.makeDataFrame()
df['A'] # cache series
df.ix["Hello Friend"] = df.ix[0]
assert "Hello Friend" in df['A'].index # fails
assert "Hello Friend" in df['B'].index # success
```



Discussion:
yep...need to update the cache ref....easy (thought I had done this....)


Resolving patch:
diff --git a/pandas/core/generic.py b/pandas/core/generic.py
--- a/pandas/core/generic.py
+++ b/pandas/core/generic.py
@@ -825,14 +825,20 @@ def _maybe_cache_changed(self, item, value):
         maybe it has changed """
         self._data.set(item, value)
 
-    def _maybe_update_cacher(self):
-        """ see if we need to update our parent cacher """
+    def _maybe_update_cacher(self, clear=False):
+        """ see if we need to update our parent cacher
+            if clear, then clear our cache """
         cacher = getattr(self,'_cacher',None)
         if cacher is not None:
             cacher[1]()._maybe_cache_changed(cacher[0],self)
+        if clear:
+            self._clear_item_cache()
 
-    def _clear_item_cache(self):
-        self._item_cache.clear()
+    def _clear_item_cache(self, i=None):
+        if i is not None:
+            self._item_cache.pop(i,None)
+        else:
+            self._item_cache.clear()
 
     def _set_item(self, key, value):
         self._data.set(key, value)
diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py
--- a/pandas/core/indexing.py
+++ b/pandas/core/indexing.py
@@ -190,12 +190,14 @@ def _setitem_with_indexer(self, indexer, value):
 
                     new_values = np.concatenate([self.obj.values, [value]])
                     self.obj._data = self.obj._constructor(new_values, index=new_index, name=self.obj.name)
+                    self.obj._maybe_update_cacher(clear=True)
                     return self.obj
 
                 elif self.ndim == 2:
                     index = self.obj._get_axis(0)
                     labels = _safe_append_to_index(index, indexer)
                     self.obj._data = self.obj.reindex_axis(labels,0)._data
+                    self.obj._maybe_update_cacher(clear=True)
                     return getattr(self.obj,self.name).__setitem__(indexer,value)
 
                 # set using setitem (Panel and > dims)
@@ -255,6 +257,7 @@ def setter(item, v):
                 # set the item, possibly having a dtype change
                 s = s.copy()
                 s._data = s._data.setitem(pi,v)
+                s._maybe_update_cacher(clear=True)
                 self.obj[item] = s
 
             def can_do_equal_len():
@@ -327,6 +330,7 @@ def can_do_equal_len():
                 value = self._align_panel(indexer, value)
 
             self.obj._data = self.obj._data.setitem(indexer,value)
+            self.obj._maybe_update_cacher(clear=True)
 
     def _align_series(self, indexer, ser):
         # indexer to assign Series can be tuple or scalar


Test patch:
diff --git a/pandas/tests/test_indexing.py b/pandas/tests/test_indexing.py
--- a/pandas/tests/test_indexing.py
+++ b/pandas/tests/test_indexing.py
@@ -1480,6 +1480,21 @@ def test_series_partial_set(self):
         result = ser.iloc[[1,1,0,0]]
         assert_series_equal(result, expected)
 
+    def test_cache_updating(self):
+        # GH 4939, make sure to update the cache on setitem
+
+        df = tm.makeDataFrame()
+        df['A'] # cache series
+        df.ix["Hello Friend"] = df.ix[0]
+        self.assert_("Hello Friend" in df['A'].index)
+        self.assert_("Hello Friend" in df['B'].index)
+
+        panel = tm.makePanel()
+        panel.ix[0] # get first item into cache
+        panel.ix[:, :, 'A+1'] = panel.ix[:, :, 'A'] + 1
+        self.assert_("A+1" in panel.ix[0].columns)
+        self.assert_("A+1" in panel.ix[1].columns)
+
 if __name__ == '__main__':
     import nose
     nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'],