Skip to content

Commit 6997fd8

Browse files
committed
deprecate categories and ordered parameters
1 parent 6e42d80 commit 6997fd8

File tree

4 files changed

+40
-69
lines changed

4 files changed

+40
-69
lines changed

doc/source/whatsnew/v0.24.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,7 @@ Deprecations
11231123
- :meth:`Series.compress` is deprecated. Use ``Series[condition]`` instead (:issue:`18262`)
11241124
- The signature of :meth:`Series.to_csv` has been uniformed to that of :meth:`DataFrame.to_csv`: the name of the first argument is now ``path_or_buf``, the order of subsequent arguments has changed, the ``header`` argument now defaults to ``True``. (:issue:`19715`)
11251125
- :meth:`Categorical.from_codes` has deprecated providing float values for the ``codes`` argument. (:issue:`21767`)
1126+
- :meth:`Categorical.from_codes` has deprecated parameters ``categories`` and ``ordered``. Supply a :class:`~pandas.api.types.CategoricalDtype` to new parameter ``dtype`` instead. (:issue:`24398`)
11261127
- :func:`pandas.read_table` is deprecated. Instead, use :func:`read_csv` passing ``sep='\t'`` if necessary (:issue:`21948`)
11271128
- :meth:`Series.str.cat` has deprecated using arbitrary list-likes *within* list-likes. A list-like container may still contain
11281129
many ``Series``, ``Index`` or 1-dimensional ``np.ndarray``, or alternatively, only scalar values. (:issue:`21950`)

pandas/core/arrays/categorical.py

