Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
is_copy -> _parent
  • Loading branch information
jreback committed Sep 2, 2015
commit 5d6e3f572db2dc99ce2b3746d90cfe0c04f9d6e8
8 changes: 4 additions & 4 deletions pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -1825,7 +1825,7 @@ def _ixs(self, i, axis=0):
copy = isinstance(new_values,np.ndarray) and new_values.base is None
result = Series(new_values, index=self.columns,
name=self.index[i], dtype=new_values.dtype)
result._set_is_copy(self, copy=copy)
result._set_parent(self, copy=copy)
return result

# icol
Expand Down Expand Up @@ -1957,7 +1957,7 @@ def _getitem_multilevel(self, key):
if isinstance(result, Series):
result = self._constructor_sliced(result, index=self.index, name=key)

result._set_is_copy(self)
result._set_parent(self)
return result
else:
return self._get_item_cache(key)
Expand Down Expand Up @@ -2300,12 +2300,12 @@ def _set_item(self, key, value):

# if we have a multi-index (which potentially has dropped levels)
# need to raise
if isinstance(self.is_copy().columns, MultiIndex):
if isinstance(self._parent().columns, MultiIndex):
raise

# we have a chained assignment
# assign back to the original
self.is_copy().loc[self.index,key] = value
self._parent().loc[self.index,key] = value

