Skip to content

API/CLN: make rename and copy consistent across NDFrame #4627

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 21, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,7 @@ Reindexing / Selection / Label manipulation
Panel.reindex
Panel.reindex_axis
Panel.reindex_like
Panel.rename
Panel.select
Panel.take
Panel.truncate
Expand Down
10 changes: 4 additions & 6 deletions doc/source/release.rst
Original file line number Diff line number Diff line change
Expand Up @@ -172,21 +172,19 @@ See :ref:`Internal Refactoring<whatsnew_0130.refactoring>`

- added ``ftypes`` method to Series/DataFame, similar to ``dtypes``, but indicates
if the underlying is sparse/dense (as well as the dtype)

- All ``NDFrame`` objects now have a ``_prop_attributes``, which can be used to indcated various
values to propogate to a new object from an existing (e.g. name in ``Series`` will follow
more automatically now)

- Internal type checking is now done via a suite of generated classes, allowing ``isinstance(value, klass)``
without having to directly import the klass, courtesy of @jtratner

- Bug in Series update where the parent frame is not updating its cache based on
changes (:issue:`4080`) or types (:issue:`3217`), fillna (:issue:`3386`)

- Indexing with dtype conversions fixed (:issue:`4463`, :issue:`4204`)

- Refactor Series.reindex to core/generic.py (:issue:`4604`, :issue:`4618`), allow ``method=`` in reindexing
- Refactor ``Series.reindex`` to core/generic.py (:issue:`4604`, :issue:`4618`), allow ``method=`` in reindexing
on a Series to work
- ``Series.copy`` no longer accepts the ``order`` parameter and is now consistent with ``NDFrame`` copy
- Refactor ``rename`` methods to core/generic.py; fixes ``Series.rename`` for (:issue`4605`), and adds ``rename``
with the same signature for ``Panel``

**Experimental Features**

