Skip to content

Commit 217bec2

Browse files
committed
BUG: Bug in Series update where the parent frame is not updating its cache based on
changes (GH4080) BUG: Series not updating properly with object dtype (GH33217) BUG: (GH3386) fillna same issue as (GH4080), not updating cacher
1 parent 03158a1 commit 217bec2

File tree

8 files changed

+73
-16
lines changed

8 files changed

+73
-16
lines changed

doc/source/release.rst

+4-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ pandas 0.13
121121

122122
In 0.13.0 there is a major refactor primarily to subclass ``Series`` from ``NDFrame``,
123123
which is the base class currently for ``DataFrame`` and ``Panel``, to unify methods
124-
and behaviors. Series formerly subclassed directly from ``ndarray``.
124+
and behaviors. Series formerly subclassed directly from ``ndarray``. (:issue:`4080`,:issue:`3862`,:issue:`816`)
125125

126126
- Refactor of series.py/frame.py/panel.py to move common code to generic.py
127127
- added _setup_axes to created generic NDFrame structures
@@ -177,6 +177,9 @@ and behaviors. Series formerly subclassed directly from ``ndarray``.
177177
values to propogate to a new object from an existing (e.g. name in ``Series`` will follow
178178
more automatically now)
179179

180+
- Bug in Series update where the parent frame is not updating its cache based on
181+
changes (:issue:`4080`) or types (:issue:`3217`), fillna (:issue:`3386`)
182+
180183
**Experimental Features**
181184

182185
**Bug Fixes**

doc/source/v0.13.0.txt

