Skip to content

Commit f4ce16c

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 f6324c4 commit f4ce16c

File tree

8 files changed

+75
-18
lines changed

8 files changed

+75
-18
lines changed

doc/source/release.rst

+4-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ pandas 0.13
4848

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

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

107+
- Bug in Series update where the parent frame is not updating its cache based on
108+
changes (:issue:`4080`) or types (:issue:`3217`), fillna (:issue:`3386`)
109+
107110
**Experimental Features**
108111

109112
**Bug Fixes**

doc/source/v0.13.0.txt

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

82+
- Bug in Series update where the parent frame is not updating its cached based on
83+
changes (:issue:`4080`) or types (:issue:`3217`), fillna (:issue:`3386`)
84+
8285
Bug Fixes
8386
~~~~~~~~~
8487

pandas/core/generic.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# pylint: disable=W0231,E1101
22
import itertools
33
import operator
4+
import weakref
45
import numpy as np
56
import pandas.lib as lib
67
from pandas.core.base import PandasObject
@@ -694,11 +695,23 @@ def _get_item_cache(self, item):
694695
values = self._data.get(item)
695696
res = self._box_item_values(item, values)
696697
cache[item] = res
698+
res._cacher = (item,weakref.ref(self))
697699
return res
698700

699701
def _box_item_values(self, key, values):
700702
raise NotImplementedError
701703

704+
def _maybe_cache_changed(self, item, value):
705+
""" the object has called back to us saying
706+
maybe it has changed """
707+
self._data.set(item, value)
708+
709+
def _maybe_update_cacher(self):
710+
""" see if we need to update our parent cacher """
711+
cacher = getattr(self,'_cacher',None)
712+
if cacher is not None:
713+
cacher[1]()._maybe_cache_changed(cacher[0],self)
714+
702715
def _clear_item_cache(self):
703716
self._item_cache.clear()
704717

@@ -1436,7 +1449,9 @@ def fillna(self, value=None, method=None, axis=0, inplace=False,
14361449
for k, v in value.iteritems():
14371450
if k not in result:
14381451
continue
1439-
result[k].fillna(v, inplace=True)
1452+
obj = result[k]
1453+
obj.fillna(v, inplace=True)
1454+
obj._maybe_update_cacher()
14401455
return result
14411456
else:
14421457
new_data = self._data.fillna(value, inplace=inplace,

pandas/core/indexing.py

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

999999
result = key
1000-
if _is_series(key) and not key.index.equals(ax):
1000+
if is_series(key) and not key.index.equals(ax):
10011001
result = result.reindex(ax)
10021002
mask = com.isnull(result.values)
10031003
if mask.any():
@@ -1012,10 +1012,6 @@ def _check_bool_indexer(ax, key):
10121012

10131013
return result
10141014

1015-
def _is_series(obj):
1016-
return is_series(obj)
1017-
1018-
10191015
def _maybe_convert_indices(indices, n):
10201016
""" if we have negative indicies, translate to postive here
10211017
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
@@ -295,17 +295,13 @@ def fillna(self, value, inplace=False, downcast=None):
295295
else:
296296
return self.copy()
297297

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

304-
block = make_block(
305-
new_values, self.items, self.ref_items, fastpath=True)
306302
if downcast:
307-
block = block.downcast()
308-
return block
303+
blocks = [ b.downcast() for b in blocks ]
304+
return blocks
309305

310306
def downcast(self, dtypes=None):
311307
""" 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.util import py3compat
@@ -2213,7 +2214,9 @@ def update(self, other):
22132214
"""
22142215
other = other.reindex_like(self)
22152216
mask = notnull(other)
2216-
com._maybe_upcast_putmask(self.values, mask, other, change=self.values)
2217+
2218+
self._data = self._data.putmask(mask, other, inplace=True)
2219+
self._maybe_update_cacher()
22172220

22182221
#----------------------------------------------------------------------
22192222
# Reindexing, sorting

pandas/tests/test_frame.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -6185,6 +6185,16 @@ def test_fillna(self):
61856185
df.x.fillna(method=m,inplace=1)
61866186
df.x.fillna(method=m)
61876187

6188+
# with different dtype (GH3386)
6189+
df = DataFrame([['a','a',np.nan,'a'],['b','b',np.nan,'b'],['c','c',np.nan,'c']])
6190+
6191+
result = df.fillna({ 2: 'foo' })
6192+
expected = DataFrame([['a','a','foo','a'],['b','b','foo','b'],['c','c','foo','c']])
6193+
assert_frame_equal(result, expected)
6194+
6195+
df.fillna({ 2: 'foo' }, inplace=True)
6196+
assert_frame_equal(df, expected)
6197+
61886198
def test_ffill(self):
61896199
self.tsframe['A'][:5] = nan
61906200
self.tsframe['A'][-5:] = nan
@@ -10683,12 +10693,12 @@ def test_isin_dict(self):
1068310693

1068410694
# without using iloc
1068510695
result = df.isin(d)
10686-
assert_frame_equal(result, expected)
10696+
assert_frame_equal(result, expected)
1068710697

1068810698
# using iloc
1068910699
result = df.isin(d, iloc=True)
1069010700
expected.iloc[0, 0] = True
10691-
assert_frame_equal(result, expected)
10701+
assert_frame_equal(result, expected)
1069210702

1069310703

1069410704
if __name__ == '__main__':

pandas/tests/test_series.py

+31
Original file line numberDiff line numberDiff line change
@@ -2355,6 +2355,37 @@ def f(x):
23552355
expected = tsdf.max()
23562356
assert_series_equal(result,expected)
23572357

2358+
def test_underlying_data_conversion(self):
2359+
2360+
# GH 4080
2361+
df = DataFrame(dict((c, [1,2,3]) for c in ['a', 'b', 'c']))
2362+
df.set_index(['a', 'b', 'c'], inplace=True)
2363+
s = Series([1], index=[(2,2,2)])
2364+
df['val'] = 0
2365+
df
2366+
df['val'].update(s)
2367+
2368+
expected = DataFrame(dict(a = [1,2,3], b = [1,2,3], c = [1,2,3], val = [0,1,0]))
2369+
expected.set_index(['a', 'b', 'c'], inplace=True)
2370+
tm.assert_frame_equal(df,expected)
2371+
2372+
# GH 3970
2373+
df = DataFrame({ "aa":range(5), "bb":[2.2]*5})
2374+
df["cc"] = 0.0
2375+
ck = [True]*len(df)
2376+
df["bb"].iloc[0] = .13
2377+
df_tmp = df.iloc[ck]
2378+
df["bb"].iloc[0] = .15
2379+
self.assert_(df['bb'].iloc[0] == 0.15)
2380+
2381+
# GH 3217
2382+
df = DataFrame(dict(a = [1,3], b = [np.nan, 2]))
2383+
df['c'] = np.nan
2384+
df['c'].update(pd.Series(['foo'],index=[0]))
2385+
2386+
expected = DataFrame(dict(a = [1,3], b = [np.nan, 2], c = ['foo',np.nan]))
2387+
tm.assert_frame_equal(df,expected)
2388+
23582389
def test_operators_corner(self):
23592390
series = self.ts
23602391

0 commit comments

Comments
 (0)