diff --git a/doc/source/whatsnew/v0.21.0.txt b/doc/source/whatsnew/v0.21.0.txt index 598e452640781..ff437365e0e0d 100644 --- a/doc/source/whatsnew/v0.21.0.txt +++ b/doc/source/whatsnew/v0.21.0.txt @@ -804,6 +804,7 @@ Deprecations - :func:`read_excel()` has deprecated ``parse_cols`` in favor of ``usecols`` for consistency with :func:`read_csv` (:issue:`4988`) - :func:`read_csv()` has deprecated the ``tupleize_cols`` argument. Column tuples will always be converted to a ``MultiIndex`` (:issue:`17060`) - :meth:`DataFrame.to_csv` has deprecated the ``tupleize_cols`` argument. Multi-index columns will be always written as rows in the CSV file (:issue:`17060`) +- The ``Index`` constructor has deprecated the ``tupleize_cols`` argument. A ``MultiIndex`` will always be created if possible (:issue:`17060`) - The ``convert`` parameter has been deprecated in the ``.take()`` method, as it was not being respected (:issue:`16948`) - ``pd.options.html.border`` has been deprecated in favor of ``pd.options.display.html.border`` (:issue:`15793`). - :func:`SeriesGroupBy.nth` has deprecated ``True`` in favor of ``'all'`` for its kwarg ``dropna`` (:issue:`11038`). diff --git a/pandas/core/dtypes/dtypes.py b/pandas/core/dtypes/dtypes.py index b3498abb3b2c0..43aae4309d9f8 100644 --- a/pandas/core/dtypes/dtypes.py +++ b/pandas/core/dtypes/dtypes.py @@ -176,7 +176,8 @@ def _finalize(self, categories, ordered, fastpath=False): ordered = False if categories is not None: - categories = Index(categories, tupleize_cols=False) + categories = Index._construct_index(categories, + tupleize_cols=False) # validation self._validate_categories(categories, fastpath=fastpath) self._validate_ordered(ordered) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index a995fc10a6674..075e2a2eed568 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -179,8 +179,21 @@ class Index(IndexOpsMixin, PandasObject): str = accessor.AccessorProperty(strings.StringMethods) def __new__(cls, data=None, dtype=None, copy=False, name=None, - fastpath=False, tupleize_cols=True, **kwargs): + fastpath=False, tupleize_cols=None, **kwargs): + if tupleize_cols is not None: + warnings.warn("The 'tupleize_cols' parameter is deprecated and " + "will be removed in a future version", + FutureWarning, stacklevel=2) + else: + tupleize_cols = True + + return cls._construct_index(data=data, dtype=dtype, copy=copy, + name=name, fastpath=fastpath, + tupleize_cols=tupleize_cols, **kwargs) + @classmethod + def _construct_index(cls, data=None, dtype=None, copy=False, name=None, + fastpath=False, tupleize_cols=True, **kwargs): if name is None and hasattr(data, 'name'): name = data.name diff --git a/pandas/tests/frame/test_constructors.py b/pandas/tests/frame/test_constructors.py index 7f1cc12ec4277..7fa9991ab41d5 100644 --- a/pandas/tests/frame/test_constructors.py +++ b/pandas/tests/frame/test_constructors.py @@ -434,10 +434,17 @@ def test_constructor_dict_multiindex(self): d['z'] = {'y': 123., ('i', 'i'): 111, ('i', 'j'): 111, ('j', 'i'): 111} _d.insert(0, ('z', d['z'])) - expected = DataFrame( - [x[1] for x in _d], - index=Index([x[0] for x in _d], tupleize_cols=False)).T - expected.index = Index(expected.index, tupleize_cols=False) + + with tm.assert_produces_warning(FutureWarning, + check_stacklevel=False): + expected = DataFrame( + [x[1] for x in _d], + index=Index([x[0] for x in _d], tupleize_cols=False)).T + + with tm.assert_produces_warning(FutureWarning, + check_stacklevel=False): + expected.index = Index(expected.index, tupleize_cols=False) + df = DataFrame(d) df = df.reindex(columns=expected.columns, index=expected.index) check(df, expected) diff --git a/pandas/tests/frame/test_indexing.py b/pandas/tests/frame/test_indexing.py index f850b8f2ee178..7c51a797ac40c 100644 --- a/pandas/tests/frame/test_indexing.py +++ b/pandas/tests/frame/test_indexing.py @@ -2299,8 +2299,11 @@ def test_index_namedtuple(self): IndexType = namedtuple("IndexType", ["a", "b"]) idx1 = IndexType("foo", "bar") idx2 = IndexType("baz", "bof") - index = Index([idx1, idx2], - name="composite_index", tupleize_cols=False) + + with tm.assert_produces_warning(FutureWarning): + index = Index([idx1, idx2], + name="composite_index", tupleize_cols=False) + df = DataFrame([(1, 2), (3, 4)], index=index, columns=["A", "B"]) with catch_warnings(record=True): diff --git a/pandas/tests/indexes/test_base.py b/pandas/tests/indexes/test_base.py index 307cda7f2d1cb..cfad98e02bfca 100644 --- a/pandas/tests/indexes/test_base.py +++ b/pandas/tests/indexes/test_base.py @@ -475,7 +475,11 @@ def test_identical(self): assert i1.identical(i2) i3 = Index([('a', 'a'), ('a', 'b'), ('b', 'a')]) - i4 = Index([('a', 'a'), ('a', 'b'), ('b', 'a')], tupleize_cols=False) + + with tm.assert_produces_warning(FutureWarning): + i4 = Index([('a', 'a'), ('a', 'b'), ('b', 'a')], + tupleize_cols=False) + assert not i3.identical(i4) def test_is_(self): diff --git a/pandas/tests/indexes/test_multi.py b/pandas/tests/indexes/test_multi.py index 18bfc3d0efbee..ccca85385b2f2 100644 --- a/pandas/tests/indexes/test_multi.py +++ b/pandas/tests/indexes/test_multi.py @@ -1547,7 +1547,10 @@ def test_identical(self): assert mi.identical(mi2) mi3 = Index(mi.tolist(), names=mi.names) - mi4 = Index(mi.tolist(), names=mi.names, tupleize_cols=False) + + with tm.assert_produces_warning(FutureWarning): + mi4 = Index(mi.tolist(), names=mi.names, tupleize_cols=False) + assert mi.identical(mi3) assert not mi.identical(mi4) assert mi.equals(mi4) diff --git a/pandas/tests/series/test_api.py b/pandas/tests/series/test_api.py index 6b950be15ca46..9da71a3d4dc84 100644 --- a/pandas/tests/series/test_api.py +++ b/pandas/tests/series/test_api.py @@ -172,9 +172,11 @@ def test_constructor_dict_multiindex(self): d['z'] = 111. _d.insert(0, ('z', d['z'])) result = self.series_klass(d) - expected = self.series_klass([x[1] for x in _d], - index=pd.Index([x[0] for x in _d], - tupleize_cols=False)) + + with tm.assert_produces_warning(FutureWarning, check_stacklevel=False): + expected = self.series_klass([x[1] for x in _d], + index=pd.Index([x[0] for x in _d], + tupleize_cols=False)) result = result.reindex(index=expected.index) self._assert_series_equal(result, expected) diff --git a/pandas/tests/test_categorical.py b/pandas/tests/test_categorical.py index e1d0b756fed1c..d3bf232005b4a 100644 --- a/pandas/tests/test_categorical.py +++ b/pandas/tests/test_categorical.py @@ -145,7 +145,10 @@ def test_constructor_empty(self): def test_constructor_tuples(self): values = np.array([(1,), (1, 2), (1,), (1, 2)], dtype=object) result = Categorical(values) - expected = Index([(1,), (1, 2)], tupleize_cols=False) + + with tm.assert_produces_warning(FutureWarning): + expected = Index([(1,), (1, 2)], tupleize_cols=False) + tm.assert_index_equal(result.categories, expected) assert result.ordered is False @@ -158,8 +161,11 @@ def test_constructor_tuples_datetimes(self): (Timestamp('2010-01-02'),), ('a', 'b')], dtype=object)[:-1] result = Categorical(values) - expected = Index([(Timestamp('2010-01-01'),), - (Timestamp('2010-01-02'),)], tupleize_cols=False) + + with tm.assert_produces_warning(FutureWarning): + expected = Index([(Timestamp('2010-01-01'),), + (Timestamp('2010-01-02'),)], tupleize_cols=False) + tm.assert_index_equal(result.categories, expected) def test_constructor_unsortable(self):