def insert(self, loc, column, value, allow_duplicates=False):
"""
Expand Down
68 changes: 40 additions & 28 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,15 @@ class NDFrame(PandasObject):
axes : list
copy : boolean, default False
"""
_internal_names = ['_data', '_cacher', '_item_cache', '_cache',
'is_copy', '_subtyp', '_index', '_allow_copy_on_write',
_internal_names = ['_data', '_cacher', '_item_cache', '_cache', '_parent',
'_subtyp', '_index', '_parent_copy_on_write',
'_default_kind', '_default_fill_value', '_metadata',
'__array_struct__', '__array_interface__']
_internal_names_set = set(_internal_names)
_accessors = frozenset([])
_metadata = []
_allow_copy_on_write = True
is_copy = None
_parent_copy_on_write = True
_parent = None

def __init__(self, data, axes=None, copy=False, dtype=None,
fastpath=False):
Expand All @@ -101,10 +101,22 @@ def __init__(self, data, axes=None, copy=False, dtype=None,
for i, ax in enumerate(axes):
data = data.reindex_axis(ax, axis=i)

object.__setattr__(self, 'is_copy', None)
object.__setattr__(self, '_parent', None)
object.__setattr__(self, '_data', data)
object.__setattr__(self, '_item_cache', {})

def _get_is_copy(self):
warnings.warn("is_copy is deprecated will be removed in a future release",
FutureWarning)
return None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, but if this really internal property is basically now meaningless, then IMO it would be better to cleanly break code which depends on this (=remove the property without warning) than just change the API contract of this property...


def _set_is_copy(self, v):
warnings.warn("is_copy is deprecated will be removed in a future release",
FutureWarning)
pass

is_copy = property(fget=_get_is_copy, fset=_set_is_copy)

def _validate_dtype(self, dtype):
""" validate the passed dtype """

Expand Down Expand Up @@ -1092,7 +1104,7 @@ def _get_item_cache(self, item):
res._set_as_cached(item, self)

# for a chain
res.is_copy = self.is_copy
res._parent = self._parent
return res

def _set_as_cached(self, item, cacher):
Expand Down Expand Up @@ -1144,7 +1156,7 @@ def _is_view(self):
""" boolean : return if I am a view of another array """
return self._data.is_view

def _maybe_update_cacher(self, clear=False, verify_is_copy=True):
def _maybe_update_cacher(self, clear=False, verify_parent=True):
"""

see if we need to update our parent cacher
Expand All @@ -1154,8 +1166,8 @@ def _maybe_update_cacher(self, clear=False, verify_is_copy=True):
----------
clear : boolean, default False
clear the item cache
verify_is_copy : boolean, default True
provide is_copy checks
verify_parent : boolean, default True
provide parent checks

"""

Expand All @@ -1173,7 +1185,7 @@ def _maybe_update_cacher(self, clear=False, verify_is_copy=True):
except:
pass

if verify_is_copy:
if verify_parent:
self._check_copy_on_write()

if clear:
Expand All @@ -1197,8 +1209,8 @@ def _slice(self, slobj, axis=0, kind=None):
result = result.__finalize__(self)

# mark this as a copy if we are not axis=0
is_copy = axis!=0
result._set_is_copy(self)
parent = axis!=0
result._set_parent(self)
return result

def _set_item(self, key, value):
Expand All @@ -1207,23 +1219,23 @@ def _set_item(self, key, value):
self._data.set(key, value)
self._clear_item_cache()

def _set_is_copy(self, ref=None, copy=True):
def _set_parent(self, ref=None, copy=True):
if not copy:
self.is_copy = None
self._parent = None
else:
if ref is not None:
self.is_copy = weakref.ref(ref)
self._parent = weakref.ref(ref)
else:
self.is_copy = None
self._parent = None

def _check_copy_on_write(self):

# we could have a copy-on-write scenario
if self.is_copy and self._allow_copy_on_write:
if self._parent and self._parent_copy_on_write:

# we have an exception
if isinstance(self.is_copy, Exception):
raise self.is_copy
if isinstance(self._parent, Exception):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still find it "hacky" that parent can be an exception :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hah, I need this for defered evaluation (e.g. see the .dt.hour = 5 example above). I agree its hacky, (i mean I could define a different kind of 'parent' that would work similarly, but this is just easy).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a fan of "parents_or_exception" :-)

raise self._parent

def get_names_for_obj(__really_unused_name__342424__):
"""Returns all named references for self"""
Expand Down Expand Up @@ -1255,14 +1267,14 @@ def get_names_for_obj(__really_unused_name__342424__):

# otherwise we have chained indexing, raise and error
gc.collect(2)
if self.is_copy() is not None:
if self._parent() is not None:
names = get_names_for_obj(self)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jreback If the fragile get_names_for_obj is merely for prohibiting chained indexing, then perhaps we can get rid of it altogether? With this change, chained indexing will always fail (due to copy-on-write), so I don't think we actually need this anymore.

if not len(names):
raise SettingWithCopyError("chained indexing detected, you can fix this ......")

# provide copy-on-write
self._data = self._data.copy()
self.is_copy = None
self._parent = None

def _check_is_chained_assignment_possible(self):
"""
Expand All @@ -1279,7 +1291,7 @@ def _check_is_chained_assignment_possible(self):
if ref is not None:
self._check_copy_on_write()
return True
elif self.is_copy:
elif self._parent:
self._check_copy_on_write()
return False

Expand Down Expand Up @@ -1342,7 +1354,7 @@ def take(self, indices, axis=0, convert=True, is_copy=True):
# maybe set copy if we didn't actually change the index
if is_copy:
if not result._get_axis(axis).equals(self._get_axis(axis)):
result._set_is_copy(self)
result._set_parent(self)

return result

Expand Down Expand Up @@ -1485,7 +1497,7 @@ def xs(self, key, axis=0, level=None, copy=None, drop_level=True):
result = self.iloc[loc]
result.index = new_index

result._set_is_copy(self)
result._set_parent(self)
return result

_xs = xs
Expand Down Expand Up @@ -1608,14 +1620,14 @@ def drop(self, labels, axis=0, level=None, inplace=False, errors='raise'):
else:
return result

def _update_inplace(self, result, verify_is_copy=True):
def _update_inplace(self, result, verify_parent=True):
"""
replace self internals with result.

Parameters
----------
verify_is_copy : boolean, default True
provide is_copy checks
verify_parent : boolean, default True
provide parent checks

"""
# NOTE: This does *not* call __finalize__ and that's an explicit
Expand All @@ -1624,7 +1636,7 @@ def _update_inplace(self, result, verify_is_copy=True):
self._reset_cache()
self._clear_item_cache()
self._data = getattr(result,'_data',result)
self._maybe_update_cacher(verify_is_copy=verify_is_copy)
self._maybe_update_cacher(verify_parent=verify_parent)

def add_prefix(self, prefix):
"""
Expand Down
4 changes: 2 additions & 2 deletions pandas/core/groupby.py
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,7 @@ def _python_apply_general(self, f):

keys, values, mutated = self.grouper.apply(f, self._selected_obj,
self.axis)
self._selected_obj._allow_copy_on_write = True
self._selected_obj._parent_copy_on_write = True

return self._wrap_applied_output(keys, values,
not_indexed_same=mutated)
Expand Down Expand Up @@ -3596,7 +3596,7 @@ def sort_idx(self):
def _set_cow(self, data):
# we may mutate, so don't allow cow
try:
data._allow_copy_on_write=False
data._parent_copy_on_write=False
except AttributeError:
pass
return data
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ def _setitem_with_indexer(self, indexer, value):
labels = index.insert(len(index),key)
self.obj._data = self.obj.reindex_axis(labels, i)._data
self.obj._maybe_update_cacher(clear=True)
self.obj.is_copy=None
self.obj._parent=None

nindexer.append(labels.get_loc(key))

Expand Down
2 changes: 1 addition & 1 deletion pandas/core/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ def f(self, other):
# this makes sure that we are aligned like the input
# we are updating inplace so we want to ignore is_copy
self._update_inplace(result.reindex_like(self,copy=False)._data,
verify_is_copy=False)
verify_parent=False)

