17
17
from pandas .core .common import (CategoricalDtype , ABCSeries , isnull , notnull ,
18
18
is_categorical_dtype , is_integer_dtype , is_object_dtype ,
19
19
_possibly_infer_to_datetimelike , get_dtype_kinds ,
20
- is_list_like , is_sequence , is_null_slice ,
20
+ is_list_like , is_sequence , is_null_slice , is_bool ,
21
21
_ensure_platform_int , _ensure_object , _ensure_int64 ,
22
22
_coerce_indexer_dtype , _values_from_object , take_1d )
23
23
from pandas .util .terminal import get_terminal_size
@@ -141,7 +141,7 @@ class Categorical(PandasObject):
141
141
to be the unique values of values.
142
142
ordered : boolean, optional
143
143
Whether or not this categorical is treated as a ordered categorical. If not given,
144
- the resulting categorical will be ordered if values can be sorted .
144
+ the resulting categorical will not be ordered .
145
145
name : str, optional
146
146
Name for the Categorical variable. If name is None, will attempt
147
147
to infer from values.
@@ -184,7 +184,6 @@ class Categorical(PandasObject):
184
184
dtype = CategoricalDtype ()
185
185
"""The dtype (always "category")"""
186
186
187
- ordered = None
188
187
"""Whether or not this Categorical is ordered.
189
188
190
189
Only ordered `Categoricals` can be sorted (according to the order
@@ -201,18 +200,17 @@ class Categorical(PandasObject):
201
200
# For comparisons, so that numpy uses our implementation if the compare ops, which raise
202
201
__array_priority__ = 1000
203
202
_typ = 'categorical'
204
- ordered = False
205
203
name = None
206
204
207
- def __init__ (self , values , categories = None , ordered = None , name = None , fastpath = False ,
205
+ def __init__ (self , values , categories = None , ordered = False , name = None , fastpath = False ,
208
206
levels = None ):
209
207
210
208
if fastpath :
211
209
# fast path
212
210
self ._codes = _coerce_indexer_dtype (values , categories )
213
211
self .name = name
214
212
self .categories = categories
215
- self .ordered = ordered
213
+ self ._ordered = ordered
216
214
return
217
215
218
216
if name is None :
@@ -237,8 +235,6 @@ def __init__(self, values, categories=None, ordered=None, name=None, fastpath=Fa
237
235
cat = values .values
238
236
if categories is None :
239
237
categories = cat .categories
240
- if ordered is None :
241
- ordered = cat .ordered
242
238
values = values .__array__ ()
243
239
244
240
elif isinstance (values , Index ):
@@ -263,18 +259,12 @@ def __init__(self, values, categories=None, ordered=None, name=None, fastpath=Fa
263
259
264
260
if categories is None :
265
261
try :
266
- codes , categories = factorize (values , sort = True )
267
- # If the underlying data structure was sortable, and the user doesn't want to
268
- # "forget" this order, the categorical also is sorted/ordered
269
- if ordered is None :
270
- ordered = True
262
+ codes , categories = factorize (values , sort = ordered )
271
263
except TypeError :
272
- codes , categories = factorize (values , sort = False )
273
- if ordered :
274
- # raise, as we don't have a sortable data structure and so the user should
275
- # give us one by specifying categories
276
- raise TypeError ("'values' is not ordered, please explicitly specify the "
277
- "categories order by passing in a categories argument." )
264
+ # raise, as we don't have a sortable data structure and so the user should
265
+ # give us one by specifying categories
266
+ raise TypeError ("'values' is not factorizable, please pass "
267
+ "categories order by passing in a categories argument." )
278
268
except ValueError :
279
269
280
270
### FIXME ####
@@ -300,12 +290,7 @@ def __init__(self, values, categories=None, ordered=None, name=None, fastpath=Fa
300
290
warn ("None of the categories were found in values. Did you mean to use\n "
301
291
"'Categorical.from_codes(codes, categories)'?" , RuntimeWarning )
302
292
303
- # if we got categories, we can assume that the order is intended
304
- # if ordered is unspecified
305
- if ordered is None :
306
- ordered = True
307
-
308
- self .ordered = False if ordered is None else ordered
293
+ self ._ordered = ordered
309
294
self .categories = categories
310
295
self .name = name
311
296
self ._codes = _coerce_indexer_dtype (codes , categories )
@@ -460,6 +445,37 @@ def _get_levels(self):
460
445
# TODO: Remove after deprecation period in 2017/ after 0.18
461
446
levels = property (fget = _get_levels , fset = _set_levels )
462
447
448
+ _ordered = None
449
+
450
+ def _set_ordered (self , value ):
451
+ """ Sets the ordered attribute to the boolean value """
452
+ warn ("Setting 'ordered' directly is deprecated, use 'set_ordered'" , FutureWarning )
453
+ self .set_ordered (value , inplace = True )
454
+
455
+ def set_ordered (self , value , inplace = False ):
456
+ """
457
+ Sets the ordered attribute to the boolean value
458
+
459
+ Parameters
460
+ ----------
461
+ value : boolean to set whether this categorical is ordered (True) or not (False)
462
+ inplace : boolean (default: False)
463
+ Whether or not to set the ordered attribute inplace or return a copy of this categorical
464
+ with ordered set to the value
465
+ """
466
+ if not is_bool (value ):
467
+ raise TypeError ("ordered must be a boolean value" )
468
+ cat = self if inplace else self .copy ()
469
+ cat ._ordered = value
470
+ if not inplace :
471
+ return cat
472
+
473
+ def _get_ordered (self ):
474
+ """ Gets the ordered attribute """
475
+ return self ._ordered
476
+
477
+ ordered = property (fget = _get_ordered , fset = _set_ordered )
478
+
463
479
def set_categories (self , new_categories , ordered = None , rename = False , inplace = False ):
464
480
""" Sets the categories to the specified new_categories.
465
481
@@ -486,7 +502,7 @@ def set_categories(self, new_categories, ordered=None, rename=False, inplace=Fal
486
502
----------
487
503
new_categories : Index-like
488
504
The categories in new order.
489
- ordered : boolean, optional
505
+ ordered : boolean, (default: False)
490
506
Whether or not the categorical is treated as a ordered categorical. If not given,
491
507
do not change the ordered information.
492
508
rename : boolean (default: False)
@@ -520,8 +536,9 @@ def set_categories(self, new_categories, ordered=None, rename=False, inplace=Fal
520
536
cat ._codes = _get_codes_for_values (values , new_categories )
521
537
cat ._categories = new_categories
522
538
523
- if not ordered is None :
524
- cat .ordered = ordered
539
+ if ordered is None :
540
+ ordered = self .ordered
541
+ cat .set_ordered (ordered , inplace = True )
525
542
526
543
if not inplace :
527
544
return cat
@@ -765,6 +782,15 @@ def __setstate__(self, state):
765
782
state ['_categories' ] = \
766
783
self ._validate_categories (state .pop ('_levels' ))
767
784
785
+ # 0.16.0 ordered change
786
+ if '_ordered' not in state :
787
+
788
+ # >=15.0 < 0.16.0
789
+ if 'ordered' in state :
790
+ state ['_ordered' ] = state .pop ('ordered' )
791
+ else :
792
+ state ['_ordered' ] = False
793
+
768
794
for k , v in compat .iteritems (state ):
769
795
setattr (self , k , v )
770
796
@@ -1498,6 +1524,7 @@ class CategoricalAccessor(PandasDelegate):
1498
1524
>>> s.cat.remove_categories(['d'])
1499
1525
>>> s.cat.remove_unused_categories()
1500
1526
>>> s.cat.set_categories(list('abcde'))
1527
+ >>> s.cat.set_ordered(True)
1501
1528
1502
1529
"""
1503
1530
@@ -1533,7 +1560,8 @@ def _delegate_method(self, name, *args, **kwargs):
1533
1560
"add_categories" ,
1534
1561
"remove_categories" ,
1535
1562
"remove_unused_categories" ,
1536
- "set_categories" ],
1563
+ "set_categories" ,
1564
+ "set_ordered" ],
1537
1565
typ = 'method' )
1538
1566
1539
1567
##### utility routines #####
0 commit comments