Skip to content

Commit

Permalink
DEPR: deprecate .select() in favor of .loc(axis=)[]
Browse files Browse the repository at this point in the history
closes #12401
  • Loading branch information
jreback committed Oct 1, 2017
1 parent 7d4a260 commit 9c2b402
Show file tree
Hide file tree
Showing 10 changed files with 232 additions and 61 deletions.
24 changes: 24 additions & 0 deletions doc/source/whatsnew/v0.21.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,30 @@ These now coerce to ``object`` dtype.
- Inconsistent behavior in ``.where()`` with datetimelikes which would raise rather than coerce to ``object`` (:issue:`16402`)
- Bug in assignment against ``int64`` data with ``np.ndarray`` with ``float64`` dtype may keep ``int64`` dtype (:issue:`14001`)

.. _whatsnew_0210.api_breaking.select:

Select method is deprecated
^^^^^^^^^^^^^^^^^^^^^^^^^^^

The :meth:`Series.select` and :meth:`DataFrame.select` methods are deprecated in favor of using ``.loc[]`` (:issue:`12401`)

.. ipython:: python

df = DataFrame({'A': [1, 2, 3]}, index=['foo', 'bar', 'baz'])

.. code_block:: ipython

In [3]: df.select(lambda x: x in ['bar', 'baz'])
FutureWarning: select is deprecated and will be removed in a future release. You can use .loc[crit] as a replacement
Out[3]:
A
bar 2
baz 3

.. ipython:: python

df.loc[lambda x: x in ['bar', 'baz']]

.. _whatsnew_0210.api.na_changes:

NA naming Changes
Expand Down
49 changes: 47 additions & 2 deletions pandas/core/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,13 +441,58 @@ def _get_callable_name(obj):
return None


def _apply_if_callable(maybe_callable, obj, **kwargs):
def _apply_if_callable(maybe_callable, obj, axis=None, **kwargs):
"""
Evaluate possibly callable input using obj and kwargs if it is callable,
otherwise return as it is
Parameters
----------
maybe_callable : possibly a callable
obj : NDFrame
axis : int, optional
**kwargs
"""

if callable(maybe_callable):
return maybe_callable(obj, **kwargs)

# we are allowing a user callable, which can return
# a result based on the object itself, e.g. a scalar / list
# of labels, or a boolean result from evaluating the labels
# on the specified axis

def try_on_axis():
labels = obj._get_axis(axis or 0)
return labels.map(maybe_callable, **kwargs).values

# act on object
try:
result = maybe_callable(obj, **kwargs)

# if we have asked for a specific axis
# then we must be 1d
if axis is not None:
if getattr(result, 'ndim', 1) != 1:
raise ValueError

return result
except KeyError as e:
# this is potentially a legitimate error
# if we cannot work on the labels
# we want to preserve the original KeyError
try:
return try_on_axis()
except: # no pragma
raise e
except: # no pragma
pass

# act on the axis
try:
return try_on_axis()
except AttributeError:
pass

return maybe_callable


Expand Down
36 changes: 23 additions & 13 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -2281,6 +2281,8 @@ def select(self, crit, axis=0):
"""
Return data corresponding to axis labels matching criteria
DEPRECATED: use .loc(axis=)[crit] to select via labels
Parameters
----------
crit : function
Expand All @@ -2291,6 +2293,11 @@ def select(self, crit, axis=0):
-------
selection : type of caller
"""
warnings.warn("select is deprecated and will be removed in a "
"future release. You can use "
".loc[crit] as a replacement",
FutureWarning, stacklevel=2)

axis = self._get_axis_number(axis)
axis_name = self._get_axis_name(axis)
axis_values = self._get_axis(axis)
Expand Down Expand Up @@ -3043,7 +3050,7 @@ def filter(self, items=None, like=None, regex=None, axis=None):
See Also
--------
pandas.DataFrame.select
pandas.DataFrame.loc
Notes
-----
Expand All @@ -3062,20 +3069,23 @@ def filter(self, items=None, like=None, regex=None, axis=None):

if axis is None:
axis = self._info_axis_name
axis_name = self._get_axis_name(axis)
axis_values = self._get_axis(axis_name)
labels = self._get_axis(axis)

if items is not None:
return self.reindex(**{axis_name:
[r for r in items if r in axis_values]})
name = self._get_axis_name(axis)
return self.reindex(
**{name: [r for r in items if r in labels]})
elif like:
matchf = lambda x: (like in x if isinstance(x, string_types) else
like in str(x))
return self.select(matchf, axis=axis_name)
def f(x):
if not isinstance(x, string_types):
x = str(x)
return like in x
values = labels.map(f)
return self.loc(axis=axis)[values.values]
elif regex:
matcher = re.compile(regex)
return self.select(lambda x: matcher.search(str(x)) is not None,
axis=axis_name)
values = labels.map(lambda x: matcher.search(str(x)) is not None)
return self.loc(axis=axis)[values.values]
else:
raise TypeError('Must pass either `items`, `like`, or `regex`')

Expand Down Expand Up @@ -5698,7 +5708,7 @@ def _where(self, cond, other=np.nan, inplace=False, axis=None, level=None,
inplace = validate_bool_kwarg(inplace, 'inplace')

# align the cond to same shape as myself
cond = com._apply_if_callable(cond, self)
cond = com._apply_if_callable(cond, self, axis=axis)
if isinstance(cond, NDFrame):
cond, _ = cond.align(self, join='right', broadcast_axis=1)
else:
Expand Down Expand Up @@ -5939,7 +5949,7 @@ def _where(self, cond, other=np.nan, inplace=False, axis=None, level=None,
def where(self, cond, other=np.nan, inplace=False, axis=None, level=None,
try_cast=False, raise_on_error=True):

other = com._apply_if_callable(other, self)
other = com._apply_if_callable(other, self, axis=axis)
return self._where(cond, other, inplace, axis, level, try_cast,
raise_on_error)

Expand All @@ -5950,7 +5960,7 @@ def mask(self, cond, other=np.nan, inplace=False, axis=None, level=None,
try_cast=False, raise_on_error=True):

inplace = validate_bool_kwarg(inplace, 'inplace')
cond = com._apply_if_callable(cond, self)
cond = com._apply_if_callable(cond, self, axis=axis)

return self.where(~cond, other=other, inplace=inplace, axis=axis,
level=level, try_cast=try_cast,
Expand Down
Loading

0 comments on commit 9c2b402

Please sign in to comment.