From 24faa4bc96cafcdfac3d9f02136c07aa7bb249a5 Mon Sep 17 00:00:00 2001 From: Brock Date: Fri, 6 Oct 2023 08:42:06 -0700 Subject: [PATCH 1/5] REF: implement Index._format_flat, _format_multi --- pandas/core/indexes/base.py | 18 ++++++++ pandas/core/indexes/datetimelike.py | 18 ++++++++ pandas/core/indexes/multi.py | 61 +++++++++++++++++++++++++++ pandas/io/formats/excel.py | 8 +--- pandas/io/formats/format.py | 39 +++++++++++------ pandas/io/formats/html.py | 14 +++--- pandas/io/formats/style_render.py | 4 +- pandas/tests/io/excel/test_writers.py | 2 +- 8 files changed, 137 insertions(+), 27 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 147113a1e505e..181e2ec7582a4 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -1392,6 +1392,24 @@ def format( return self._format_with_header(header=header, na_rep=na_rep) + def _format_flat( + self, + *, + name: bool, + ) -> list[str_t]: + """ + Render a string representation of the Index. + """ + header = [] + if name: + header.append( + pprint_thing(self.name, escape_chars=("\t", "\r", "\n")) + if self.name is not None + else "" + ) + + return self._format_with_header(header, na_rep="NaN") + def _format_with_header(self, *, header: list[str_t], na_rep: str_t) -> list[str_t]: from pandas.io.formats.format import format_array diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 94ad556219b35..09ccc6e1cd424 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -213,6 +213,24 @@ def format( header=header, na_rep=na_rep, date_format=date_format ) + def _format_flat( + self, + *, + name: bool, + ) -> list[str]: + """ + Render a string representation of the Index. + """ + header = [] + if name: + header.append( + ibase.pprint_thing(self.name, escape_chars=("\t", "\r", "\n")) + if self.name is not None + else "" + ) + + return self._format_with_header(header, na_rep="NaT") + def _format_with_header( self, *, header: list[str], na_rep: str, date_format: str | None = None ) -> list[str]: diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 8955329a7afbf..4cf27ae681b77 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -1444,6 +1444,67 @@ def format( else: return result_levels + def _format_multi( + self, + *, + names: bool, + sparsify: bool | None | lib.NoDefault, + ) -> list: + if len(self) == 0: + return [] + + stringified_levels = [] + for lev, level_codes in zip(self.levels, self.codes): + na = _get_na_rep(lev.dtype) + + if len(lev) > 0: + formatted = lev.take(level_codes)._format_flat(name=False) + + # we have some NA + mask = level_codes == -1 + if mask.any(): + formatted = np.array(formatted, dtype=object) + formatted[mask] = na + formatted = formatted.tolist() + + else: + # weird all NA case + formatted = [ + pprint_thing(na if isna(x) else x, escape_chars=("\t", "\r", "\n")) + for x in algos.take_nd(lev._values, level_codes) + ] + stringified_levels.append(formatted) + + result_levels = [] + for lev, lev_name in zip(stringified_levels, self.names): + level = [] + + if names: + level.append( + pprint_thing(lev_name, escape_chars=("\t", "\r", "\n")) + if lev_name is not None + else "" + ) + + level.extend(np.array(lev, dtype=object)) + result_levels.append(level) + + if sparsify is None: + sparsify = get_option("display.multi_sparse") + + if sparsify: + sentinel: Literal[""] | bool | lib.NoDefault = "" + # GH3547 use value of sparsify as sentinel if it's "Falsey" + assert isinstance(sparsify, bool) or sparsify is lib.no_default + if sparsify is lib.no_default: + sentinel = sparsify + # little bit of a kludge job for #1217 + result_levels = sparsify_labels( + result_levels, start=int(names), sentinel=sentinel + ) + + return result_levels + # -------------------------------------------------------------------- # Names Methods diff --git a/pandas/io/formats/excel.py b/pandas/io/formats/excel.py index b344d9849f16c..6e4fcb7e22806 100644 --- a/pandas/io/formats/excel.py +++ b/pandas/io/formats/excel.py @@ -623,9 +623,7 @@ def _format_header_mi(self) -> Iterable[ExcelCell]: return columns = self.columns - level_strs = columns.format( - sparsify=self.merge_cells, adjoin=False, names=False - ) + level_strs = columns._format_multi(sparsify=self.merge_cells, names=False) level_lengths = get_level_lengths(level_strs) coloffset = 0 lnum = 0 @@ -813,9 +811,7 @@ def _format_hierarchical_rows(self) -> Iterable[ExcelCell]: if self.merge_cells: # Format hierarchical rows as merged cells. - level_strs = self.df.index.format( - sparsify=True, adjoin=False, names=False - ) + level_strs = self.df.index._format_multi(sparsify=True, names=False) level_lengths = get_level_lengths(level_strs) for spans, levels, level_codes in zip( diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 1efd6f18dc0af..8d43ffae1eb01 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -314,8 +314,14 @@ def to_string(self) -> str: if len(series) == 0: return f"{type(self.series).__name__}([], {footer})" - have_header = _has_names(series.index) - fmt_index = self.tr_series.index.format(name=True) + index = series.index + have_header = _has_names(index) + if isinstance(index, MultiIndex): + fmt_index = index._format_multi(names=True, sparsify=None) + adj = get_adjustment() + fmt_index = adj.adjoin(2, *fmt_index).split("\n") + else: + fmt_index = index._format_flat(name=True) fmt_values = self._get_formatted_values() if self.is_truncated_vertically: @@ -841,7 +847,7 @@ def _get_formatted_column_labels(self, frame: DataFrame) -> list[list[str]]: columns = frame.columns if isinstance(columns, MultiIndex): - fmt_columns = columns.format(sparsify=False, adjoin=False) + fmt_columns = columns._format_multi(sparsify=False, names=False) fmt_columns = list(zip(*fmt_columns)) dtypes = self.frame.dtypes._values @@ -866,7 +872,7 @@ def space_format(x, y): str_columns = [list(x) for x in zip(*str_columns)] else: - fmt_columns = columns.format() + fmt_columns = columns._format_flat(name=False) dtypes = self.frame.dtypes need_leadsp = dict(zip(fmt_columns, map(is_numeric_dtype, dtypes))) str_columns = [ @@ -884,15 +890,24 @@ def _get_formatted_index(self, frame: DataFrame) -> list[str]: columns = frame.columns fmt = self._get_formatter("__index__") - if isinstance(index, MultiIndex): - fmt_index = index.format( - sparsify=self.sparsify, - adjoin=False, - names=self.show_row_idx_names, - formatter=fmt, - ) + if fmt is not None: + if isinstance(index, MultiIndex): + fmt_index = index.format( + sparsify=self.sparsify, + adjoin=False, + names=self.show_row_idx_names, + formatter=fmt, + ) + else: + fmt_index = [index.format(name=self.show_row_idx_names, formatter=fmt)] else: - fmt_index = [index.format(name=self.show_row_idx_names, formatter=fmt)] + if isinstance(index, MultiIndex): + fmt_index = index._format_multi( + sparsify=self.sparsify, + names=self.show_row_idx_names, + ) + else: + fmt_index = [index._format_flat(name=self.show_row_idx_names)] fmt_index = [ tuple( diff --git a/pandas/io/formats/html.py b/pandas/io/formats/html.py index b1a3504d46b27..73dda1c07e948 100644 --- a/pandas/io/formats/html.py +++ b/pandas/io/formats/html.py @@ -282,7 +282,7 @@ def _write_col_header(self, indent: int) -> None: sentinel = lib.no_default else: sentinel = False - levels = self.columns.format(sparsify=sentinel, adjoin=False, names=False) + levels = self.columns._format_multi(sparsify=sentinel, names=False) level_lengths = get_level_lengths(levels, sentinel) inner_lvl = len(level_lengths) - 1 for lnum, (records, values) in enumerate(zip(level_lengths, levels)): @@ -437,7 +437,8 @@ def _write_regular_rows( if fmt is not None: index_values = self.fmt.tr_frame.index.map(fmt) else: - index_values = self.fmt.tr_frame.index.format() + # only reached with non-Multi index + index_values = self.fmt.tr_frame.index._format_flat(name=False) row: list[str] = [] for i in range(nrows): @@ -480,13 +481,13 @@ def _write_hierarchical_rows( nrows = len(frame) assert isinstance(frame.index, MultiIndex) - idx_values = frame.index.format(sparsify=False, adjoin=False, names=False) + idx_values = frame.index._format_multi(sparsify=False, names=False) idx_values = list(zip(*idx_values)) if self.fmt.sparsify: # GH3547 sentinel = lib.no_default - levels = frame.index.format(sparsify=sentinel, adjoin=False, names=False) + levels = frame.index._format_multi(sparsify=sentinel, names=False) level_lengths = get_level_lengths(levels, sentinel) inner_lvl = len(level_lengths) - 1 @@ -579,7 +580,7 @@ def _write_hierarchical_rows( ) idx_values = list( - zip(*frame.index.format(sparsify=False, adjoin=False, names=False)) + zip(*frame.index._format_multi(sparsify=False, names=False)) ) row = [] row.extend(idx_values[i]) @@ -606,7 +607,8 @@ def _get_formatted_values(self) -> dict[int, list[str]]: return {i: self.fmt.format_col(i) for i in range(self.ncols)} def _get_columns_formatted_values(self) -> list[str]: - return self.columns.format() + # only reached with non-Multi Index + return self.columns._format_flat(name=False) def write_style(self) -> None: # We use the "scoped" attribute here so that the desired diff --git a/pandas/io/formats/style_render.py b/pandas/io/formats/style_render.py index 90e9b1f0486db..aefebe3ae2894 100644 --- a/pandas/io/formats/style_render.py +++ b/pandas/io/formats/style_render.py @@ -1652,9 +1652,9 @@ def _get_level_lengths( Result is a dictionary of (level, initial_position): span """ if isinstance(index, MultiIndex): - levels = index.format(sparsify=lib.no_default, adjoin=False) + levels = index._format_multi(sparsify=lib.no_default, names=False) else: - levels = index.format() + levels = index._format_flat(name=False) if hidden_elements is None: hidden_elements = [] diff --git a/pandas/tests/io/excel/test_writers.py b/pandas/tests/io/excel/test_writers.py index e4ce969daab53..bee9fa5fa8503 100644 --- a/pandas/tests/io/excel/test_writers.py +++ b/pandas/tests/io/excel/test_writers.py @@ -809,7 +809,7 @@ def test_to_excel_multiindex_cols(self, merge_cells, frame, path): reader, sheet_name="test1", header=header, index_col=[0, 1] ) if not merge_cells: - fm = frame.columns.format(sparsify=False, adjoin=False, names=False) + fm = frame.columns._format_multi(sparsify=False, names=False) frame.columns = [".".join(map(str, q)) for q in zip(*fm)] tm.assert_frame_equal(frame, df) From 9470bc81444a4922e3969859433948ab8c204e79 Mon Sep 17 00:00:00 2001 From: Brock Date: Fri, 6 Oct 2023 16:40:57 -0700 Subject: [PATCH 2/5] de-duplicate, change keyword --- pandas/core/indexes/base.py | 9 ++++++--- pandas/core/indexes/datetimelike.py | 19 +------------------ pandas/core/indexes/multi.py | 8 ++++---- pandas/io/formats/excel.py | 8 ++++++-- pandas/io/formats/format.py | 12 ++++++------ pandas/io/formats/html.py | 12 ++++++------ pandas/io/formats/style_render.py | 4 ++-- pandas/tests/io/excel/test_writers.py | 2 +- 8 files changed, 32 insertions(+), 42 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 181e2ec7582a4..c10c5fbb2e2ba 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -1392,23 +1392,26 @@ def format( return self._format_with_header(header=header, na_rep=na_rep) + _default_na_rep = "NaN" + + @final def _format_flat( self, *, - name: bool, + include_name: bool, ) -> list[str_t]: """ Render a string representation of the Index. """ header = [] - if name: + if include_name: header.append( pprint_thing(self.name, escape_chars=("\t", "\r", "\n")) if self.name is not None else "" ) - return self._format_with_header(header, na_rep="NaN") + return self._format_with_header(header=header, na_rep=self._default_na_rep) def _format_with_header(self, *, header: list[str_t], na_rep: str_t) -> list[str_t]: from pandas.io.formats.format import format_array diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index 09ccc6e1cd424..d9ac28c668eac 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -187,6 +187,7 @@ def _convert_tolerance(self, tolerance, target): # -------------------------------------------------------------------- # Rendering Methods + _default_na_rep = "NaT" def format( self, @@ -213,24 +214,6 @@ def format( header=header, na_rep=na_rep, date_format=date_format ) - def _format_flat( - self, - *, - name: bool, - ) -> list[str]: - """ - Render a string representation of the Index. - """ - header = [] - if name: - header.append( - ibase.pprint_thing(self.name, escape_chars=("\t", "\r", "\n")) - if self.name is not None - else "" - ) - - return self._format_with_header(header, na_rep="NaT") - def _format_with_header( self, *, header: list[str], na_rep: str, date_format: str | None = None ) -> list[str]: diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 4cf27ae681b77..64d0381f73aef 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -1447,7 +1447,7 @@ def format( def _format_multi( self, *, - names: bool, + include_names: bool, sparsify: bool | None | lib.NoDefault, ) -> list: if len(self) == 0: @@ -1458,7 +1458,7 @@ def _format_multi( na = _get_na_rep(lev.dtype) if len(lev) > 0: - formatted = lev.take(level_codes)._format_flat(name=False) + formatted = lev.take(level_codes)._format_flat(include_name=False) # we have some NA mask = level_codes == -1 @@ -1479,7 +1479,7 @@ def _format_multi( for lev, lev_name in zip(stringified_levels, self.names): level = [] - if names: + if include_names: level.append( pprint_thing(lev_name, escape_chars=("\t", "\r", "\n")) if lev_name is not None @@ -1500,7 +1500,7 @@ def _format_multi( sentinel = sparsify # little bit of a kludge job for #1217 result_levels = sparsify_labels( - result_levels, start=int(names), sentinel=sentinel + result_levels, start=int(include_names), sentinel=sentinel ) return result_levels diff --git a/pandas/io/formats/excel.py b/pandas/io/formats/excel.py index 6e4fcb7e22806..684cd4340cd2b 100644 --- a/pandas/io/formats/excel.py +++ b/pandas/io/formats/excel.py @@ -623,7 +623,9 @@ def _format_header_mi(self) -> Iterable[ExcelCell]: return columns = self.columns - level_strs = columns._format_multi(sparsify=self.merge_cells, names=False) + level_strs = columns._format_multi( + sparsify=self.merge_cells, include_names=False + ) level_lengths = get_level_lengths(level_strs) coloffset = 0 lnum = 0 @@ -811,7 +813,9 @@ def _format_hierarchical_rows(self) -> Iterable[ExcelCell]: if self.merge_cells: # Format hierarchical rows as merged cells. - level_strs = self.df.index._format_multi(sparsify=True, names=False) + level_strs = self.df.index._format_multi( + sparsify=True, include_names=False + ) level_lengths = get_level_lengths(level_strs) for spans, levels, level_codes in zip( diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 8d43ffae1eb01..7cca3935b45f1 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -317,11 +317,11 @@ def to_string(self) -> str: index = series.index have_header = _has_names(index) if isinstance(index, MultiIndex): - fmt_index = index._format_multi(names=True, sparsify=None) + fmt_index = index._format_multi(include_names=True, sparsify=None) adj = get_adjustment() fmt_index = adj.adjoin(2, *fmt_index).split("\n") else: - fmt_index = index._format_flat(name=True) + fmt_index = index._format_flat(include_name=True) fmt_values = self._get_formatted_values() if self.is_truncated_vertically: @@ -847,7 +847,7 @@ def _get_formatted_column_labels(self, frame: DataFrame) -> list[list[str]]: columns = frame.columns if isinstance(columns, MultiIndex): - fmt_columns = columns._format_multi(sparsify=False, names=False) + fmt_columns = columns._format_multi(sparsify=False, include_names=False) fmt_columns = list(zip(*fmt_columns)) dtypes = self.frame.dtypes._values @@ -872,7 +872,7 @@ def space_format(x, y): str_columns = [list(x) for x in zip(*str_columns)] else: - fmt_columns = columns._format_flat(name=False) + fmt_columns = columns._format_flat(include_name=False) dtypes = self.frame.dtypes need_leadsp = dict(zip(fmt_columns, map(is_numeric_dtype, dtypes))) str_columns = [ @@ -904,10 +904,10 @@ def _get_formatted_index(self, frame: DataFrame) -> list[str]: if isinstance(index, MultiIndex): fmt_index = index._format_multi( sparsify=self.sparsify, - names=self.show_row_idx_names, + include_names=self.show_row_idx_names, ) else: - fmt_index = [index._format_flat(name=self.show_row_idx_names)] + fmt_index = [index._format_flat(include_name=self.show_row_idx_names)] fmt_index = [ tuple( diff --git a/pandas/io/formats/html.py b/pandas/io/formats/html.py index 73dda1c07e948..794ce77b3b45e 100644 --- a/pandas/io/formats/html.py +++ b/pandas/io/formats/html.py @@ -282,7 +282,7 @@ def _write_col_header(self, indent: int) -> None: sentinel = lib.no_default else: sentinel = False - levels = self.columns._format_multi(sparsify=sentinel, names=False) + levels = self.columns._format_multi(sparsify=sentinel, include_names=False) level_lengths = get_level_lengths(levels, sentinel) inner_lvl = len(level_lengths) - 1 for lnum, (records, values) in enumerate(zip(level_lengths, levels)): @@ -438,7 +438,7 @@ def _write_regular_rows( index_values = self.fmt.tr_frame.index.map(fmt) else: # only reached with non-Multi index - index_values = self.fmt.tr_frame.index._format_flat(name=False) + index_values = self.fmt.tr_frame.index._format_flat(include_name=False) row: list[str] = [] for i in range(nrows): @@ -481,13 +481,13 @@ def _write_hierarchical_rows( nrows = len(frame) assert isinstance(frame.index, MultiIndex) - idx_values = frame.index._format_multi(sparsify=False, names=False) + idx_values = frame.index._format_multi(sparsify=False, include_names=False) idx_values = list(zip(*idx_values)) if self.fmt.sparsify: # GH3547 sentinel = lib.no_default - levels = frame.index._format_multi(sparsify=sentinel, names=False) + levels = frame.index._format_multi(sparsify=sentinel, include_names=False) level_lengths = get_level_lengths(levels, sentinel) inner_lvl = len(level_lengths) - 1 @@ -580,7 +580,7 @@ def _write_hierarchical_rows( ) idx_values = list( - zip(*frame.index._format_multi(sparsify=False, names=False)) + zip(*frame.index._format_multi(sparsify=False, include_names=False)) ) row = [] row.extend(idx_values[i]) @@ -608,7 +608,7 @@ def _get_formatted_values(self) -> dict[int, list[str]]: def _get_columns_formatted_values(self) -> list[str]: # only reached with non-Multi Index - return self.columns._format_flat(name=False) + return self.columns._format_flat(include_name=False) def write_style(self) -> None: # We use the "scoped" attribute here so that the desired diff --git a/pandas/io/formats/style_render.py b/pandas/io/formats/style_render.py index aefebe3ae2894..487ec21e77050 100644 --- a/pandas/io/formats/style_render.py +++ b/pandas/io/formats/style_render.py @@ -1652,9 +1652,9 @@ def _get_level_lengths( Result is a dictionary of (level, initial_position): span """ if isinstance(index, MultiIndex): - levels = index._format_multi(sparsify=lib.no_default, names=False) + levels = index._format_multi(sparsify=lib.no_default, include_names=False) else: - levels = index._format_flat(name=False) + levels = index._format_flat(include_name=False) if hidden_elements is None: hidden_elements = [] diff --git a/pandas/tests/io/excel/test_writers.py b/pandas/tests/io/excel/test_writers.py index bee9fa5fa8503..18af18ade85f4 100644 --- a/pandas/tests/io/excel/test_writers.py +++ b/pandas/tests/io/excel/test_writers.py @@ -809,7 +809,7 @@ def test_to_excel_multiindex_cols(self, merge_cells, frame, path): reader, sheet_name="test1", header=header, index_col=[0, 1] ) if not merge_cells: - fm = frame.columns._format_multi(sparsify=False, names=False) + fm = frame.columns._format_multi(sparsify=False, include_names=False) frame.columns = [".".join(map(str, q)) for q in zip(*fm)] tm.assert_frame_equal(frame, df) From dd1cd785cfce07c1d474d394789b1fc6253ece03 Mon Sep 17 00:00:00 2001 From: Brock Date: Sat, 7 Oct 2023 09:28:09 -0700 Subject: [PATCH 3/5] DEPR: Index.format --- doc/source/whatsnew/v2.2.0.rst | 2 + pandas/core/indexes/base.py | 8 +++ pandas/core/indexes/datetimelike.py | 10 ++++ pandas/core/indexes/multi.py | 9 +++ pandas/io/formats/format.py | 26 ++++++--- .../tests/indexes/base_class/test_formats.py | 9 ++- .../tests/indexes/categorical/test_formats.py | 5 +- .../indexes/datetimes/test_datetimelike.py | 5 +- .../tests/indexes/datetimes/test_formats.py | 8 ++- pandas/tests/indexes/multi/test_formats.py | 20 +++++-- pandas/tests/indexes/period/test_period.py | 7 ++- pandas/tests/indexes/ranges/test_range.py | 11 +++- pandas/tests/indexes/test_base.py | 12 +++- pandas/tests/indexes/test_old_base.py | 15 +++-- pandas/tests/io/formats/test_format.py | 57 +++++++++++++------ pandas/tests/series/test_repr.py | 4 +- 16 files changed, 158 insertions(+), 50 deletions(-) diff --git a/doc/source/whatsnew/v2.2.0.rst b/doc/source/whatsnew/v2.2.0.rst index 017a28ffb573a..215e6437da8d9 100644 --- a/doc/source/whatsnew/v2.2.0.rst +++ b/doc/source/whatsnew/v2.2.0.rst @@ -233,6 +233,7 @@ For example: Other Deprecations ^^^^^^^^^^^^^^^^^^ - Changed :meth:`Timedelta.resolution_string` to return ``min``, ``s``, ``ms``, ``us``, and ``ns`` instead of ``T``, ``S``, ``L``, ``U``, and ``N``, for compatibility with respective deprecations in frequency aliases (:issue:`52536`) +- Deprecated :meth:`Index.format`, use ``index.astype(str)`` or ``index.map(formatter)`` instead (:issue:`55413`) - Deprecated allowing non-keyword arguments in :meth:`DataFrame.to_clipboard`. (:issue:`54229`) - Deprecated allowing non-keyword arguments in :meth:`DataFrame.to_csv` except ``path_or_buf``. (:issue:`54229`) - Deprecated allowing non-keyword arguments in :meth:`DataFrame.to_dict`. (:issue:`54229`) @@ -258,6 +259,7 @@ Other Deprecations - Deprecated the extension test classes ``BaseNoReduceTests``, ``BaseBooleanReduceTests``, and ``BaseNumericReduceTests``, use ``BaseReduceTests`` instead (:issue:`54663`) - Deprecated the option ``mode.data_manager`` and the ``ArrayManager``; only the ``BlockManager`` will be available in future versions (:issue:`55043`) - Deprecating downcasting the results of :meth:`DataFrame.fillna`, :meth:`Series.fillna`, :meth:`DataFrame.ffill`, :meth:`Series.ffill`, :meth:`DataFrame.bfill`, :meth:`Series.bfill` in object-dtype cases. To opt in to the future version, use ``pd.set_option("future.no_silent_downcasting", True)`` (:issue:`54261`) +- .. --------------------------------------------------------------------------- .. _whatsnew_220.performance: diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index c10c5fbb2e2ba..0b8b9fccb4509 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -1379,6 +1379,14 @@ def format( """ Render a string representation of the Index. """ + warnings.warn( + # GH#55413 + f"{type(self).__name__}.format is deprecated and will be removed " + "in a future version. Convert using index.astype(str) or " + "index.map(formatter) instead.", + FutureWarning, + stacklevel=find_stack_level(), + ) header = [] if name: header.append( diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py index d9ac28c668eac..42af9556e04e1 100644 --- a/pandas/core/indexes/datetimelike.py +++ b/pandas/core/indexes/datetimelike.py @@ -14,6 +14,7 @@ cast, final, ) +import warnings import numpy as np @@ -42,6 +43,7 @@ cache_readonly, doc, ) +from pandas.util._exceptions import find_stack_level from pandas.core.dtypes.common import ( is_integer, @@ -199,6 +201,14 @@ def format( """ Render a string representation of the Index. """ + warnings.warn( + # GH#55413 + f"{type(self).__name__}.format is deprecated and will be removed " + "in a future version. Convert using index.astype(str) or " + "index.map(formatter) instead.", + FutureWarning, + stacklevel=find_stack_level(), + ) header = [] if name: header.append( diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 64d0381f73aef..520c8d54fcdf0 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -1380,6 +1380,15 @@ def format( sparsify=None, adjoin: bool = True, ) -> list: + warnings.warn( + # GH#55413 + f"{type(self).__name__}.format is deprecated and will be removed " + "in a future version. Convert using index.astype(str) or " + "index.map(formatter) instead.", + FutureWarning, + stacklevel=find_stack_level(), + ) + if name is not None: names = name diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 7cca3935b45f1..37f7f3c3c7a70 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -27,6 +27,7 @@ cast, ) from unicodedata import east_asian_width +import warnings import numpy as np @@ -891,15 +892,24 @@ def _get_formatted_index(self, frame: DataFrame) -> list[str]: fmt = self._get_formatter("__index__") if fmt is not None: - if isinstance(index, MultiIndex): - fmt_index = index.format( - sparsify=self.sparsify, - adjoin=False, - names=self.show_row_idx_names, - formatter=fmt, + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", + f"{type(index).__name__}.format is deprecated", + FutureWarning, ) - else: - fmt_index = [index.format(name=self.show_row_idx_names, formatter=fmt)] + if isinstance(index, MultiIndex): + # TODO: 2023-10-07 no tests get here + fmt_index = index.format( + sparsify=self.sparsify, + adjoin=False, + names=self.show_row_idx_names, + formatter=fmt, + ) + else: + fmt_index = [ + index.format(name=self.show_row_idx_names, formatter=fmt) + ] else: if isinstance(index, MultiIndex): fmt_index = index._format_multi( diff --git a/pandas/tests/indexes/base_class/test_formats.py b/pandas/tests/indexes/base_class/test_formats.py index 9053d45dee623..20f94010f56f8 100644 --- a/pandas/tests/indexes/base_class/test_formats.py +++ b/pandas/tests/indexes/base_class/test_formats.py @@ -4,6 +4,7 @@ import pandas._config.config as cf from pandas import Index +import pandas._testing as tm class TestIndexRendering: @@ -133,7 +134,9 @@ def test_summary_bug(self): def test_index_repr_bool_nan(self): # GH32146 arr = Index([True, False, np.nan], dtype=object) - exp1 = arr.format() + msg = "Index.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + exp1 = arr.format() out1 = ["True", "False", "NaN"] assert out1 == exp1 @@ -145,4 +148,6 @@ def test_format_different_scalar_lengths(self): # GH#35439 idx = Index(["aaaaaaaaa", "b"]) expected = ["aaaaaaaaa", "b"] - assert idx.format() == expected + msg = r"Index\.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + assert idx.format() == expected diff --git a/pandas/tests/indexes/categorical/test_formats.py b/pandas/tests/indexes/categorical/test_formats.py index 7dbcaaa8d4ba6..ea3e4ce213e67 100644 --- a/pandas/tests/indexes/categorical/test_formats.py +++ b/pandas/tests/indexes/categorical/test_formats.py @@ -4,6 +4,7 @@ import pandas._config.config as cf from pandas import CategoricalIndex +import pandas._testing as tm class TestCategoricalIndexRepr: @@ -11,7 +12,9 @@ def test_format_different_scalar_lengths(self): # GH#35439 idx = CategoricalIndex(["aaaaaaaaa", "b"]) expected = ["aaaaaaaaa", "b"] - assert idx.format() == expected + msg = r"CategoricalIndex\.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + assert idx.format() == expected def test_string_categorical_index_repr(self): # short diff --git a/pandas/tests/indexes/datetimes/test_datetimelike.py b/pandas/tests/indexes/datetimes/test_datetimelike.py index a6bee20d3d3ec..a012a2985b41c 100644 --- a/pandas/tests/indexes/datetimes/test_datetimelike.py +++ b/pandas/tests/indexes/datetimes/test_datetimelike.py @@ -1,5 +1,6 @@ """ generic tests from the Datetimelike class """ from pandas import date_range +import pandas._testing as tm class TestDatetimeIndex: @@ -7,4 +8,6 @@ def test_format(self): # GH35439 idx = date_range("20130101", periods=5) expected = [f"{x:%Y-%m-%d}" for x in idx] - assert idx.format() == expected + msg = r"DatetimeIndex\.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + assert idx.format() == expected diff --git a/pandas/tests/indexes/datetimes/test_formats.py b/pandas/tests/indexes/datetimes/test_formats.py index 502cb0407bfcd..9f42c528bb816 100644 --- a/pandas/tests/indexes/datetimes/test_formats.py +++ b/pandas/tests/indexes/datetimes/test_formats.py @@ -285,13 +285,17 @@ def test_format_with_name_time_info(self): # bug I fixed 12/20/2011 dates = pd.date_range("2011-01-01 04:00:00", periods=10, name="something") - formatted = dates.format(name=True) + msg = "DatetimeIndex.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + formatted = dates.format(name=True) assert formatted[0] == "something" def test_format_datetime_with_time(self): dti = DatetimeIndex([datetime(2012, 2, 7), datetime(2012, 2, 7, 23)]) - result = dti.format() + msg = "DatetimeIndex.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + result = dti.format() expected = ["2012-02-07 00:00:00", "2012-02-07 23:00:00"] assert len(result) == 2 assert result == expected diff --git a/pandas/tests/indexes/multi/test_formats.py b/pandas/tests/indexes/multi/test_formats.py index 011f61fac90e8..bbe94824eefa1 100644 --- a/pandas/tests/indexes/multi/test_formats.py +++ b/pandas/tests/indexes/multi/test_formats.py @@ -6,24 +6,31 @@ Index, MultiIndex, ) +import pandas._testing as tm def test_format(idx): - idx.format() - idx[:0].format() + msg = "MultiIndex.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + idx.format() + idx[:0].format() def test_format_integer_names(): index = MultiIndex( levels=[[0, 1], [0, 1]], codes=[[0, 0, 1, 1], [0, 1, 0, 1]], names=[0, 1] ) - index.format(names=True) + msg = "MultiIndex.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + index.format(names=True) def test_format_sparse_config(idx): # GH1538 + msg = "MultiIndex.format is deprecated" with pd.option_context("display.multi_sparse", False): - result = idx.format() + with tm.assert_produces_warning(FutureWarning, match=msg): + result = idx.format() assert result[1] == "foo two" @@ -37,8 +44,9 @@ def test_format_sparse_display(): [0, 0, 0, 0, 0, 0], ], ) - - result = index.format() + msg = "MultiIndex.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + result = index.format() assert result[3] == "1 0 0 0" diff --git a/pandas/tests/indexes/period/test_period.py b/pandas/tests/indexes/period/test_period.py index 0c445b4cdf770..e94b6477ca83b 100644 --- a/pandas/tests/indexes/period/test_period.py +++ b/pandas/tests/indexes/period/test_period.py @@ -275,8 +275,11 @@ def test_map(self): def test_format_empty(self): # GH35712 empty_idx = PeriodIndex([], freq="Y") - assert empty_idx.format() == [] - assert empty_idx.format(name=True) == [""] + msg = r"PeriodIndex\.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + assert empty_idx.format() == [] + with tm.assert_produces_warning(FutureWarning, match=msg): + assert empty_idx.format(name=True) == [""] def test_period_index_frequency_ME_error_message(self): msg = "Invalid frequency: 2ME" diff --git a/pandas/tests/indexes/ranges/test_range.py b/pandas/tests/indexes/ranges/test_range.py index 132704434829e..95756b04bca69 100644 --- a/pandas/tests/indexes/ranges/test_range.py +++ b/pandas/tests/indexes/ranges/test_range.py @@ -240,7 +240,9 @@ def test_cache(self): pass assert idx._cache == {} - idx.format() + msg = "RangeIndex.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + idx.format() assert idx._cache == {} df = pd.DataFrame({"a": range(10)}, index=idx) @@ -566,8 +568,11 @@ def test_engineless_lookup(self): def test_format_empty(self): # GH35712 empty_idx = RangeIndex(0) - assert empty_idx.format() == [] - assert empty_idx.format(name=True) == [""] + msg = r"RangeIndex\.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + assert empty_idx.format() == [] + with tm.assert_produces_warning(FutureWarning, match=msg): + assert empty_idx.format(name=True) == [""] @pytest.mark.parametrize( "ri", diff --git a/pandas/tests/indexes/test_base.py b/pandas/tests/indexes/test_base.py index 6afab569797f2..04ab2020b4c7a 100644 --- a/pandas/tests/indexes/test_base.py +++ b/pandas/tests/indexes/test_base.py @@ -666,13 +666,17 @@ def test_format_bug(self): # include us since the default for Timestamp shows these but Index # formatting does not we are skipping) now = datetime.now() + msg = r"Index\.format is deprecated" + if not str(now).endswith("000"): index = Index([now]) - formatted = index.format() + with tm.assert_produces_warning(FutureWarning, match=msg): + formatted = index.format() expected = [str(index[0])] assert formatted == expected - Index([]).format() + with tm.assert_produces_warning(FutureWarning, match=msg): + Index([]).format() @pytest.mark.parametrize("vals", [[1, 2.0 + 3.0j, 4.0], ["a", "b", "c"]]) def test_format_missing(self, vals, nulls_fixture): @@ -682,7 +686,9 @@ def test_format_missing(self, vals, nulls_fixture): index = Index(vals, dtype=object) # TODO: case with complex dtype? - formatted = index.format() + msg = r"Index\.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + formatted = index.format() null_repr = "NaN" if isinstance(nulls_fixture, float) else str(nulls_fixture) expected = [str(index[0]), str(index[1]), str(index[2]), null_repr] diff --git a/pandas/tests/indexes/test_old_base.py b/pandas/tests/indexes/test_old_base.py index 79dc423f12a85..32adbc693390b 100644 --- a/pandas/tests/indexes/test_old_base.py +++ b/pandas/tests/indexes/test_old_base.py @@ -559,15 +559,20 @@ def test_format(self, simple_index): pytest.skip("Tested elsewhere.") idx = simple_index expected = [str(x) for x in idx] - assert idx.format() == expected + msg = r"Index\.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + assert idx.format() == expected def test_format_empty(self, simple_index): # GH35712 if isinstance(simple_index, (PeriodIndex, RangeIndex)): pytest.skip("Tested elsewhere") empty_idx = type(simple_index)([]) - assert empty_idx.format() == [] - assert empty_idx.format(name=True) == [""] + msg = r"Index\.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + assert empty_idx.format() == [] + with tm.assert_produces_warning(FutureWarning, match=msg): + assert empty_idx.format(name=True) == [""] def test_fillna(self, index): # GH 11343 @@ -955,7 +960,9 @@ def test_format(self, simple_index): idx = simple_index max_width = max(len(str(x)) for x in idx) expected = [str(x).ljust(max_width) for x in idx] - assert idx.format() == expected + msg = r"Index\.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + assert idx.format() == expected def test_insert_non_na(self, simple_index): # GH#43921 inserting an element that we know we can hold should diff --git a/pandas/tests/io/formats/test_format.py b/pandas/tests/io/formats/test_format.py index 51965f753a0fb..52f63d97dc3a3 100644 --- a/pandas/tests/io/formats/test_format.py +++ b/pandas/tests/io/formats/test_format.py @@ -3331,9 +3331,11 @@ def test_str(self): class TestPeriodIndexFormat: def test_period_format_and_strftime_default(self): per = pd.PeriodIndex([datetime(2003, 1, 1, 12), None], freq="H") + msg = "PeriodIndex.format is deprecated" # Default formatting - formatted = per.format() + with tm.assert_produces_warning(FutureWarning, match=msg): + formatted = per.format() assert formatted[0] == "2003-01-01 12:00" # default: minutes not shown assert formatted[1] == "NaT" # format is equivalent to strftime(None)... @@ -3342,35 +3344,40 @@ def test_period_format_and_strftime_default(self): # Same test with nanoseconds freq per = pd.period_range("2003-01-01 12:01:01.123456789", periods=2, freq="ns") - formatted = per.format() + with tm.assert_produces_warning(FutureWarning, match=msg): + formatted = per.format() assert (formatted == per.strftime(None)).all() assert formatted[0] == "2003-01-01 12:01:01.123456789" assert formatted[1] == "2003-01-01 12:01:01.123456790" def test_period_custom(self): # GH#46252 custom formatting directives %l (ms) and %u (us) + msg = "PeriodIndex.format is deprecated" # 3 digits per = pd.period_range("2003-01-01 12:01:01.123", periods=2, freq="ms") - formatted = per.format(date_format="%y %I:%M:%S (ms=%l us=%u ns=%n)") + with tm.assert_produces_warning(FutureWarning, match=msg): + formatted = per.format(date_format="%y %I:%M:%S (ms=%l us=%u ns=%n)") assert formatted[0] == "03 12:01:01 (ms=123 us=123000 ns=123000000)" assert formatted[1] == "03 12:01:01 (ms=124 us=124000 ns=124000000)" # 6 digits per = pd.period_range("2003-01-01 12:01:01.123456", periods=2, freq="us") - formatted = per.format(date_format="%y %I:%M:%S (ms=%l us=%u ns=%n)") + with tm.assert_produces_warning(FutureWarning, match=msg): + formatted = per.format(date_format="%y %I:%M:%S (ms=%l us=%u ns=%n)") assert formatted[0] == "03 12:01:01 (ms=123 us=123456 ns=123456000)" assert formatted[1] == "03 12:01:01 (ms=123 us=123457 ns=123457000)" # 9 digits per = pd.period_range("2003-01-01 12:01:01.123456789", periods=2, freq="ns") - formatted = per.format(date_format="%y %I:%M:%S (ms=%l us=%u ns=%n)") + with tm.assert_produces_warning(FutureWarning, match=msg): + formatted = per.format(date_format="%y %I:%M:%S (ms=%l us=%u ns=%n)") assert formatted[0] == "03 12:01:01 (ms=123 us=123456 ns=123456789)" assert formatted[1] == "03 12:01:01 (ms=123 us=123456 ns=123456790)" def test_period_tz(self): # Formatting periods created from a datetime with timezone. - + msg = r"PeriodIndex\.format is deprecated" # This timestamp is in 2013 in Europe/Paris but is 2012 in UTC dt = pd.to_datetime(["2013-01-01 00:00:00+01:00"], utc=True) @@ -3378,13 +3385,15 @@ def test_period_tz(self): # Since tz is currently set as utc, we'll see 2012 with tm.assert_produces_warning(UserWarning, match="will drop timezone"): per = dt.to_period(freq="H") - assert per.format()[0] == "2012-12-31 23:00" + with tm.assert_produces_warning(FutureWarning, match=msg): + assert per.format()[0] == "2012-12-31 23:00" # If tz is currently set as paris before conversion, we'll see 2013 dt = dt.tz_convert("Europe/Paris") with tm.assert_produces_warning(UserWarning, match="will drop timezone"): per = dt.to_period(freq="H") - assert per.format()[0] == "2013-01-01 00:00" + with tm.assert_produces_warning(FutureWarning, match=msg): + assert per.format()[0] == "2013-01-01 00:00" @pytest.mark.parametrize( "locale_str", @@ -3411,7 +3420,9 @@ def test_period_non_ascii_fmt(self, locale_str): # Index per = pd.period_range("2003-01-01 01:00:00", periods=2, freq="12h") - formatted = per.format(date_format="%y é") + msg = "PeriodIndex.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + formatted = per.format(date_format="%y é") assert formatted[0] == "03 é" assert formatted[1] == "03 é" @@ -3443,33 +3454,45 @@ def test_period_custom_locale_directive(self, locale_str): # Index per = pd.period_range("2003-01-01 01:00:00", periods=2, freq="12h") - formatted = per.format(date_format="%y %I:%M:%S%p") + msg = "PeriodIndex.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + formatted = per.format(date_format="%y %I:%M:%S%p") assert formatted[0] == f"03 01:00:00{am_local}" assert formatted[1] == f"03 01:00:00{pm_local}" class TestDatetimeIndexFormat: def test_datetime(self): - formatted = pd.to_datetime([datetime(2003, 1, 1, 12), NaT]).format() + msg = "DatetimeIndex.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + formatted = pd.to_datetime([datetime(2003, 1, 1, 12), NaT]).format() assert formatted[0] == "2003-01-01 12:00:00" assert formatted[1] == "NaT" def test_date(self): - formatted = pd.to_datetime([datetime(2003, 1, 1), NaT]).format() + msg = "DatetimeIndex.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + formatted = pd.to_datetime([datetime(2003, 1, 1), NaT]).format() assert formatted[0] == "2003-01-01" assert formatted[1] == "NaT" def test_date_tz(self): - formatted = pd.to_datetime([datetime(2013, 1, 1)], utc=True).format() + dti = pd.to_datetime([datetime(2013, 1, 1)], utc=True) + msg = "DatetimeIndex.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + formatted = dti.format() assert formatted[0] == "2013-01-01 00:00:00+00:00" - formatted = pd.to_datetime([datetime(2013, 1, 1), NaT], utc=True).format() + dti = pd.to_datetime([datetime(2013, 1, 1), NaT], utc=True) + with tm.assert_produces_warning(FutureWarning, match=msg): + formatted = dti.format() assert formatted[0] == "2013-01-01 00:00:00+00:00" def test_date_explicit_date_format(self): - formatted = pd.to_datetime([datetime(2003, 2, 1), NaT]).format( - date_format="%m-%d-%Y", na_rep="UT" - ) + dti = pd.to_datetime([datetime(2003, 2, 1), NaT]) + msg = "DatetimeIndex.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + formatted = dti.format(date_format="%m-%d-%Y", na_rep="UT") assert formatted[0] == "02-01-2003" assert formatted[1] == "UT" diff --git a/pandas/tests/series/test_repr.py b/pandas/tests/series/test_repr.py index 535a64f303ec2..4dd5eae86c87e 100644 --- a/pandas/tests/series/test_repr.py +++ b/pandas/tests/series/test_repr.py @@ -258,7 +258,9 @@ def test_index_repr_in_frame_with_nan(self): def test_format_pre_1900_dates(self): rng = date_range("1/1/1850", "1/1/1950", freq="Y-DEC") - rng.format() + msg = "DatetimeIndex.format is deprecated" + with tm.assert_produces_warning(FutureWarning, match=msg): + rng.format() ts = Series(1, index=rng) repr(ts) From d4d91dffd1ab2047880f6b14eb69fe5509924e4e Mon Sep 17 00:00:00 2001 From: Brock Date: Mon, 9 Oct 2023 12:53:41 -0700 Subject: [PATCH 4/5] add formatter kwd --- pandas/core/indexes/base.py | 4 ++++ pandas/core/indexes/multi.py | 4 +++- pandas/io/formats/format.py | 36 +++++++++--------------------------- 3 files changed, 16 insertions(+), 28 deletions(-) diff --git a/pandas/core/indexes/base.py b/pandas/core/indexes/base.py index 932e473efe004..e8ea4729e8232 100644 --- a/pandas/core/indexes/base.py +++ b/pandas/core/indexes/base.py @@ -1398,6 +1398,7 @@ def _format_flat( self, *, include_name: bool, + formatter: Callable | None = None, ) -> list[str_t]: """ Render a string representation of the Index. @@ -1410,6 +1411,9 @@ def _format_flat( else "" ) + if formatter is not None: + return header + list(self.map(formatter)) + return self._format_with_header(header=header, na_rep=self._default_na_rep) def _format_with_header(self, *, header: list[str_t], na_rep: str_t) -> list[str_t]: diff --git a/pandas/core/indexes/multi.py b/pandas/core/indexes/multi.py index 520c8d54fcdf0..d54dbdbc07b99 100644 --- a/pandas/core/indexes/multi.py +++ b/pandas/core/indexes/multi.py @@ -1458,6 +1458,7 @@ def _format_multi( *, include_names: bool, sparsify: bool | None | lib.NoDefault, + formatter: Callable | None = None, ) -> list: if len(self) == 0: return [] @@ -1467,7 +1468,8 @@ def _format_multi( na = _get_na_rep(lev.dtype) if len(lev) > 0: - formatted = lev.take(level_codes)._format_flat(include_name=False) + taken = formatted = lev.take(level_codes) + formatted = taken._format_flat(include_name=False, formatter=formatter) # we have some NA mask = level_codes == -1 diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 37f7f3c3c7a70..e4b7833c0d53f 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -27,7 +27,6 @@ cast, ) from unicodedata import east_asian_width -import warnings import numpy as np @@ -891,33 +890,16 @@ def _get_formatted_index(self, frame: DataFrame) -> list[str]: columns = frame.columns fmt = self._get_formatter("__index__") - if fmt is not None: - with warnings.catch_warnings(): - warnings.filterwarnings( - "ignore", - f"{type(index).__name__}.format is deprecated", - FutureWarning, - ) - if isinstance(index, MultiIndex): - # TODO: 2023-10-07 no tests get here - fmt_index = index.format( - sparsify=self.sparsify, - adjoin=False, - names=self.show_row_idx_names, - formatter=fmt, - ) - else: - fmt_index = [ - index.format(name=self.show_row_idx_names, formatter=fmt) - ] + if isinstance(index, MultiIndex): + fmt_index = index._format_multi( + sparsify=self.sparsify, + include_names=self.show_row_idx_names, + formatter=fmt, + ) else: - if isinstance(index, MultiIndex): - fmt_index = index._format_multi( - sparsify=self.sparsify, - include_names=self.show_row_idx_names, - ) - else: - fmt_index = [index._format_flat(include_name=self.show_row_idx_names)] + fmt_index = [ + index._format_flat(include_name=self.show_row_idx_names, formatter=fmt) + ] fmt_index = [ tuple( From 4f3b4db8ca3c5edbb77136588ff69a96deba8403 Mon Sep 17 00:00:00 2001 From: Brock Date: Tue, 10 Oct 2023 11:00:48 -0700 Subject: [PATCH 5/5] Post-merge fixup --- pandas/io/formats/format.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index fce6fa85bb5b1..9d75f4cf3ab49 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -316,7 +316,7 @@ def to_string(self) -> str: have_header = _has_names(index) if isinstance(index, MultiIndex): fmt_index = index._format_multi(include_names=True, sparsify=None) - adj = get_adjustment() + adj = printing.get_adjustment() fmt_index = adj.adjoin(2, *fmt_index).split("\n") else: fmt_index = index._format_flat(include_name=True)