Skip to content

Commit 077b99e

Browse files
committed
TST/DOC: Fix tests and docs for .cat raising AttributeError if invalid
Follow-up on GH9617 Fixes GH8814 -- is it fair to say that ``hasattr(s, 'cat')`` is probably the best solution we're going to come up with for checking for categorical data?
1 parent 8fadfa9 commit 077b99e

File tree

4 files changed

+19
-9
lines changed

4 files changed

+19
-9
lines changed

doc/source/categorical.rst

+8
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,14 @@ Dtype comparisons work:
766766
dtype == np.str_
767767
np.str_ == dtype
768768
769+
To check if a Series contains Categorical data, with pandas 0.16 or later, use
770+
``hasattr(s, 'cat')``:
771+
772+
.. ipython:: python
773+
774+
hasattr(Series(['a'], dtype='category'), 'cat')
775+
hasattr(Series(['a']), 'cat')
776+
769777
Using `numpy` functions on a `Series` of type ``category`` should not work as `Categoricals`
770778
are not numeric data (even in the case that ``.categories`` is numeric).
771779

pandas/core/generic.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,11 @@ class NDFrame(PandasObject):
7878
copy : boolean, default False
7979
"""
8080
_internal_names = ['_data', '_cacher', '_item_cache', '_cache',
81-
'is_copy', 'dt', 'cat', 'str', '_subtyp', '_index',
81+
'is_copy', '_subtyp', '_index',
8282
'_default_kind', '_default_fill_value',
8383
'__array_struct__','__array_interface__']
8484
_internal_names_set = set(_internal_names)
85+
_accessors = frozenset([])
8586
_metadata = []
8687
is_copy = None
8788

@@ -1957,9 +1958,9 @@ def __getattr__(self, name):
19571958
# Note: obj.x will always call obj.__getattribute__('x') prior to
19581959
# calling obj.__getattr__('x').
19591960

1960-
if name in self._internal_names_set:
1961-
return object.__getattribute__(self, name)
1962-
elif name in self._metadata:
1961+
if (name in self._internal_names_set
1962+
or name in self._metadata
1963+
or name in self._accessors):
19631964
return object.__getattribute__(self, name)
19641965
else:
19651966
if name in self._info_axis:

pandas/core/series.py

+1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ class Series(base.IndexOpsMixin, generic.NDFrame):
113113
Copy input data
114114
"""
115115
_metadata = ['name']
116+
_accessors = frozenset(['dt', 'cat', 'str'])
116117
_allow_index_ops = True
117118

118119
def __init__(self, data=None, index=None, dtype=None, name=None,

pandas/tests/test_categorical.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -1354,13 +1354,13 @@ def test_sequence_like(self):
13541354
def test_series_delegations(self):
13551355

13561356
# invalid accessor
1357-
self.assertRaises(TypeError, lambda : Series([1,2,3]).cat)
1358-
tm.assertRaisesRegexp(TypeError,
1357+
self.assertRaises(AttributeError, lambda : Series([1,2,3]).cat)
1358+
tm.assertRaisesRegexp(AttributeError,
13591359
r"Can only use .cat accessor with a 'category' dtype",
13601360
lambda : Series([1,2,3]).cat)
1361-
self.assertRaises(TypeError, lambda : Series(['a','b','c']).cat)
1362-
self.assertRaises(TypeError, lambda : Series(np.arange(5.)).cat)
1363-
self.assertRaises(TypeError, lambda : Series([Timestamp('20130101')]).cat)
1361+
self.assertRaises(AttributeError, lambda : Series(['a','b','c']).cat)
1362+
self.assertRaises(AttributeError, lambda : Series(np.arange(5.)).cat)
1363+
self.assertRaises(AttributeError, lambda : Series([Timestamp('20130101')]).cat)
13641364

13651365
# Series should delegate calls to '.categories', '.codes', '.ordered' and the
13661366
# methods '.set_categories()' 'drop_unused_categories()' to the categorical

0 commit comments

Comments
 (0)