+14-10
Original file line numberDiff line numberDiff line change
@@ -639,11 +639,13 @@ def _from_inferred_categories(cls, inferred_categories, inferred_codes,
639639
return cls(codes, dtype=dtype, fastpath=True)
640640

641641
@classmethod
642+
@deprecate_kwarg(old_arg_name='categories', new_arg_name=None)
643+
@deprecate_kwarg(old_arg_name='ordered', new_arg_name=None)
642644
def from_codes(cls, codes, categories=None, ordered=None, dtype=None):
643645
"""
644-
Make a Categorical type from codes and categories arrays.
646+
Make a Categorical type from codes and CategoricalDtype.
645647
646-
This constructor is useful if you already have codes and categories and
648+
This constructor is useful if you already have codes and the dtype and
647649
so do not need the (computation intensive) factorization step, which is
648650
usually done on the constructor.
649651
@@ -657,16 +659,17 @@ def from_codes(cls, codes, categories=None, ordered=None, dtype=None):
657659
categories or -1 for NaN
658660
categories : index-like, optional
659661
The categories for the categorical. Items need to be unique.
662+
663+
.. deprecated:: 0.24.0
664+
Use ``dtype`` instead.
660665
ordered : bool, optional
661666
Whether or not this categorical is treated as an ordered
662667
categorical. If not given, the resulting categorical will be
663668
unordered.
664669
665-
.. versionchanged:: 0.24.0
666-
667-
The default value has been changed to ``None``. Previously
668-
the default value was ``False``.
669-
dtype : CategoricalDtype, optional
670+
.. deprecated:: 0.24.0
671+
Use ``dtype`` instead.
672+
dtype : CategoricalDtype
670673
An instance of ``CategoricalDtype`` to use for this categorical.
671674
672675
.. versionadded:: 0.24.0
@@ -682,6 +685,8 @@ def from_codes(cls, codes, categories=None, ordered=None, dtype=None):
682685
if categories is not None or ordered is not None:
683686
raise ValueError("Cannot specify `categories` or `ordered` "
684687
"together with `dtype`.")
688+
elif categories is None and dtype is None:
689+
raise ValueError("Must specify `categories` or `dtype`.")
685690
else:
686691
dtype = CategoricalDtype(categories, ordered)
687692

@@ -1245,9 +1250,8 @@ def map(self, mapper):
12451250
"""
12461251
new_categories = self.categories.map(mapper)
12471252
try:
1248-
return self.from_codes(self._codes.copy(),
1249-
categories=new_categories,
1250-
ordered=self.ordered)
1253+
new_dtype = CategoricalDtype(new_categories, ordered=self.ordered)
1254+
return self.from_codes(self._codes.copy(), dtype=new_dtype)
12511255
except ValueError:
12521256
return np.take(new_categories, self._codes)
12531257

pandas/tests/arrays/categorical/test_constructors.py

+23-58
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,13 @@ class TestCategoricalConstructors(object):
2121
def test_validate_ordered(self):
2222
# see gh-14058
2323
exp_msg = "'ordered' must either be 'True' or 'False'"
24-
exp_err = TypeError
2524

26-
# This should be a boolean.
25+
# This should be a boolean or None.
2726
ordered = np.array([0, 1, 2])
2827

29-
with pytest.raises(exp_err, match=exp_msg):
28+
with pytest.raises(TypeError, match=exp_msg):
3029
Categorical([1, 2, 3], ordered=ordered)
3130

32-
with pytest.raises(exp_err, match=exp_msg):
33-
Categorical.from_codes([0, 0, 1], categories=['a', 'b', 'c'],
34-
ordered=ordered)
35-
3631
def test_constructor_empty(self):
3732
# GH 17248
3833
c = Categorical([])
@@ -421,76 +416,41 @@ def test_constructor_with_categorical_categories(self):
421416
tm.assert_categorical_equal(result, expected)
422417

423418
def test_from_codes(self):
419+
dtype = CategoricalDtype(categories=[1, 2])
420+
421+
# no dtype or categories
422+
msg = "Must specify `categories` or `dtype`."
423+
with pytest.raises(ValueError, match=msg):
424+
Categorical.from_codes([1, 2])
424425

425426
# too few categories
426-
dtype = CategoricalDtype(categories=[1, 2])
427427
msg = "codes need to be between "
428-
with pytest.raises(ValueError, match=msg):
429-
Categorical.from_codes([1, 2], categories=dtype.categories)
430428
with pytest.raises(ValueError, match=msg):
431429
Categorical.from_codes([1, 2], dtype=dtype)
432430

433431
# no int codes
434432
msg = "codes need to be array-like integers"
435-
with pytest.raises(ValueError, match=msg):
436-
Categorical.from_codes(["a"], categories=dtype.categories)
437433
with pytest.raises(ValueError, match=msg):
438434
Categorical.from_codes(["a"], dtype=dtype)
439435

440-
# no unique categories
441-
with pytest.raises(ValueError,
442-
match="Categorical categories must be unique"):
443-
Categorical.from_codes([0, 1, 2], categories=["a", "a", "b"])
444-
445-
# NaN categories included
446-
with pytest.raises(ValueError,
447-
match="Categorial categories cannot be null"):
448-
Categorical.from_codes([0, 1, 2], categories=["a", "b", np.nan])
449-
450436
# too negative
451437
dtype = CategoricalDtype(categories=["a", "b", "c"])
452438
msg = r"codes need to be between -1 and len\(categories\)-1"
453-
with pytest.raises(ValueError, match=msg):
454-
Categorical.from_codes([-2, 1, 2], categories=dtype.categories)
455439
with pytest.raises(ValueError, match=msg):
456440
Categorical.from_codes([-2, 1, 2], dtype=dtype)
457441

458442
exp = Categorical(["a", "b", "c"], ordered=False)
459-
res = Categorical.from_codes([0, 1, 2], categories=dtype.categories)
460-
tm.assert_categorical_equal(exp, res)
461-
462443
res = Categorical.from_codes([0, 1, 2], dtype=dtype)
463444
tm.assert_categorical_equal(exp, res)
464445

465446
codes = np.random.choice([0, 1], 5, p=[0.9, 0.1])
466447
dtype = CategoricalDtype(categories=["train", "test"])
467-
Categorical.from_codes(codes, categories=dtype.categories)
468448
Categorical.from_codes(codes, dtype=dtype)
469449

470-
def test_from_codes_with_categorical_categories(self):
471-
# GH17884
472-
expected = Categorical(['a', 'b'], categories=['a', 'b', 'c'])
473-
474-
result = Categorical.from_codes(
475-
[0, 1], categories=Categorical(['a', 'b', 'c']))
476-
tm.assert_categorical_equal(result, expected)
477-
478-
result = Categorical.from_codes(
479-
[0, 1], categories=CategoricalIndex(['a', 'b', 'c']))
480-
tm.assert_categorical_equal(result, expected)
481-
482-
# non-unique Categorical still raises
483-
with pytest.raises(ValueError,
484-
match="Categorical categories must be unique"):
485-
Categorical.from_codes([0, 1], Categorical(['a', 'b', 'a']))
486-
487450
def test_from_codes_with_nan_code(self):
488451
# GH21767
489452
codes = [1, 2, np.nan]
490453
dtype = CategoricalDtype(categories=['a', 'b', 'c'])
491-
with pytest.raises(ValueError,
492-
match="codes need to be array-like integers"):
493-
Categorical.from_codes(codes, categories=dtype.categories)
494454
with pytest.raises(ValueError,
495455
match="codes need to be array-like integers"):
496456
Categorical.from_codes(codes, dtype=dtype)
@@ -500,36 +460,41 @@ def test_from_codes_with_float(self):
500460
codes = [1.0, 2.0, 0] # integer, but in float dtype
501461
dtype = CategoricalDtype(categories=['a', 'b', 'c'])
502462

503-
with tm.assert_produces_warning(FutureWarning):
504-
cat = Categorical.from_codes(codes, dtype.categories)
505-
tm.assert_numpy_array_equal(cat.codes, np.array([1, 2, 0], dtype='i1'))
506-
507-
with tm.assert_produces_warning(FutureWarning):
463+
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
508464
cat = Categorical.from_codes(codes, dtype=dtype)
509465
tm.assert_numpy_array_equal(cat.codes, np.array([1, 2, 0], dtype='i1'))
510466

511467
codes = [1.1, 2.0, 0] # non-integer
512-
with pytest.raises(ValueError,
513-
match="codes need to be array-like integers"):
514-
Categorical.from_codes(codes, dtype.categories)
515468
with pytest.raises(ValueError,
516469
match="codes need to be array-like integers"):
517470
Categorical.from_codes(codes, dtype=dtype)
518471

472+
def test_from_codes_deprecated(self):
473+
with tm.assert_produces_warning(FutureWarning):
474+
Categorical.from_codes([0, 1], categories=['a', 'b'])
475+
476+
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
477+
Categorical.from_codes([0, 1], categories=['a', 'b'], ordered=True)
478+
479+
with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
480+
Categorical.from_codes([0, 1], categories=['a', 'b'], ordered=False)
481+
519482
@pytest.mark.parametrize('dtype', [None, 'category'])
520483
def test_from_inferred_categories(self, dtype):
521484
cats = ['a', 'b']
522485
codes = np.array([0, 0, 1, 1], dtype='i8')
523486
result = Categorical._from_inferred_categories(cats, codes, dtype)
524-
expected = Categorical.from_codes(codes, cats)
487+
expected = Categorical.from_codes(codes,
488+
dtype=CategoricalDtype(cats))
525489
tm.assert_categorical_equal(result, expected)
526490

527491
@pytest.mark.parametrize('dtype', [None, 'category'])
528492
def test_from_inferred_categories_sorts(self, dtype):
529493
cats = ['b', 'a']
530494
codes = np.array([0, 1, 1, 1], dtype='i8')
531495
result = Categorical._from_inferred_categories(cats, codes, dtype)
532-
expected = Categorical.from_codes([1, 0, 0, 0], ['a', 'b'])
496+
expected = Categorical.from_codes([1, 0, 0, 0],
497+
dtype=CategoricalDtype(['a', 'b']))
533498
tm.assert_categorical_equal(result, expected)
534499

535500
def test_from_inferred_categories_dtype(self):

pandas/tests/indexes/test_category.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -433,8 +433,9 @@ def test_astype(self):
433433
right=[2, 4],
434434
closed='right')
435435

436+
dtype = CategoricalDtype(categories=ii, ordered=True)
436437
ci = CategoricalIndex(Categorical.from_codes(
437-
[0, 1, -1], categories=ii, ordered=True))
438+
[0, 1, -1], dtype=dtype))
438439

439440
result = ci.astype('interval')
440441
expected = ii.take([0, 1, -1])

0 commit comments

Comments
 (0)