Expand Down
10 changes: 4 additions & 6 deletions doc/source/v0.13.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -226,21 +226,19 @@ and behaviors. Series formerly subclassed directly from ``ndarray``. (:issue:`40

- added ``ftypes`` method to Series/DataFame, similar to ``dtypes``, but indicates
if the underlying is sparse/dense (as well as the dtype)

- All ``NDFrame`` objects now have a ``_prop_attributes``, which can be used to indcated various
values to propogate to a new object from an existing (e.g. name in ``Series`` will follow
more automatically now)

- Internal type checking is now done via a suite of generated classes, allowing ``isinstance(value, klass)``
without having to directly import the klass, courtesy of @jtratner

- Bug in Series update where the parent frame is not updating its cache based on
changes (:issue:`4080`) or types (:issue:`3217`), fillna (:issue:`3386`)

- Indexing with dtype conversions fixed (:issue:`4463`, :issue:`4204`)

- Refactor Series.reindex to core/generic.py (:issue:`4604`, :issue:`4618`), allow ``method=`` in reindexing
- Refactor ``Series.reindex`` to core/generic.py (:issue:`4604`, :issue:`4618`), allow ``method=`` in reindexing
on a Series to work
- ``Series.copy`` no longer accepts the ``order`` parameter and is now consistent with ``NDFrame`` copy
- Refactor ``rename`` methods to core/generic.py; fixes ``Series.rename`` for (:issue`4605`), and adds ``rename``
with the same signature for ``Panel``

Bug Fixes
~~~~~~~~~
Expand Down
58 changes: 0 additions & 58 deletions pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -2897,64 +2897,6 @@ def reorder_levels(self, order, axis=0):
result.columns = result.columns.reorder_levels(order)
return result

#----------------------------------------------------------------------
# Rename

def rename(self, index=None, columns=None, copy=True, inplace=False):
"""
Alter index and / or columns using input function or
functions. Function / dict values must be unique (1-to-1). Labels not
contained in a dict / Series will be left as-is.

Parameters
----------
index : dict-like or function, optional
Transformation to apply to index values
columns : dict-like or function, optional
Transformation to apply to column values
copy : boolean, default True
Also copy underlying data
inplace : boolean, default False
Whether to return a new DataFrame. If True then value of copy is
ignored.

See also
--------
Series.rename

Returns
-------
renamed : DataFrame (new object)
"""
from pandas.core.series import _get_rename_function

if index is None and columns is None:
raise Exception('must pass either index or columns')

index_f = _get_rename_function(index)
columns_f = _get_rename_function(columns)

self._consolidate_inplace()

result = self if inplace else self.copy(deep=copy)

if index is not None:
result._rename_index_inplace(index_f)

if columns is not None:
result._rename_columns_inplace(columns_f)

if not inplace:
return result

def _rename_index_inplace(self, mapper):
self._data = self._data.rename_axis(mapper, axis=1)
self._clear_item_cache()

def _rename_columns_inplace(self, mapper):
self._data = self._data.rename_items(mapper, copydata=False)
self._clear_item_cache()

#----------------------------------------------------------------------
# Arithmetic / combination related

Expand Down
97 changes: 80 additions & 17 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@
from pandas.core.internals import BlockManager
import pandas.core.common as com
from pandas import compat
from pandas.compat import map, zip
from pandas.compat import map, zip, lrange
from pandas.core.common import (isnull, notnull, is_list_like,
_values_from_object,
_infer_dtype_from_scalar, _maybe_promote)
_infer_dtype_from_scalar, _maybe_promote,
ABCSeries)

class NDFrame(PandasObject):

Expand Down Expand Up @@ -382,7 +383,77 @@ def swaplevel(self, i, j, axis=0):
result._data.set_axis(axis, labels.swaplevel(i, j))
return result

def rename_axis(self, mapper, axis=0, copy=True):
#----------------------------------------------------------------------
# Rename

def rename(self, *args, **kwargs):
"""
Alter axes input function or
functions. Function / dict values must be unique (1-to-1). Labels not
contained in a dict / Series will be left as-is.

Parameters
----------
axis keywords for this object
(e.g. index for Series,
index,columns for DataFrame,
items,major_axis,minor_axis for Panel)
: dict-like or function, optional
Transformation to apply to that axis values

copy : boolean, default True
Also copy underlying data
inplace : boolean, default False
Whether to return a new PandasObject. If True then value of copy is
ignored.

Returns
-------
renamed : PandasObject (new object)
"""

axes, kwargs = self._construct_axes_from_arguments(args, kwargs)
copy = kwargs.get('copy', True)
inplace = kwargs.get('inplace', False)

if (com._count_not_none(*axes.values()) == 0):
raise Exception('must pass an index to rename')

# renamer function if passed a dict
def _get_rename_function(mapper):
if isinstance(mapper, (dict, ABCSeries)):
def f(x):
if x in mapper:
return mapper[x]
else:
return x
else:
f = mapper

return f


self._consolidate_inplace()
result = self if inplace else self.copy(deep=copy)

# start in the axis order to eliminate too many copies
for axis in lrange(self._AXIS_LEN):
v = axes.get(self._AXIS_NAMES[axis])
if v is None: continue
f = _get_rename_function(v)

baxis = self._get_block_manager_axis(axis)
result._data = result._data.rename(f, axis=baxis, copy=copy)
result._clear_item_cache()

if inplace:
self._data = result._data
self._clear_item_cache()

else:
return result._propogate_attributes(self)

def rename_axis(self, mapper, axis=0, copy=True, inplace=False):
"""
Alter index and / or columns using input function or functions.
Function / dict values must be unique (1-to-1). Labels not contained in
Expand All @@ -394,24 +465,16 @@ def rename_axis(self, mapper, axis=0, copy=True):
axis : int, default 0
copy : boolean, default True
Also copy underlying data
inplace : boolean, default False

Returns
-------
renamed : type of caller
"""
# should move this at some point
from pandas.core.series import _get_rename_function

mapper_f = _get_rename_function(mapper)

if axis == 0:
new_data = self._data.rename_items(mapper_f, copydata=copy)
else:
new_data = self._data.rename_axis(mapper_f, axis=axis)
if copy:
new_data = new_data.copy()

return self._constructor(new_data)
axis = self._AXIS_NAMES[axis]
d = { 'copy' : copy, 'inplace' : inplace }
d[axis] = mapper
return self.rename(**d)

#----------------------------------------------------------------------
# Comparisons
Expand Down Expand Up @@ -1373,7 +1436,7 @@ def copy(self, deep=True):
data = self._data
if deep:
data = data.copy()
return self._constructor(data)
return self._constructor(data)._propogate_attributes(self)

def convert_objects(self, convert_dates=True, convert_numeric=False, copy=True):
"""
Expand Down
15 changes: 11 additions & 4 deletions pandas/core/internals.py
Original file line number Diff line number Diff line change
Expand Up @@ -2758,8 +2758,8 @@ def rrenamer(x):
return '%s%s' % (x, rsuffix)
return x

this = self.rename_items(lrenamer, copydata=copydata)
other = other.rename_items(rrenamer, copydata=copydata)
this = self.rename_items(lrenamer, copy=copydata)
other = other.rename_items(rrenamer, copy=copydata)
else:
this = self

Expand All @@ -2777,6 +2777,13 @@ def _is_indexed_like(self, other):
return False
return True

def rename(self, mapper, axis, copy=False):
""" generic rename """

if axis == 0:
return self.rename_items(mapper, copy=copy)
return self.rename_axis(mapper, axis=axis)

def rename_axis(self, mapper, axis=1):

index = self.axes[axis]
Expand All @@ -2793,7 +2800,7 @@ def rename_axis(self, mapper, axis=1):
new_axes[axis] = new_axis
return self.__class__(self.blocks, new_axes)

def rename_items(self, mapper, copydata=True):
def rename_items(self, mapper, copy=True):
if isinstance(self.items, MultiIndex):
items = [tuple(mapper(y) for y in x) for x in self.items]
new_items = MultiIndex.from_tuples(items, names=self.items.names)
Expand All @@ -2803,7 +2810,7 @@ def rename_items(self, mapper, copydata=True):

new_blocks = []
for block in self.blocks:
newb = block.copy(deep=copydata)
newb = block.copy(deep=copy)
newb.set_ref_items(new_items, maybe_rename=True)
new_blocks.append(newb)
new_axes = list(self.axes)
Expand Down
Loading