return self
return f
Expand Down
3 changes: 1 addition & 2 deletions pandas/core/panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -812,8 +812,7 @@ def xs(self, key, axis=1, copy=None):
axis_number = self._get_axis_number(axis)
new_data = self._data.xs(key, axis=axis_number, copy=False)
result = self._construct_return_type(new_data)
copy = new_data.is_mixed_type
result._set_is_copy(self, copy=copy)
result._set_parent(self)
return result

_xs = xs
Expand Down
2 changes: 1 addition & 1 deletion pandas/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,7 @@ def setitem(key, value):

# we have a chained assignment
# assign back to the original
self.is_copy().loc[self.name,key] = value
self._parent().loc[self.name,key] = value
return

setitem(key, value)
Expand Down
4 changes: 2 additions & 2 deletions pandas/src/reduce.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ def apply_frame_axis0(object frame, object f, object names,
# Need to infer if our low-level mucking is going to cause a segfault
if n > 0:
chunk = frame.iloc[starts[0]:ends[0]]
chunk._allow_copy_on_write = False
chunk._parent_copy_on_write = False
shape_before = chunk.shape
try:
result = f(chunk)
Expand All @@ -509,7 +509,7 @@ def apply_frame_axis0(object frame, object f, object names,
item_cache.clear() # ugh

object.__setattr__(slider.dummy, 'name', names[i])
object.__setattr__(slider.dummy, '_allow_copy_on_write', False)
object.__setattr__(slider.dummy, '_parent_copy_on_write', False)
piece = f(slider.dummy)

# I'm paying the price for index-sharing, ugh
Expand Down
4 changes: 2 additions & 2 deletions pandas/tests/test_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -3534,10 +3534,10 @@ def test_set_value_keeps_names(self):
columns=['one', 'two', 'three', 'four'],
index=idx)
df = df.sortlevel()
self.assertIsNone(df.is_copy)
self.assertIsNone(df._parent)
self.assertEqual(df.index.names, ('Name', 'Number'))
df = df.set_value(('grethe', '4'), 'one', 99.34)
self.assertIsNone(df.is_copy)
self.assertIsNone(df._parent)
self.assertEqual(df.index.names, ('Name', 'Number'))

def test_names(self):
Expand Down
Loading