+3
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,9 @@ and behaviors. Series formerly subclassed directly from ``ndarray``. (:issue:`40
195195
values to propogate to a new object from an existing (e.g. name in ``Series`` will follow
196196
more automatically now)
197197

198+
- Bug in Series update where the parent frame is not updating its cached based on
199+
changes (:issue:`4080`) or types (:issue:`3217`), fillna (:issue:`3386`)
200+
198201
Bug Fixes
199202
~~~~~~~~~
200203

pandas/core/generic.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from pandas import compat
44
import itertools
55
import operator
6+
import weakref
67
import numpy as np
78
import pandas.lib as lib
89
from pandas.core.base import PandasObject
@@ -702,11 +703,23 @@ def _get_item_cache(self, item):
702703
values = self._data.get(item)
703704
res = self._box_item_values(item, values)
704705
cache[item] = res
706+
res._cacher = (item,weakref.ref(self))
705707
return res
706708

707709
def _box_item_values(self, key, values):
708710
raise NotImplementedError
709711

712+
def _maybe_cache_changed(self, item, value):
713+
""" the object has called back to us saying
714+
maybe it has changed """
715+
self._data.set(item, value)
716+
717+
def _maybe_update_cacher(self):
718+
""" see if we need to update our parent cacher """
719+
cacher = getattr(self,'_cacher',None)
720+
if cacher is not None:
721+
cacher[1]()._maybe_cache_changed(cacher[0],self)
722+
710723
def _clear_item_cache(self):
711724
self._item_cache.clear()
712725

@@ -1437,7 +1450,9 @@ def fillna(self, value=None, method=None, axis=0, inplace=False,
14371450
for k, v in value.iteritems():
14381451
if k not in result:
14391452
continue
1440-
result[k].fillna(v, inplace=True)
1453+
obj = result[k]
1454+
obj.fillna(v, inplace=True)
1455+
obj._maybe_update_cacher()
14411456
return result
14421457
else:
14431458
new_data = self._data.fillna(value, inplace=inplace,

pandas/core/indexing.py

+1-5
Original file line numberDiff line numberDiff line change
@@ -1027,7 +1027,7 @@ def _check_bool_indexer(ax, key):
10271027
# this function assumes that com._is_bool_indexer(key) == True
10281028

10291029
result = key
1030-
if _is_series(key) and not key.index.equals(ax):
1030+
if is_series(key) and not key.index.equals(ax):
10311031
result = result.reindex(ax)
10321032
mask = com.isnull(result.values)
10331033
if mask.any():
@@ -1042,10 +1042,6 @@ def _check_bool_indexer(ax, key):
10421042

10431043
return result
10441044

1045-
def _is_series(obj):
1046-
return is_series(obj)
1047-
1048-
10491045
def _maybe_convert_indices(indices, n):
10501046
""" if we have negative indicies, translate to postive here
10511047
if have indicies that are out-of-bounds, raise an IndexError """

pandas/core/internals.py

+4-8
Original file line numberDiff line numberDiff line change
@@ -297,17 +297,13 @@ def fillna(self, value, inplace=False, downcast=None):
297297
else:
298298
return self.copy()
299299

300-
new_values = self.values if inplace else self.values.copy()
301-
mask = com.isnull(new_values)
302-
300+
mask = com.isnull(self.values)
303301
value = self._try_fill(value)
304-
np.putmask(new_values, mask, value)
302+
blocks = self.putmask(mask, value, inplace=inplace)
305303

306-
block = make_block(
307-
new_values, self.items, self.ref_items, fastpath=True)
308304
if downcast:
309-
block = block.downcast()
310-
return block
305+
blocks = [ b.downcast() for b in blocks ]
306+
return blocks
311307

312308
def downcast(self, dtypes=None):
313309
""" try to downcast each item to the dict of dtypes if present """

pandas/core/series.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
_is_index_slice, _maybe_convert_indices)
2828
from pandas.core import generic
2929
from pandas.core.internals import SingleBlockManager
30+
import pandas.core.expressions as expressions
3031
from pandas.tseries.index import DatetimeIndex
3132
from pandas.tseries.period import PeriodIndex, Period
3233
from pandas.tseries.offsets import DateOffset
@@ -2306,7 +2307,9 @@ def update(self, other):
23062307
"""
23072308
other = other.reindex_like(self)
23082309
mask = notnull(other)
2309-
com._maybe_upcast_putmask(self.values, mask, other, change=self.values)
2310+
2311+
self._data = self._data.putmask(mask, other, inplace=True)
2312+
self._maybe_update_cacher()
23102313

23112314
#----------------------------------------------------------------------
23122315
# Reindexing, sorting

pandas/tests/test_frame.py

+10
Original file line numberDiff line numberDiff line change
@@ -6221,6 +6221,16 @@ def test_fillna(self):
62216221
df.x.fillna(method=m,inplace=1)
62226222
df.x.fillna(method=m)
62236223

6224+
# with different dtype (GH3386)
6225+
df = DataFrame([['a','a',np.nan,'a'],['b','b',np.nan,'b'],['c','c',np.nan,'c']])
6226+
6227+
result = df.fillna({ 2: 'foo' })
6228+
expected = DataFrame([['a','a','foo','a'],['b','b','foo','b'],['c','c','foo','c']])
6229+
assert_frame_equal(result, expected)
6230+
6231+
df.fillna({ 2: 'foo' }, inplace=True)
6232+
assert_frame_equal(df, expected)
6233+
62246234
def test_ffill(self):
62256235
self.tsframe['A'][:5] = nan
62266236
self.tsframe['A'][-5:] = nan

pandas/tests/test_series.py

+31
Original file line numberDiff line numberDiff line change
@@ -2550,6 +2550,37 @@ def f(x):
25502550
expected = tsdf.max()
25512551
assert_series_equal(result,expected)
25522552

2553+
def test_underlying_data_conversion(self):
2554+
2555+
# GH 4080
2556+
df = DataFrame(dict((c, [1,2,3]) for c in ['a', 'b', 'c']))
2557+
df.set_index(['a', 'b', 'c'], inplace=True)
2558+
s = Series([1], index=[(2,2,2)])
2559+
df['val'] = 0
2560+
df
2561+
df['val'].update(s)
2562+
2563+
expected = DataFrame(dict(a = [1,2,3], b = [1,2,3], c = [1,2,3], val = [0,1,0]))
2564+
expected.set_index(['a', 'b', 'c'], inplace=True)
2565+
tm.assert_frame_equal(df,expected)
2566+
2567+
# GH 3970
2568+
df = DataFrame({ "aa":range(5), "bb":[2.2]*5})
2569+
df["cc"] = 0.0
2570+
ck = [True]*len(df)
2571+
df["bb"].iloc[0] = .13
2572+
df_tmp = df.iloc[ck]
2573+
df["bb"].iloc[0] = .15
2574+
self.assert_(df['bb'].iloc[0] == 0.15)
2575+
2576+
# GH 3217
2577+
df = DataFrame(dict(a = [1,3], b = [np.nan, 2]))
2578+
df['c'] = np.nan
2579+
df['c'].update(pd.Series(['foo'],index=[0]))
2580+
2581+
expected = DataFrame(dict(a = [1,3], b = [np.nan, 2], c = ['foo',np.nan]))
2582+
tm.assert_frame_equal(df,expected)
2583+
25532584
def test_operators_corner(self):
25542585
series = self.ts
25552586

0 commit comments

Comments
 (0)