From b0d629662113316f3b09fde81712c71987f1d1aa Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Thu, 17 Sep 2020 21:54:43 +0700 Subject: [PATCH 01/75] REF: extract formatters property --- pandas/io/formats/format.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 77f2a53fc7fab..ce0fe1bae94d6 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -572,15 +572,7 @@ def __init__( self.sparsify = sparsify self.float_format = float_format - if formatters is None: - self.formatters = {} - elif len(frame.columns) == len(formatters) or isinstance(formatters, dict): - self.formatters = formatters - else: - raise ValueError( - f"Formatters length({len(formatters)}) should match " - f"DataFrame number of columns({len(frame.columns)})" - ) + self.formatters = formatters self.na_rep = na_rep self.decimal = decimal if col_space is None: @@ -631,6 +623,22 @@ def __init__( self._chk_truncate() self.adj = get_adjustment() + @property + def formatters(self): + return self._formatters + + @formatters.setter + def formatters(self, formatters): + if formatters is None: + self._formatters = {} + elif len(self.frame.columns) == len(formatters) or isinstance(formatters, dict): + self._formatters = formatters + else: + raise ValueError( + f"Formatters length({len(formatters)}) should match " + f"DataFrame number of columns({len(self.frame.columns)})" + ) + def _chk_truncate(self) -> None: """ Checks whether the frame should be truncated. If so, slices @@ -701,7 +709,7 @@ def _chk_truncate(self) -> None: # truncate formatter if isinstance(self.formatters, (list, tuple)): truncate_fmt = self.formatters - self.formatters = [ + self._formatters = [ *truncate_fmt[:col_num], *truncate_fmt[-col_num:], ] From 8e7a65f05674b4e0cfb2c710a236232cd4af9079 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Thu, 17 Sep 2020 21:56:07 +0700 Subject: [PATCH 02/75] REF: extract justify property --- pandas/io/formats/format.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index ce0fe1bae94d6..2059ae40c80d9 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -605,12 +605,7 @@ def __init__( self.show_dimensions = show_dimensions self.table_id = table_id self.render_links = render_links - - if justify is None: - self.justify = get_option("display.colheader_justify") - else: - self.justify = justify - + self.justify = justify self.bold_rows = bold_rows self.escape = escape @@ -639,6 +634,17 @@ def formatters(self, formatters): f"DataFrame number of columns({len(self.frame.columns)})" ) + @property + def justify(self): + return self._justify + + @justify.setter + def justify(self, justify): + if justify is None: + self._justify = get_option("display.colheader_justify") + else: + self._justify = justify + def _chk_truncate(self) -> None: """ Checks whether the frame should be truncated. If so, slices From 9e741ddae517bc5d1026a742725b3a02e2cc135f Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Thu, 17 Sep 2020 21:59:29 +0700 Subject: [PATCH 03/75] REF: extract columns property --- pandas/io/formats/format.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 2059ae40c80d9..cf12819a6dc35 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -608,13 +608,7 @@ def __init__( self.justify = justify self.bold_rows = bold_rows self.escape = escape - - if columns is not None: - self.columns = ensure_index(columns) - self.frame = self.frame[self.columns] - else: - self.columns = frame.columns - + self.columns = columns self._chk_truncate() self.adj = get_adjustment() @@ -645,6 +639,18 @@ def justify(self, justify): else: self._justify = justify + @property + def columns(self): + return self._columns + + @columns.setter + def columns(self, columns): + if columns is not None: + self._columns = ensure_index(columns) + self.frame = self.frame[self._columns] + else: + self._columns = self.frame.columns + def _chk_truncate(self) -> None: """ Checks whether the frame should be truncated. If so, slices From a222616359e805e90e9f548dbf4e65278439fc38 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Thu, 17 Sep 2020 23:08:13 +0700 Subject: [PATCH 04/75] REF: extract col_space property --- pandas/io/formats/format.py | 47 +++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index cf12819a6dc35..282c9f672afee 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -575,26 +575,7 @@ def __init__( self.formatters = formatters self.na_rep = na_rep self.decimal = decimal - if col_space is None: - self.col_space = {} - elif isinstance(col_space, (int, str)): - self.col_space = {"": col_space} - self.col_space.update({column: col_space for column in self.frame.columns}) - elif isinstance(col_space, Mapping): - for column in col_space.keys(): - if column not in self.frame.columns and column != "": - raise ValueError( - f"Col_space is defined for an unknown column: {column}" - ) - self.col_space = col_space - else: - if len(frame.columns) != len(col_space): - raise ValueError( - f"Col_space length({len(col_space)}) should match " - f"DataFrame number of columns({len(frame.columns)})" - ) - self.col_space = dict(zip(self.frame.columns, col_space)) - + self.col_space = col_space self.header = header self.index = index self.line_width = line_width @@ -651,6 +632,32 @@ def columns(self, columns): else: self._columns = self.frame.columns + @property + def col_space(self): + return self._col_space + + @col_space.setter + def col_space(self, col_space): + if col_space is None: + self._col_space = {} + elif isinstance(col_space, (int, str)): + self._col_space = {"": col_space} + self._col_space.update({column: col_space for column in self.frame.columns}) + elif isinstance(col_space, Mapping): + for column in col_space.keys(): + if column not in self.frame.columns and column != "": + raise ValueError( + f"Col_space is defined for an unknown column: {column}" + ) + self._col_space = col_space + else: + if len(self.frame.columns) != len(col_space): + raise ValueError( + f"Col_space length({len(col_space)}) should match " + f"DataFrame number of columns({len(self.frame.columns)})" + ) + self._col_space = dict(zip(self.frame.columns, col_space)) + def _chk_truncate(self) -> None: """ Checks whether the frame should be truncated. If so, slices From 4bbd37074df28252f1f6623919dab4ffcdaab04d Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Thu, 17 Sep 2020 23:13:42 +0700 Subject: [PATCH 05/75] REF: extract sparsify property --- pandas/io/formats/format.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 282c9f672afee..39f3fb7bb59de 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -565,12 +565,7 @@ def __init__( ): self.frame = frame self.show_index_names = index_names - - if sparsify is None: - sparsify = get_option("display.multi_sparse") - self.sparsify = sparsify - self.float_format = float_format self.formatters = formatters self.na_rep = na_rep @@ -593,6 +588,17 @@ def __init__( self._chk_truncate() self.adj = get_adjustment() + @property + def sparsify(self): + return self._sparsify + + @sparsify.setter + def sparsify(self, sparsify): + if sparsify is None: + self._sparsify = get_option("display.multi_sparse") + else: + self._sparsify = sparsify + @property def formatters(self): return self._formatters From 6c51ca4eceefead8568b267fb24738fbe190c483 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Thu, 17 Sep 2020 23:14:55 +0700 Subject: [PATCH 06/75] REF: extract max_rows_displayed property --- pandas/io/formats/format.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 39f3fb7bb59de..7ead90e89444a 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -577,7 +577,6 @@ def __init__( self.max_rows = max_rows self.min_rows = min_rows self.max_cols = max_cols - self.max_rows_displayed = min(max_rows or len(self.frame), len(self.frame)) self.show_dimensions = show_dimensions self.table_id = table_id self.render_links = render_links @@ -664,6 +663,10 @@ def col_space(self, col_space): ) self._col_space = dict(zip(self.frame.columns, col_space)) + @property + def max_rows_displayed(self): + return min(self.max_rows or len(self.frame), len(self.frame)) + def _chk_truncate(self) -> None: """ Checks whether the frame should be truncated. If so, slices From ad615218f727dc9f5b4973fa14987e3af6c66f59 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Thu, 17 Sep 2020 23:17:25 +0700 Subject: [PATCH 07/75] CLN: eliminate 'w' and 'h' attributes --- pandas/io/formats/format.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 7ead90e89444a..9c6dfcc3468a7 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -681,9 +681,7 @@ def _chk_truncate(self) -> None: max_rows_adj: Optional[int] if max_cols == 0 or max_rows == 0: # assume we are in the terminal - (w, h) = get_terminal_size() - self.w = w - self.h = h + (width, height) = get_terminal_size() if self.max_rows == 0: dot_row = 1 prompt_row = 1 @@ -694,15 +692,15 @@ def _chk_truncate(self) -> None: self.header = cast(bool, self.header) n_add_rows = self.header + dot_row + show_dimension_rows + prompt_row # rows available to fill with actual data - max_rows_adj = self.h - n_add_rows + max_rows_adj = height - n_add_rows self.max_rows_adj = max_rows_adj # Format only rows and columns that could potentially fit the # screen - if max_cols == 0 and len(self.frame.columns) > w: - max_cols = w - if max_rows == 0 and len(self.frame) > h: - max_rows = h + if max_cols == 0 and len(self.frame.columns) > width: + max_cols = width + if max_rows == 0 and len(self.frame) > height: + max_rows = height if not hasattr(self, "max_rows_adj"): if max_rows: @@ -880,7 +878,8 @@ def write_result(self, buf: IO[str]) -> None: lines = self.adj.adjoin(1, *strcols).split("\n") max_len = Series(lines).str.len().max() # plus truncate dot col - dif = max_len - self.w + width, _ = get_terminal_size() + dif = max_len - width # '+ 1' to avoid too wide repr (GH PR #17023) adj_dif = dif + 1 col_lens = Series([Series(ele).apply(len).max() for ele in strcols]) From 21be89b512197ebaada00f00c35fc1d7f2f140ad Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 00:39:57 +0700 Subject: [PATCH 08/75] TYP: annotate col_space --- pandas/io/formats/format.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 9c6dfcc3468a7..7174a46a93303 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -537,8 +537,6 @@ class DataFrameFormatter(TableFormatter): __doc__ = __doc__ if __doc__ else "" __doc__ += common_docstring + return_docstring - col_space: ColspaceType - def __init__( self, frame: "DataFrame", @@ -638,11 +636,12 @@ def columns(self, columns): self._columns = self.frame.columns @property - def col_space(self): + def col_space(self) -> ColspaceType: return self._col_space @col_space.setter - def col_space(self, col_space): + def col_space(self, col_space: Optional[ColspaceArgType]) -> None: + self._col_space: ColspaceType if col_space is None: self._col_space = {} elif isinstance(col_space, (int, str)): @@ -662,6 +661,7 @@ def col_space(self, col_space): f"DataFrame number of columns({len(self.frame.columns)})" ) self._col_space = dict(zip(self.frame.columns, col_space)) + assert self._col_space is not None @property def max_rows_displayed(self): From df77b0d772e857d5e4ea21b61f1495c75d2bf301 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 00:40:18 +0700 Subject: [PATCH 09/75] TYP: annotate formatters --- pandas/io/formats/format.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 7174a46a93303..bd57b60a7e8dc 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -597,11 +597,12 @@ def sparsify(self, sparsify): self._sparsify = sparsify @property - def formatters(self): + def formatters(self) -> FormattersType: return self._formatters @formatters.setter - def formatters(self, formatters): + def formatters(self, formatters: Optional[FormattersType]) -> None: + self._formatters: FormattersType if formatters is None: self._formatters = {} elif len(self.frame.columns) == len(formatters) or isinstance(formatters, dict): @@ -611,6 +612,7 @@ def formatters(self, formatters): f"Formatters length({len(formatters)}) should match " f"DataFrame number of columns({len(self.frame.columns)})" ) + assert self._formatters is not None @property def justify(self): From 2126f1bd93fa95266d1b34428c8ecc9fa46b1c48 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 00:40:43 +0700 Subject: [PATCH 10/75] TYP: annotate columns --- pandas/io/formats/format.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index bd57b60a7e8dc..ae2a9b0e5b5ba 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -626,16 +626,17 @@ def justify(self, justify): self._justify = justify @property - def columns(self): + def columns(self) -> Index: return self._columns @columns.setter - def columns(self, columns): + def columns(self, columns: Optional[Sequence[str]]) -> None: if columns is not None: self._columns = ensure_index(columns) self.frame = self.frame[self._columns] else: self._columns = self.frame.columns + assert self._columns is not None @property def col_space(self) -> ColspaceType: From 194791453ca45c5592df1d3644cc534a90c7874e Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 00:40:57 +0700 Subject: [PATCH 11/75] TYP: annotate max_rows_displayed --- 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 ae2a9b0e5b5ba..2ee131b2ebd04 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -667,7 +667,7 @@ def col_space(self, col_space: Optional[ColspaceArgType]) -> None: assert self._col_space is not None @property - def max_rows_displayed(self): + def max_rows_displayed(self) -> int: return min(self.max_rows or len(self.frame), len(self.frame)) def _chk_truncate(self) -> None: From 3496e974b106ed11f1a0b003c3e21cff0eb67db9 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 00:43:04 +0700 Subject: [PATCH 12/75] TYP: add formatters property in superclass To ensure that mypy does not raise override error --- pandas/io/formats/format.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 2ee131b2ebd04..1db7a656e23cf 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -452,7 +452,15 @@ class TableFormatter: show_dimensions: Union[bool, str] is_truncated: bool - formatters: FormattersType + + @property + def formatters(self) -> FormattersType: + return self._formatters + + @formatters.setter + def formatters(self, formatters: FormattersType) -> None: + self._formatters: FormattersType = formatters + columns: Index @property From 2de23a0046c9ab61cc8078450b3ca828cec2e9e9 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 00:45:44 +0700 Subject: [PATCH 13/75] TYP: add columns property in superclass To ensure that mypy does not raise override error --- pandas/io/formats/format.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 1db7a656e23cf..e737b34e903a3 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -461,7 +461,13 @@ def formatters(self) -> FormattersType: def formatters(self, formatters: FormattersType) -> None: self._formatters: FormattersType = formatters - columns: Index + @property + def columns(self) -> Index: + return self._columns + + @columns.setter + def columns(self, columns: Index) -> None: + self._columns: Index = columns @property def should_show_dimensions(self) -> bool: From 84863fbacb6caf87be71f68850cfa6b46267decd Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 00:49:18 +0700 Subject: [PATCH 14/75] REF: extract method _is_in_terminal --- pandas/io/formats/format.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index e737b34e903a3..76a409ac686fb 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -684,6 +684,10 @@ def col_space(self, col_space: Optional[ColspaceArgType]) -> None: def max_rows_displayed(self) -> int: return min(self.max_rows or len(self.frame), len(self.frame)) + def _is_in_terminal(self) -> bool: + """Check if the output is to be shown in terminal.""" + return bool(self.max_cols == 0 or self.max_rows == 0) + def _chk_truncate(self) -> None: """ Checks whether the frame should be truncated. If so, slices @@ -697,7 +701,7 @@ def _chk_truncate(self) -> None: self.max_rows_adj: Optional[int] max_rows_adj: Optional[int] - if max_cols == 0 or max_rows == 0: # assume we are in the terminal + if self._is_in_terminal(): (width, height) = get_terminal_size() if self.max_rows == 0: dot_row = 1 From f54637856c69c0435b06c2b7a599a742c1126f91 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 01:03:49 +0700 Subject: [PATCH 15/75] REF: extract max_cols_adj property --- pandas/io/formats/format.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 76a409ac686fb..6cf2ac39018fe 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -688,6 +688,18 @@ def _is_in_terminal(self) -> bool: """Check if the output is to be shown in terminal.""" return bool(self.max_cols == 0 or self.max_rows == 0) + @property + def max_cols_adj(self) -> int: + try: + return self._max_cols_adj + except AttributeError: + width, _ = get_terminal_size() + if self.max_cols == 0 and len(self.frame.columns) > width: + self._max_cols_adj = width + else: + self._max_cols_adj = self.max_cols + return self._max_cols_adj + def _chk_truncate(self) -> None: """ Checks whether the frame should be truncated. If so, slices @@ -718,8 +730,6 @@ def _chk_truncate(self) -> None: # Format only rows and columns that could potentially fit the # screen - if max_cols == 0 and len(self.frame.columns) > width: - max_cols = width if max_rows == 0 and len(self.frame) > height: max_rows = height @@ -729,8 +739,6 @@ def _chk_truncate(self) -> None: # if truncated, set max_rows showed to min_rows max_rows = min(self.min_rows, max_rows) self.max_rows_adj = max_rows - if not hasattr(self, "max_cols_adj"): - self.max_cols_adj = max_cols max_cols_adj = self.max_cols_adj max_rows_adj = self.max_rows_adj @@ -919,7 +927,7 @@ def write_result(self, buf: IO[str]) -> None: max_cols_adj = n_cols - self.index # GH-21180. Ensure that we print at least two. max_cols_adj = max(max_cols_adj, 2) - self.max_cols_adj = max_cols_adj + self._max_cols_adj = max_cols_adj # Call again _chk_truncate to cut frame appropriately # and then generate string representation From bb01b8faaf3996c61b196aaf97dbf8e31ef0339b Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 01:17:21 +0700 Subject: [PATCH 16/75] REF: extract max_rows_adj property --- pandas/io/formats/format.py | 49 ++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 6cf2ac39018fe..5009cfbe8a989 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -689,7 +689,8 @@ def _is_in_terminal(self) -> bool: return bool(self.max_cols == 0 or self.max_rows == 0) @property - def max_cols_adj(self) -> int: + def max_cols_adj(self) -> Optional[int]: + """Number of columns fitting the screen.""" try: return self._max_cols_adj except AttributeError: @@ -700,18 +701,10 @@ def max_cols_adj(self) -> int: self._max_cols_adj = self.max_cols return self._max_cols_adj - def _chk_truncate(self) -> None: - """ - Checks whether the frame should be truncated. If so, slices - the frame up. - """ - from pandas.core.reshape.concat import concat - - # Cut the data to the information actually printed - max_cols = self.max_cols + @property + def max_rows_adj(self) -> Optional[int]: + """Number of rows fitting the screen.""" max_rows = self.max_rows - self.max_rows_adj: Optional[int] - max_rows_adj: Optional[int] if self._is_in_terminal(): (width, height) = get_terminal_size() @@ -725,20 +718,27 @@ def _chk_truncate(self) -> None: self.header = cast(bool, self.header) n_add_rows = self.header + dot_row + show_dimension_rows + prompt_row # rows available to fill with actual data - max_rows_adj = height - n_add_rows - self.max_rows_adj = max_rows_adj + return height - n_add_rows - # Format only rows and columns that could potentially fit the - # screen - if max_rows == 0 and len(self.frame) > height: + if self.max_rows == 0 and len(self.frame) > height: max_rows = height - if not hasattr(self, "max_rows_adj"): - if max_rows: - if (len(self.frame) > max_rows) and self.min_rows: - # if truncated, set max_rows showed to min_rows - max_rows = min(self.min_rows, max_rows) - self.max_rows_adj = max_rows + if max_rows: + if (len(self.frame) > max_rows) and self.min_rows: + # if truncated, set max_rows showed to min_rows + max_rows = min(self.min_rows, max_rows) + return max_rows + + def _chk_truncate(self) -> None: + """ + Checks whether the frame should be truncated. If so, slices + the frame up. + """ + from pandas.core.reshape.concat import concat + + # Cut the data to the information actually printed + max_cols = self.max_cols + max_rows = self.max_rows max_cols_adj = self.max_cols_adj max_rows_adj = self.max_rows_adj @@ -957,8 +957,7 @@ def _join_multiline(self, *args) -> str: nbins = len(col_bins) if self.truncate_v: - # cast here since if truncate_v is True, max_rows_adj is not None - self.max_rows_adj = cast(int, self.max_rows_adj) + assert self.max_rows_adj is not None nrows = self.max_rows_adj + 1 else: nrows = len(self.frame) From bffd773dd0f8765bd4be73ad82fe8ca9e9c3e8d7 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 01:22:55 +0700 Subject: [PATCH 17/75] REF: extract _is_screen_narrow/short methods --- pandas/io/formats/format.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 5009cfbe8a989..0320ce687ae58 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -695,12 +695,15 @@ def max_cols_adj(self) -> Optional[int]: return self._max_cols_adj except AttributeError: width, _ = get_terminal_size() - if self.max_cols == 0 and len(self.frame.columns) > width: + if self._is_screen_narrow(width): self._max_cols_adj = width else: self._max_cols_adj = self.max_cols return self._max_cols_adj + def _is_screen_narrow(self, max_width) -> bool: + return bool(self.max_cols == 0 and len(self.frame.columns) > max_width) + @property def max_rows_adj(self) -> Optional[int]: """Number of rows fitting the screen.""" @@ -720,7 +723,7 @@ def max_rows_adj(self) -> Optional[int]: # rows available to fill with actual data return height - n_add_rows - if self.max_rows == 0 and len(self.frame) > height: + if self._is_screen_short(height): max_rows = height if max_rows: @@ -729,6 +732,9 @@ def max_rows_adj(self) -> Optional[int]: max_rows = min(self.min_rows, max_rows) return max_rows + def _is_screen_short(self, max_height) -> bool: + return bool(self.max_rows == 0 and len(self.frame) > max_height) + def _chk_truncate(self) -> None: """ Checks whether the frame should be truncated. If so, slices From 1f955b9f70f49bc044cc90a0cdcbc856e39e23a8 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 01:26:02 +0700 Subject: [PATCH 18/75] REF: simplify logic in max_rows_adj --- pandas/io/formats/format.py | 38 +++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 0320ce687ae58..c72aa474d2a1b 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -707,24 +707,26 @@ def _is_screen_narrow(self, max_width) -> bool: @property def max_rows_adj(self) -> Optional[int]: """Number of rows fitting the screen.""" - max_rows = self.max_rows - - if self._is_in_terminal(): - (width, height) = get_terminal_size() - if self.max_rows == 0: - dot_row = 1 - prompt_row = 1 - if self.show_dimensions: - show_dimension_rows = 3 - # assume we only get here if self.header is boolean. - # i.e. not to_latex() where self.header may be List[str] - self.header = cast(bool, self.header) - n_add_rows = self.header + dot_row + show_dimension_rows + prompt_row - # rows available to fill with actual data - return height - n_add_rows - - if self._is_screen_short(height): - max_rows = height + if not self._is_in_terminal(): + return self.max_rows + + (width, height) = get_terminal_size() + if self.max_rows == 0: + dot_row = 1 + prompt_row = 1 + if self.show_dimensions: + show_dimension_rows = 3 + # assume we only get here if self.header is boolean. + # i.e. not to_latex() where self.header may be List[str] + self.header = cast(bool, self.header) + n_add_rows = self.header + dot_row + show_dimension_rows + prompt_row + # rows available to fill with actual data + return height - n_add_rows + + if self._is_screen_short(height): + max_rows = height + else: + max_rows = self.max_rows if max_rows: if (len(self.frame) > max_rows) and self.min_rows: From 6386a6c944eaa29873d319f07af0d340ee9c4b7f Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 01:30:37 +0700 Subject: [PATCH 19/75] REF: check for terminal in max_cols_adj This is done for consistency with max_rows_adj --- pandas/io/formats/format.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index c72aa474d2a1b..03712fcd88878 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -694,6 +694,10 @@ def max_cols_adj(self) -> Optional[int]: try: return self._max_cols_adj except AttributeError: + if not self._is_in_terminal(): + self._max_cols_adj = self.max_cols + return self._max_cols_adj + width, _ = get_terminal_size() if self._is_screen_narrow(width): self._max_cols_adj = width From 17e545c5ab084bd8db6436f1cb14ba88ddc37e66 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 01:34:15 +0700 Subject: [PATCH 20/75] REF: extract truncate_h/v and is_truncated props --- pandas/io/formats/format.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 03712fcd88878..251f926151a57 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -741,6 +741,18 @@ def max_rows_adj(self) -> Optional[int]: def _is_screen_short(self, max_height) -> bool: return bool(self.max_rows == 0 and len(self.frame) > max_height) + @property + def truncate_h(self) -> bool: + return bool(self.max_cols_adj and (len(self.columns) > self.max_cols_adj)) + + @property + def truncate_v(self) -> bool: + return bool(self.max_rows_adj and (len(self.frame) > self.max_rows_adj)) + + @property + def is_truncated(self) -> bool: + return bool(self.truncate_h or self.truncate_v) + def _chk_truncate(self) -> None: """ Checks whether the frame should be truncated. If so, slices @@ -755,11 +767,8 @@ def _chk_truncate(self) -> None: max_cols_adj = self.max_cols_adj max_rows_adj = self.max_rows_adj - truncate_h = max_cols_adj and (len(self.columns) > max_cols_adj) - truncate_v = max_rows_adj and (len(self.frame) > max_rows_adj) - frame = self.frame - if truncate_h: + if self.truncate_h: # cast here since if truncate_h is True, max_cols_adj is not None max_cols_adj = cast(int, max_cols_adj) if max_cols_adj == 0: @@ -781,7 +790,7 @@ def _chk_truncate(self) -> None: *truncate_fmt[-col_num:], ] self.tr_col_num = col_num - if truncate_v: + if self.truncate_v: # cast here since if truncate_v is True, max_rows_adj is not None max_rows_adj = cast(int, max_rows_adj) if max_rows_adj == 1: @@ -795,9 +804,6 @@ def _chk_truncate(self) -> None: self.tr_row_num = None self.tr_frame = frame - self.truncate_h = truncate_h - self.truncate_v = truncate_v - self.is_truncated = bool(self.truncate_h or self.truncate_v) def _to_str_columns(self) -> List[List[str]]: """ From 99035c3a78b5d9878072c3e702db3aabb107f2c3 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 01:36:25 +0700 Subject: [PATCH 21/75] REF: truncate_h -> is_truncated_horizontally --- pandas/io/formats/format.py | 15 ++++++++------- pandas/io/formats/html.py | 20 ++++++++++---------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 251f926151a57..fec82bc761f45 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -742,7 +742,7 @@ def _is_screen_short(self, max_height) -> bool: return bool(self.max_rows == 0 and len(self.frame) > max_height) @property - def truncate_h(self) -> bool: + def is_truncated_horizontally(self) -> bool: return bool(self.max_cols_adj and (len(self.columns) > self.max_cols_adj)) @property @@ -751,7 +751,7 @@ def truncate_v(self) -> bool: @property def is_truncated(self) -> bool: - return bool(self.truncate_h or self.truncate_v) + return bool(self.is_truncated_horizontally or self.truncate_v) def _chk_truncate(self) -> None: """ @@ -768,8 +768,9 @@ def _chk_truncate(self) -> None: max_rows_adj = self.max_rows_adj frame = self.frame - if self.truncate_h: - # cast here since if truncate_h is True, max_cols_adj is not None + if self.is_truncated_horizontally: + # cast here since if is_truncated_horizontally is True + # max_cols_adj is not None max_cols_adj = cast(int, max_cols_adj) if max_cols_adj == 0: col_num = len(frame.columns) @@ -863,10 +864,10 @@ def _to_str_columns(self) -> List[List[str]]: strcols.insert(0, str_index) # Add ... to signal truncated - truncate_h = self.truncate_h + is_truncated_horizontally = self.is_truncated_horizontally truncate_v = self.truncate_v - if truncate_h: + if is_truncated_horizontally: col_num = self.tr_col_num strcols.insert(self.tr_col_num + 1, [" ..."] * (len(str_index))) if truncate_v: @@ -878,7 +879,7 @@ def _to_str_columns(self) -> List[List[str]]: # infer from above row cwidth = self.adj.len(strcols[ix][row_num]) is_dot_col = False - if truncate_h: + if is_truncated_horizontally: is_dot_col = ix == col_num + 1 if cwidth > 3 or is_dot_col: my_str = "..." diff --git a/pandas/io/formats/html.py b/pandas/io/formats/html.py index c89189f1e679a..97d72400545b6 100644 --- a/pandas/io/formats/html.py +++ b/pandas/io/formats/html.py @@ -236,7 +236,7 @@ def _write_table(self, indent: int = 0) -> None: self.write("", indent) def _write_col_header(self, indent: int) -> None: - truncate_h = self.fmt.truncate_h + is_truncated_horizontally = self.fmt.is_truncated_horizontally if isinstance(self.columns, MultiIndex): template = 'colspan="{span:d}" halign="left"' @@ -249,7 +249,7 @@ def _write_col_header(self, indent: int) -> None: level_lengths = get_level_lengths(levels, sentinel) inner_lvl = len(level_lengths) - 1 for lnum, (records, values) in enumerate(zip(level_lengths, levels)): - if truncate_h: + if is_truncated_horizontally: # modify the header lines ins_col = self.fmt.tr_col_num if self.fmt.sparsify: @@ -346,16 +346,16 @@ def _write_col_header(self, indent: int) -> None: row.extend(self._get_columns_formatted_values()) align = self.fmt.justify - if truncate_h: + if is_truncated_horizontally: ins_col = self.row_levels + self.fmt.tr_col_num row.insert(ins_col, "...") self.write_tr(row, indent, self.indent_delta, header=True, align=align) def _write_row_header(self, indent: int) -> None: - truncate_h = self.fmt.truncate_h + is_truncated_horizontally = self.fmt.is_truncated_horizontally row = [x if x is not None else "" for x in self.frame.index.names] + [""] * ( - self.ncols + (1 if truncate_h else 0) + self.ncols + (1 if is_truncated_horizontally else 0) ) self.write_tr(row, indent, self.indent_delta, header=True) @@ -390,7 +390,7 @@ def _write_body(self, indent: int) -> None: def _write_regular_rows( self, fmt_values: Mapping[int, List[str]], indent: int ) -> None: - truncate_h = self.fmt.truncate_h + is_truncated_horizontally = self.fmt.is_truncated_horizontally truncate_v = self.fmt.truncate_v nrows = len(self.fmt.tr_frame) @@ -426,7 +426,7 @@ def _write_regular_rows( row.append("") row.extend(fmt_values[j][i] for j in range(self.ncols)) - if truncate_h: + if is_truncated_horizontally: dot_col_ix = self.fmt.tr_col_num + self.row_levels row.insert(dot_col_ix, "...") self.write_tr( @@ -438,7 +438,7 @@ def _write_hierarchical_rows( ) -> None: template = 'rowspan="{span}" valign="top"' - truncate_h = self.fmt.truncate_h + is_truncated_horizontally = self.fmt.is_truncated_horizontally truncate_v = self.fmt.truncate_v frame = self.fmt.tr_frame nrows = len(frame) @@ -520,7 +520,7 @@ def _write_hierarchical_rows( row.append(v) row.extend(fmt_values[j][i] for j in range(self.ncols)) - if truncate_h: + if is_truncated_horizontally: row.insert( self.row_levels - sparse_offset + self.fmt.tr_col_num, "..." ) @@ -550,7 +550,7 @@ def _write_hierarchical_rows( row = [] row.extend(idx_values[i]) row.extend(fmt_values[j][i] for j in range(self.ncols)) - if truncate_h: + if is_truncated_horizontally: row.insert(self.row_levels + self.fmt.tr_col_num, "...") self.write_tr( row, From e16fc9d736e5cc8877e6ee76dae9dd8bdb2abb68 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 01:37:37 +0700 Subject: [PATCH 22/75] REF: truncate_v -> is_truncated_vertically --- pandas/io/formats/format.py | 30 +++++++++++++++++------------- pandas/io/formats/html.py | 12 ++++++------ 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index fec82bc761f45..2875527a0fca7 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -269,9 +269,9 @@ def _chk_truncate(self) -> None: max_rows = self.max_rows # truncation determined by max_rows, actual truncated number of rows # used below by min_rows - truncate_v = max_rows and (len(self.series) > max_rows) + is_truncated_vertically = max_rows and (len(self.series) > max_rows) series = self.series - if truncate_v: + if is_truncated_vertically: max_rows = cast(int, max_rows) if min_rows: # if min_rows is set (not None or 0), set max_rows to minimum @@ -287,7 +287,7 @@ def _chk_truncate(self) -> None: else: self.tr_row_num = None self.tr_series = series - self.truncate_v = truncate_v + self.is_truncated_vertically = is_truncated_vertically def _get_footer(self) -> str: name = self.series.name @@ -306,7 +306,9 @@ def _get_footer(self) -> str: series_name = pprint_thing(name, escape_chars=("\t", "\r", "\n")) footer += f"Name: {series_name}" - if self.length is True or (self.length == "truncate" and self.truncate_v): + if self.length is True or ( + self.length == "truncate" and self.is_truncated_vertically + ): if footer: footer += ", " footer += f"Length: {len(self.series)}" @@ -358,7 +360,7 @@ def to_string(self) -> str: fmt_index, have_header = self._get_formatted_index() fmt_values = self._get_formatted_values() - if self.truncate_v: + if self.is_truncated_vertically: n_header_rows = 0 row_num = self.tr_row_num row_num = cast(int, row_num) @@ -746,12 +748,12 @@ def is_truncated_horizontally(self) -> bool: return bool(self.max_cols_adj and (len(self.columns) > self.max_cols_adj)) @property - def truncate_v(self) -> bool: + def is_truncated_vertically(self) -> bool: return bool(self.max_rows_adj and (len(self.frame) > self.max_rows_adj)) @property def is_truncated(self) -> bool: - return bool(self.is_truncated_horizontally or self.truncate_v) + return bool(self.is_truncated_horizontally or self.is_truncated_vertically) def _chk_truncate(self) -> None: """ @@ -791,8 +793,9 @@ def _chk_truncate(self) -> None: *truncate_fmt[-col_num:], ] self.tr_col_num = col_num - if self.truncate_v: - # cast here since if truncate_v is True, max_rows_adj is not None + if self.is_truncated_vertically: + # cast here since if is_truncated_vertically is True + # max_rows_adj is not None max_rows_adj = cast(int, max_rows_adj) if max_rows_adj == 1: row_num = max_rows @@ -865,15 +868,16 @@ def _to_str_columns(self) -> List[List[str]]: # Add ... to signal truncated is_truncated_horizontally = self.is_truncated_horizontally - truncate_v = self.truncate_v + is_truncated_vertically = self.is_truncated_vertically if is_truncated_horizontally: col_num = self.tr_col_num strcols.insert(self.tr_col_num + 1, [" ..."] * (len(str_index))) - if truncate_v: + if is_truncated_vertically: n_header_rows = len(str_index) - len(frame) row_num = self.tr_row_num - # cast here since if truncate_v is True, self.tr_row_num is not None + # cast here since if is_truncated_vertically is True + # self.tr_row_num is not None row_num = cast(int, row_num) for ix, col in enumerate(strcols): # infer from above row @@ -975,7 +979,7 @@ def _join_multiline(self, *args) -> str: col_bins = _binify(col_widths, lwidth) nbins = len(col_bins) - if self.truncate_v: + if self.is_truncated_vertically: assert self.max_rows_adj is not None nrows = self.max_rows_adj + 1 else: diff --git a/pandas/io/formats/html.py b/pandas/io/formats/html.py index 97d72400545b6..68ee855478fd0 100644 --- a/pandas/io/formats/html.py +++ b/pandas/io/formats/html.py @@ -391,7 +391,7 @@ def _write_regular_rows( self, fmt_values: Mapping[int, List[str]], indent: int ) -> None: is_truncated_horizontally = self.fmt.is_truncated_horizontally - truncate_v = self.fmt.truncate_v + is_truncated_vertically = self.fmt.is_truncated_vertically nrows = len(self.fmt.tr_frame) @@ -405,7 +405,7 @@ def _write_regular_rows( row: List[str] = [] for i in range(nrows): - if truncate_v and i == (self.fmt.tr_row_num): + if is_truncated_vertically and i == (self.fmt.tr_row_num): str_sep_row = ["..."] * len(row) self.write_tr( str_sep_row, @@ -439,7 +439,7 @@ def _write_hierarchical_rows( template = 'rowspan="{span}" valign="top"' is_truncated_horizontally = self.fmt.is_truncated_horizontally - truncate_v = self.fmt.truncate_v + is_truncated_vertically = self.fmt.is_truncated_vertically frame = self.fmt.tr_frame nrows = len(frame) @@ -454,11 +454,11 @@ def _write_hierarchical_rows( level_lengths = get_level_lengths(levels, sentinel) inner_lvl = len(level_lengths) - 1 - if truncate_v: + if is_truncated_vertically: # Insert ... row and adjust idx_values and # level_lengths to take this into account. ins_row = self.fmt.tr_row_num - # cast here since if truncate_v is True, self.fmt.tr_row_num is not None + # cast here since if is_truncated_vertically is True, self.fmt.tr_row_num is not None ins_row = cast(int, ins_row) inserted = False for lnum, records in enumerate(level_lengths): @@ -534,7 +534,7 @@ def _write_hierarchical_rows( else: row = [] for i in range(len(frame)): - if truncate_v and i == (self.fmt.tr_row_num): + if is_truncated_vertically and i == (self.fmt.tr_row_num): str_sep_row = ["..."] * len(row) self.write_tr( str_sep_row, From 19948b8be2060de29b4e84fb84e93bdb75510746 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 02:01:26 +0700 Subject: [PATCH 23/75] TYP: make is_truncated compatible with superclass --- pandas/io/formats/format.py | 6 +++++- pandas/io/formats/html.py | 4 +--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 2875527a0fca7..9a294951d54b4 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -453,7 +453,11 @@ def get_adjustment() -> TextAdjustment: class TableFormatter: show_dimensions: Union[bool, str] - is_truncated: bool + + @property + def is_truncated(self) -> bool: + self._is_truncated: bool + return self._is_truncated @property def formatters(self) -> FormattersType: diff --git a/pandas/io/formats/html.py b/pandas/io/formats/html.py index 68ee855478fd0..bed94e6c8d2d3 100644 --- a/pandas/io/formats/html.py +++ b/pandas/io/formats/html.py @@ -85,10 +85,8 @@ def row_levels(self) -> int: def _get_columns_formatted_values(self) -> Iterable: return self.columns - # https://github.com/python/mypy/issues/1237 - # error: Signature of "is_truncated" incompatible with supertype "TableFormatter" @property - def is_truncated(self) -> bool: # type: ignore[override] + def is_truncated(self) -> bool: return self.fmt.is_truncated @property From c57c4ce430b488d82ef3dfbd272199bc637d8bfb Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 02:01:54 +0700 Subject: [PATCH 24/75] CLN: remove unnecessary variables --- pandas/io/formats/format.py | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 9a294951d54b4..477a85735fc00 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -697,6 +697,8 @@ def _is_in_terminal(self) -> bool: @property def max_cols_adj(self) -> Optional[int]: """Number of columns fitting the screen.""" + self._max_cols_adj: Optional[int] + try: return self._max_cols_adj except AttributeError: @@ -766,22 +768,15 @@ def _chk_truncate(self) -> None: """ from pandas.core.reshape.concat import concat - # Cut the data to the information actually printed - max_cols = self.max_cols - max_rows = self.max_rows - - max_cols_adj = self.max_cols_adj - max_rows_adj = self.max_rows_adj - frame = self.frame if self.is_truncated_horizontally: # cast here since if is_truncated_horizontally is True # max_cols_adj is not None - max_cols_adj = cast(int, max_cols_adj) + max_cols_adj = cast(int, self.max_cols_adj) if max_cols_adj == 0: col_num = len(frame.columns) elif max_cols_adj == 1: - max_cols = cast(int, max_cols) + max_cols = cast(int, self.max_cols) frame = frame.iloc[:, :max_cols] col_num = max_cols else: @@ -797,13 +792,14 @@ def _chk_truncate(self) -> None: *truncate_fmt[-col_num:], ] self.tr_col_num = col_num + if self.is_truncated_vertically: # cast here since if is_truncated_vertically is True # max_rows_adj is not None - max_rows_adj = cast(int, max_rows_adj) + max_rows_adj = cast(int, self.max_rows_adj) if max_rows_adj == 1: - row_num = max_rows - frame = frame.iloc[:max_rows, :] + row_num = self.max_rows + frame = frame.iloc[:row_num, :] else: row_num = max_rows_adj // 2 frame = concat((frame.iloc[:row_num, :], frame.iloc[-row_num:, :])) @@ -870,14 +866,10 @@ def _to_str_columns(self) -> List[List[str]]: if self.index: strcols.insert(0, str_index) - # Add ... to signal truncated - is_truncated_horizontally = self.is_truncated_horizontally - is_truncated_vertically = self.is_truncated_vertically - - if is_truncated_horizontally: + if self.is_truncated_horizontally: col_num = self.tr_col_num strcols.insert(self.tr_col_num + 1, [" ..."] * (len(str_index))) - if is_truncated_vertically: + if self.is_truncated_vertically: n_header_rows = len(str_index) - len(frame) row_num = self.tr_row_num # cast here since if is_truncated_vertically is True @@ -887,7 +879,7 @@ def _to_str_columns(self) -> List[List[str]]: # infer from above row cwidth = self.adj.len(strcols[ix][row_num]) is_dot_col = False - if is_truncated_horizontally: + if self.is_truncated_horizontally: is_dot_col = ix == col_num + 1 if cwidth > 3 or is_dot_col: my_str = "..." From cb680ce6c7628b0abea3ee5e0dbd33e9ccfd897a Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 02:07:52 +0700 Subject: [PATCH 25/75] REF: extract _get_number_of_auxillary_rows --- pandas/io/formats/format.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 477a85735fc00..d35d4e1742b9d 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -722,18 +722,10 @@ def max_rows_adj(self) -> Optional[int]: if not self._is_in_terminal(): return self.max_rows - (width, height) = get_terminal_size() + _, height = get_terminal_size() if self.max_rows == 0: - dot_row = 1 - prompt_row = 1 - if self.show_dimensions: - show_dimension_rows = 3 - # assume we only get here if self.header is boolean. - # i.e. not to_latex() where self.header may be List[str] - self.header = cast(bool, self.header) - n_add_rows = self.header + dot_row + show_dimension_rows + prompt_row # rows available to fill with actual data - return height - n_add_rows + return height - self._get_number_of_auxillary_rows() if self._is_screen_short(height): max_rows = height @@ -746,6 +738,16 @@ def max_rows_adj(self) -> Optional[int]: max_rows = min(self.min_rows, max_rows) return max_rows + def _get_number_of_auxillary_rows(self) -> int: + dot_row = 1 + prompt_row = 1 + if self.show_dimensions: + show_dimension_rows = 3 + # assume we only get here if self.header is boolean. + # i.e. not to_latex() where self.header may be List[str] + self.header = cast(bool, self.header) + return self.header + dot_row + show_dimension_rows + prompt_row + def _is_screen_short(self, max_height) -> bool: return bool(self.max_rows == 0 and len(self.frame) > max_height) From b6579520252decfe62993b8a3ab5523f2b7425d7 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 02:20:53 +0700 Subject: [PATCH 26/75] REF: use var header_row for clarity --- pandas/io/formats/format.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index d35d4e1742b9d..fa310753b7ad6 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -741,12 +741,16 @@ def max_rows_adj(self) -> Optional[int]: def _get_number_of_auxillary_rows(self) -> int: dot_row = 1 prompt_row = 1 + if self.show_dimensions: show_dimension_rows = 3 - # assume we only get here if self.header is boolean. - # i.e. not to_latex() where self.header may be List[str] - self.header = cast(bool, self.header) - return self.header + dot_row + show_dimension_rows + prompt_row + + if self.header: + header_row = 1 + else: + header_row = 0 + + return header_row + dot_row + show_dimension_rows + prompt_row def _is_screen_short(self, max_height) -> bool: return bool(self.max_rows == 0 and len(self.frame) > max_height) From 84c42b7935bd3932add292cd243fe2a7eaf0b883 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 02:21:50 +0700 Subject: [PATCH 27/75] TYP: ignore assignment error (setters) --- pandas/io/formats/format.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index fa310753b7ad6..f67afb1e0ac08 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -585,10 +585,24 @@ def __init__( self.show_index_names = index_names self.sparsify = sparsify self.float_format = float_format - self.formatters = formatters + + # Ignoring error + # expression has type "Union[List[Callable[..., Any]], + # Tuple[Callable[..., Any], ...], + # Mapping[Union[str, int], Callable[..., Any]], None]", + # variable has type "Union[List[Callable[..., Any]], + # Tuple[Callable[..., Any], ...], Mapping[Union[str, int], + # Callable[..., Any]]]") + self.formatters = formatters # type: ignore[assignment] self.na_rep = na_rep self.decimal = decimal - self.col_space = col_space + + # Ignoring error + # expression has type "Union[str, int, Sequence[Union[str, int]], + # Mapping[Optional[Hashable], Union[str, int]], None]", + # variable has type "Mapping[Optional[Hashable], Union[str, int]]" + self.col_space = col_space # type: ignore[assignment] + self.header = header self.index = index self.line_width = line_width @@ -601,7 +615,11 @@ def __init__( self.justify = justify self.bold_rows = bold_rows self.escape = escape - self.columns = columns + + # Ignoring error: + # expression has type "Optional[Sequence[str]]", variable has type "Index" + self.columns = columns # type: ignore[assignment] + self._chk_truncate() self.adj = get_adjustment() From 6f1a81f45cad8c0d9f20e12f7059d30b0d48157f Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 02:22:07 +0700 Subject: [PATCH 28/75] TYP: specify max_rows type --- pandas/io/formats/format.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index f67afb1e0ac08..a2d3d1ac4e713 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -745,6 +745,7 @@ def max_rows_adj(self) -> Optional[int]: # rows available to fill with actual data return height - self._get_number_of_auxillary_rows() + max_rows: Optional[int] if self._is_screen_short(height): max_rows = height else: From 8f583c75f25d064b9fd08ceba9064fa0824f172a Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 02:35:17 +0700 Subject: [PATCH 29/75] LINT: long comment line --- pandas/io/formats/html.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/io/formats/html.py b/pandas/io/formats/html.py index bed94e6c8d2d3..ce98ac930f864 100644 --- a/pandas/io/formats/html.py +++ b/pandas/io/formats/html.py @@ -456,7 +456,8 @@ def _write_hierarchical_rows( # Insert ... row and adjust idx_values and # level_lengths to take this into account. ins_row = self.fmt.tr_row_num - # cast here since if is_truncated_vertically is True, self.fmt.tr_row_num is not None + # cast here since if is_truncated_vertically is True + # self.fmt.tr_row_num is not None ins_row = cast(int, ins_row) inserted = False for lnum, records in enumerate(level_lengths): From d8744f1c2f968a9bde171283927b1cfee0eb8508 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 10:54:41 +0700 Subject: [PATCH 30/75] REF: use hasattr instead of try on AttributeError --- pandas/io/formats/format.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index a2d3d1ac4e713..d6cd0e7d1f12b 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -717,20 +717,20 @@ def max_cols_adj(self) -> Optional[int]: """Number of columns fitting the screen.""" self._max_cols_adj: Optional[int] - try: + if hasattr(self, "_max_cols_adj"): return self._max_cols_adj - except AttributeError: - if not self._is_in_terminal(): - self._max_cols_adj = self.max_cols - return self._max_cols_adj - - width, _ = get_terminal_size() - if self._is_screen_narrow(width): - self._max_cols_adj = width - else: - self._max_cols_adj = self.max_cols + + if not self._is_in_terminal(): + self._max_cols_adj = self.max_cols return self._max_cols_adj + width, _ = get_terminal_size() + if self._is_screen_narrow(width): + self._max_cols_adj = width + else: + self._max_cols_adj = self.max_cols + return self._max_cols_adj + def _is_screen_narrow(self, max_width) -> bool: return bool(self.max_cols == 0 and len(self.frame.columns) > max_width) From 1cfbf209e9173e205dc15f948e1ee5c4bff26c78 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 11:59:31 +0700 Subject: [PATCH 31/75] REF: extract helper truncate methods This simplifies method _chk_truncate. New methods extracted: - _truncate_vertically - _truncate_horizontally --- pandas/io/formats/format.py | 78 ++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index d6cd0e7d1f12b..b11af7010320a 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -67,6 +67,7 @@ from pandas.core.indexes.api import Index, MultiIndex, PeriodIndex, ensure_index from pandas.core.indexes.datetimes import DatetimeIndex from pandas.core.indexes.timedeltas import TimedeltaIndex +from pandas.core.reshape.concat import concat from pandas.io.common import stringify_path from pandas.io.formats.printing import adjoin, justify, pprint_thing @@ -791,48 +792,53 @@ def _chk_truncate(self) -> None: Checks whether the frame should be truncated. If so, slices the frame up. """ - from pandas.core.reshape.concat import concat + self.tr_frame = self.frame.copy() - frame = self.frame if self.is_truncated_horizontally: - # cast here since if is_truncated_horizontally is True - # max_cols_adj is not None - max_cols_adj = cast(int, self.max_cols_adj) - if max_cols_adj == 0: - col_num = len(frame.columns) - elif max_cols_adj == 1: - max_cols = cast(int, self.max_cols) - frame = frame.iloc[:, :max_cols] - col_num = max_cols - else: - col_num = max_cols_adj // 2 - frame = concat( - (frame.iloc[:, :col_num], frame.iloc[:, -col_num:]), axis=1 - ) - # truncate formatter - if isinstance(self.formatters, (list, tuple)): - truncate_fmt = self.formatters - self._formatters = [ - *truncate_fmt[:col_num], - *truncate_fmt[-col_num:], - ] - self.tr_col_num = col_num - + self._truncate_horizontally() if self.is_truncated_vertically: - # cast here since if is_truncated_vertically is True - # max_rows_adj is not None - max_rows_adj = cast(int, self.max_rows_adj) - if max_rows_adj == 1: - row_num = self.max_rows - frame = frame.iloc[:row_num, :] - else: - row_num = max_rows_adj // 2 - frame = concat((frame.iloc[:row_num, :], frame.iloc[-row_num:, :])) - self.tr_row_num = row_num + self._truncate_vertically() else: self.tr_row_num = None - self.tr_frame = frame + def _truncate_horizontally(self): + # cast here since if is_truncated_horizontally is True + # max_cols_adj is not None + max_cols_adj = cast(int, self.max_cols_adj) + if max_cols_adj == 0: + col_num = len(self.tr_frame.columns) + elif max_cols_adj == 1: + max_cols = cast(int, self.max_cols) + self.tr_frame = self.tr_frame.iloc[:, :max_cols] + col_num = max_cols + else: + col_num = max_cols_adj // 2 + self.tr_frame = concat( + (self.tr_frame.iloc[:, :col_num], self.tr_frame.iloc[:, -col_num:]), + axis=1, + ) + # truncate formatter + if isinstance(self.formatters, (list, tuple)): + truncate_fmt = self.formatters + self._formatters = [ + *truncate_fmt[:col_num], + *truncate_fmt[-col_num:], + ] + self.tr_col_num = col_num + + def _truncate_vertically(self): + # cast here since if is_truncated_vertically is True + # max_rows_adj is not None + max_rows_adj = cast(int, self.max_rows_adj) + if max_rows_adj == 1: + row_num = self.max_rows + self.tr_frame = self.tr_frame.iloc[:row_num, :] + else: + row_num = max_rows_adj // 2 + self.tr_frame = concat( + (self.tr_frame.iloc[:row_num, :], self.tr_frame.iloc[-row_num:, :]) + ) + self.tr_row_num = row_num def _to_str_columns(self) -> List[List[str]]: """ From 3081b8b838ce1b6ebcc72773b2082591adcbc532 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 12:01:14 +0700 Subject: [PATCH 32/75] CLN: remove unnecessary if statement --- pandas/io/formats/format.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index b11af7010320a..ace61920c851a 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -805,9 +805,7 @@ def _truncate_horizontally(self): # cast here since if is_truncated_horizontally is True # max_cols_adj is not None max_cols_adj = cast(int, self.max_cols_adj) - if max_cols_adj == 0: - col_num = len(self.tr_frame.columns) - elif max_cols_adj == 1: + if max_cols_adj == 1: max_cols = cast(int, self.max_cols) self.tr_frame = self.tr_frame.iloc[:, :max_cols] col_num = max_cols From a05c0c6452ec3e02f80e31886313347a6900e313 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 12:02:29 +0700 Subject: [PATCH 33/75] CLN: remove unnecessary else clause --- pandas/io/formats/format.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index ace61920c851a..847fad4ea9647 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -796,10 +796,9 @@ def _chk_truncate(self) -> None: if self.is_truncated_horizontally: self._truncate_horizontally() + if self.is_truncated_vertically: self._truncate_vertically() - else: - self.tr_row_num = None def _truncate_horizontally(self): # cast here since if is_truncated_horizontally is True From 62d1cce5900588f8b29ffe6d1132993d31813428 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 12:04:51 +0700 Subject: [PATCH 34/75] CLN: move str_index to where it is used --- pandas/io/formats/format.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 847fad4ea9647..8de7f0dec0934 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -848,8 +848,6 @@ def _to_str_columns(self) -> List[List[str]]: frame = self.tr_frame # may include levels names also - str_index = self._get_formatted_index(frame) - if not is_list_like(self.header) and not self.header: stringified = [] for i, c in enumerate(frame): @@ -890,6 +888,7 @@ def _to_str_columns(self) -> List[List[str]]: cheader = self.adj.justify(cheader, max_len, mode=self.justify) stringified.append(cheader + fmt_values) + str_index = self._get_formatted_index(frame) strcols = stringified if self.index: strcols.insert(0, str_index) @@ -897,6 +896,7 @@ def _to_str_columns(self) -> List[List[str]]: if self.is_truncated_horizontally: col_num = self.tr_col_num strcols.insert(self.tr_col_num + 1, [" ..."] * (len(str_index))) + if self.is_truncated_vertically: n_header_rows = len(str_index) - len(frame) row_num = self.tr_row_num @@ -923,6 +923,7 @@ def _to_str_columns(self) -> List[List[str]]: dot_mode = "right" dot_str = self.adj.justify([my_str], cwidth, mode=dot_mode)[0] strcols[ix].insert(row_num + n_header_rows, dot_str) + return strcols def write_result(self, buf: IO[str]) -> None: From e51b4feeb60a38d2258240d757a0b7dd6e9c3a28 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 12:12:26 +0700 Subject: [PATCH 35/75] CLN: several assignments --- pandas/io/formats/format.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 8de7f0dec0934..fb245b2412419 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -894,7 +894,6 @@ def _to_str_columns(self) -> List[List[str]]: strcols.insert(0, str_index) if self.is_truncated_horizontally: - col_num = self.tr_col_num strcols.insert(self.tr_col_num + 1, [" ..."] * (len(str_index))) if self.is_truncated_vertically: @@ -905,14 +904,14 @@ def _to_str_columns(self) -> List[List[str]]: row_num = cast(int, row_num) for ix, col in enumerate(strcols): # infer from above row - cwidth = self.adj.len(strcols[ix][row_num]) + cwidth = self.adj.len(col[row_num]) is_dot_col = False if self.is_truncated_horizontally: - is_dot_col = ix == col_num + 1 + is_dot_col = ix == self.tr_col_num + 1 if cwidth > 3 or is_dot_col: - my_str = "..." + dots = "..." else: - my_str = ".." + dots = ".." if ix == 0: dot_mode = "left" @@ -921,8 +920,8 @@ def _to_str_columns(self) -> List[List[str]]: dot_mode = "right" else: dot_mode = "right" - dot_str = self.adj.justify([my_str], cwidth, mode=dot_mode)[0] - strcols[ix].insert(row_num + n_header_rows, dot_str) + dot_str = self.adj.justify([dots], cwidth, mode=dot_mode)[0] + col.insert(row_num + n_header_rows, dot_str) return strcols From 99cf1a276ff0421325bac5f90e665a446b819b4f Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 12:16:52 +0700 Subject: [PATCH 36/75] REF: extract _get_strcols method This returns strcols before any truncation. It will be useful for LaTeX formatting, for instance, where no truncation is present. Should consider polymorphism later (with and without truncation). --- pandas/io/formats/format.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index fb245b2412419..92e1fd7330d3a 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -837,10 +837,8 @@ def _truncate_vertically(self): ) self.tr_row_num = row_num - def _to_str_columns(self) -> List[List[str]]: - """ - Render a DataFrame to a list of columns (as lists of strings). - """ + def _get_strcols(self) -> List[List[str]]: + # TODO check this comment validity # this method is not used by to_html where self.col_space # could be a string so safe to cast col_space = {k: cast(int, v) for k, v in self.col_space.items()} @@ -893,11 +891,21 @@ def _to_str_columns(self) -> List[List[str]]: if self.index: strcols.insert(0, str_index) + return strcols + + def _to_str_columns(self) -> List[List[str]]: + """ + Render a DataFrame to a list of columns (as lists of strings). + """ + strcols = self._get_strcols() + + str_index = self._get_formatted_index(self.tr_frame) + if self.is_truncated_horizontally: strcols.insert(self.tr_col_num + 1, [" ..."] * (len(str_index))) if self.is_truncated_vertically: - n_header_rows = len(str_index) - len(frame) + n_header_rows = len(str_index) - len(self.tr_frame) row_num = self.tr_row_num # cast here since if is_truncated_vertically is True # self.tr_row_num is not None From be570cdb8de934cacb4fa7cb6662954c642f59f6 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 12:19:59 +0700 Subject: [PATCH 37/75] CLN: remove stringified variable --- pandas/io/formats/format.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 92e1fd7330d3a..9f017c5484679 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -847,13 +847,13 @@ def _get_strcols(self) -> List[List[str]]: # may include levels names also if not is_list_like(self.header) and not self.header: - stringified = [] + strcols = [] for i, c in enumerate(frame): fmt_values = self._format_col(i) fmt_values = _make_fixed_width( fmt_values, self.justify, minimum=col_space.get(c, 0), adj=self.adj ) - stringified.append(fmt_values) + strcols.append(fmt_values) else: if is_list_like(self.header): # cast here since can't be bool if is_list_like @@ -871,7 +871,7 @@ def _get_strcols(self) -> List[List[str]]: for x in str_columns: x.append("") - stringified = [] + strcols = [] for i, c in enumerate(frame): cheader = str_columns[i] header_colwidth = max( @@ -884,10 +884,9 @@ def _get_strcols(self) -> List[List[str]]: max_len = max(max(self.adj.len(x) for x in fmt_values), header_colwidth) cheader = self.adj.justify(cheader, max_len, mode=self.justify) - stringified.append(cheader + fmt_values) + strcols.append(cheader + fmt_values) str_index = self._get_formatted_index(frame) - strcols = stringified if self.index: strcols.insert(0, str_index) From 6562ebb1fb28a498784b0457e704e82c8470938a Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 12:24:56 +0700 Subject: [PATCH 38/75] REF: extract _get_strcols_without_index method --- pandas/io/formats/format.py | 64 ++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 9f017c5484679..d91396bf30b15 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -837,7 +837,7 @@ def _truncate_vertically(self): ) self.tr_row_num = row_num - def _get_strcols(self) -> List[List[str]]: + def _get_strcols_without_index(self): # TODO check this comment validity # this method is not used by to_html where self.col_space # could be a string so safe to cast @@ -854,39 +854,45 @@ def _get_strcols(self) -> List[List[str]]: fmt_values, self.justify, minimum=col_space.get(c, 0), adj=self.adj ) strcols.append(fmt_values) + return strcols + + if is_list_like(self.header): + # cast here since can't be bool if is_list_like + self.header = cast(List[str], self.header) + if len(self.header) != len(self.columns): + raise ValueError( + f"Writing {len(self.columns)} cols " + f"but got {len(self.header)} aliases" + ) + str_columns = [[label] for label in self.header] else: - if is_list_like(self.header): - # cast here since can't be bool if is_list_like - self.header = cast(List[str], self.header) - if len(self.header) != len(self.columns): - raise ValueError( - f"Writing {len(self.columns)} cols " - f"but got {len(self.header)} aliases" - ) - str_columns = [[label] for label in self.header] - else: - str_columns = self._get_formatted_column_labels(frame) + str_columns = self._get_formatted_column_labels(frame) - if self.show_row_idx_names: - for x in str_columns: - x.append("") + if self.show_row_idx_names: + for x in str_columns: + x.append("") - strcols = [] - for i, c in enumerate(frame): - cheader = str_columns[i] - header_colwidth = max( - col_space.get(c, 0), *(self.adj.len(x) for x in cheader) - ) - fmt_values = self._format_col(i) - fmt_values = _make_fixed_width( - fmt_values, self.justify, minimum=header_colwidth, adj=self.adj - ) + strcols = [] + for i, c in enumerate(frame): + cheader = str_columns[i] + header_colwidth = max( + col_space.get(c, 0), *(self.adj.len(x) for x in cheader) + ) + fmt_values = self._format_col(i) + fmt_values = _make_fixed_width( + fmt_values, self.justify, minimum=header_colwidth, adj=self.adj + ) - max_len = max(max(self.adj.len(x) for x in fmt_values), header_colwidth) - cheader = self.adj.justify(cheader, max_len, mode=self.justify) - strcols.append(cheader + fmt_values) + max_len = max(max(self.adj.len(x) for x in fmt_values), header_colwidth) + cheader = self.adj.justify(cheader, max_len, mode=self.justify) + strcols.append(cheader + fmt_values) - str_index = self._get_formatted_index(frame) + return strcols + + def _get_strcols(self) -> List[List[str]]: + strcols = self._get_strcols_without_index() + + str_index = self._get_formatted_index(self.tr_frame) if self.index: strcols.insert(0, str_index) From 6b04bef2752b2d44af36f54782a18d90d3bfaf8d Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 12:27:08 +0700 Subject: [PATCH 39/75] REF: extract _truncate_strcols method --- pandas/io/formats/format.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index d91396bf30b15..dc8e87776980f 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -904,6 +904,12 @@ def _to_str_columns(self) -> List[List[str]]: """ strcols = self._get_strcols() + if self.is_truncated: + strcols = self._truncate_strcols(strcols) + + return strcols + + def _truncate_strcols(self, strcols): str_index = self._get_formatted_index(self.tr_frame) if self.is_truncated_horizontally: From 1b3cd3646a8f11c75576767ddea87f4a7c23053b Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 12:39:54 +0700 Subject: [PATCH 40/75] REF: extract _get_string_representation This method gets string representation of the table to be printed or saved. --- pandas/io/formats/format.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index dc8e87776980f..52a71f26cca41 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -948,6 +948,18 @@ def write_result(self, buf: IO[str]) -> None: """ Render a DataFrame to a console-friendly tabular output. """ + text = self._get_string_representation() + + buf.writelines(text) + + if self.should_show_dimensions: + dim_str = ( + f"\n\n[{len(self.frame)} rows x " + f"{len(self.frame.columns)} columns]" + ) + buf.write(dim_str) + + def _get_string_representation(self): from pandas import Series frame = self.frame @@ -1000,10 +1012,7 @@ def write_result(self, buf: IO[str]) -> None: self._chk_truncate() strcols = self._to_str_columns() text = self.adj.adjoin(1, *strcols) - buf.writelines(text) - - if self.should_show_dimensions: - buf.write(f"\n\n[{len(frame)} rows x {len(frame.columns)} columns]") + return text def _join_multiline(self, *args) -> str: lwidth = self.line_width From 99d57ba2e39394efcb9301de3a14577b6f29ed5a Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 12:59:58 +0700 Subject: [PATCH 41/75] CLN: simplify logic in _get_string_representation --- pandas/io/formats/format.py | 93 ++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 47 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 52a71f26cca41..04e6d24fb4b99 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -962,57 +962,56 @@ def write_result(self, buf: IO[str]) -> None: def _get_string_representation(self): from pandas import Series - frame = self.frame - - if len(frame.columns) == 0 or len(frame.index) == 0: + if self.frame.empty: info_line = ( f"Empty {type(self.frame).__name__}\n" - f"Columns: {pprint_thing(frame.columns)}\n" - f"Index: {pprint_thing(frame.index)}" + f"Columns: {pprint_thing(self.frame.columns)}\n" + f"Index: {pprint_thing(self.frame.index)}" ) - text = info_line - else: + return info_line + + strcols = self._to_str_columns() + + if self.line_width is None: + # no need to wrap around just print the whole frame + return self.adj.adjoin(1, *strcols) - strcols = self._to_str_columns() - if self.line_width is None: # no need to wrap around just print - # the whole frame - text = self.adj.adjoin(1, *strcols) - elif ( - not isinstance(self.max_cols, int) or self.max_cols > 0 - ): # need to wrap around - text = self._join_multiline(*strcols) - else: # max_cols == 0. Try to fit frame to terminal - lines = self.adj.adjoin(1, *strcols).split("\n") - max_len = Series(lines).str.len().max() - # plus truncate dot col - width, _ = get_terminal_size() - dif = max_len - width - # '+ 1' to avoid too wide repr (GH PR #17023) - adj_dif = dif + 1 - col_lens = Series([Series(ele).apply(len).max() for ele in strcols]) - n_cols = len(col_lens) - counter = 0 - while adj_dif > 0 and n_cols > 1: - counter += 1 - mid = int(round(n_cols / 2.0)) - mid_ix = col_lens.index[mid] - col_len = col_lens[mid_ix] - # adjoin adds one - adj_dif -= col_len + 1 - col_lens = col_lens.drop(mid_ix) - n_cols = len(col_lens) - # subtract index column - max_cols_adj = n_cols - self.index - # GH-21180. Ensure that we print at least two. - max_cols_adj = max(max_cols_adj, 2) - self._max_cols_adj = max_cols_adj - - # Call again _chk_truncate to cut frame appropriately - # and then generate string representation - self._chk_truncate() - strcols = self._to_str_columns() - text = self.adj.adjoin(1, *strcols) - return text + if (not isinstance(self.max_cols, int) or self.max_cols > 0): + # need to wrap around + return self._join_multiline(*strcols) + + # max_cols == 0. Try to fit frame to terminal + lines = self.adj.adjoin(1, *strcols).split("\n") + max_len = Series(lines).str.len().max() + # plus truncate dot col + width, _ = get_terminal_size() + dif = max_len - width + # '+ 1' to avoid too wide repr (GH PR #17023) + adj_dif = dif + 1 + col_lens = Series([Series(ele).apply(len).max() for ele in strcols]) + n_cols = len(col_lens) + counter = 0 + while adj_dif > 0 and n_cols > 1: + counter += 1 + mid = int(round(n_cols / 2.0)) + mid_ix = col_lens.index[mid] + col_len = col_lens[mid_ix] + # adjoin adds one + adj_dif -= col_len + 1 + col_lens = col_lens.drop(mid_ix) + n_cols = len(col_lens) + + # subtract index column + max_cols_adj = n_cols - self.index + # GH-21180. Ensure that we print at least two. + max_cols_adj = max(max_cols_adj, 2) + self._max_cols_adj = max_cols_adj + + # Call again _chk_truncate to cut frame appropriately + # and then generate string representation + self._chk_truncate() + strcols = self._to_str_columns() + return self.adj.adjoin(1, *strcols) def _join_multiline(self, *args) -> str: lwidth = self.line_width From 4e97e97c57ba47cb2b1a1da9280bc251beaa4616 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 13:19:05 +0700 Subject: [PATCH 42/75] CLN: simplify condition to wrap around --- 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 04e6d24fb4b99..5bd1a29c2ff7b 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -976,7 +976,7 @@ def _get_string_representation(self): # no need to wrap around just print the whole frame return self.adj.adjoin(1, *strcols) - if (not isinstance(self.max_cols, int) or self.max_cols > 0): + if self.max_cols is None or self.max_cols > 0: # need to wrap around return self._join_multiline(*strcols) From 62af21164147eb7720149fbf400c9e0ed853c053 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 13:27:39 +0700 Subject: [PATCH 43/75] REF: extract _fit_strcols_to_terminal_width --- pandas/io/formats/format.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 5bd1a29c2ff7b..d8443bc30a8d4 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -960,8 +960,6 @@ def write_result(self, buf: IO[str]) -> None: buf.write(dim_str) def _get_string_representation(self): - from pandas import Series - if self.frame.empty: info_line = ( f"Empty {type(self.frame).__name__}\n" @@ -981,6 +979,11 @@ def _get_string_representation(self): return self._join_multiline(*strcols) # max_cols == 0. Try to fit frame to terminal + return self._fit_strcols_to_terminal_width(strcols) + + def _fit_strcols_to_terminal_width(self, strcols): + from pandas import Series + lines = self.adj.adjoin(1, *strcols).split("\n") max_len = Series(lines).str.len().max() # plus truncate dot col From 860ae65d8928530752ccd75f7a0d9a23c8280bea Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 13:40:10 +0700 Subject: [PATCH 44/75] CLN: better names for start, end --- pandas/io/formats/format.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index d8443bc30a8d4..86c2bd9c11939 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -1040,18 +1040,18 @@ def _join_multiline(self, *args) -> str: nrows = len(self.frame) str_lst = [] - st = 0 - for i, ed in enumerate(col_bins): - row = strcols[st:ed] + start = 0 + for i, end in enumerate(col_bins): + row = strcols[start:end] if self.index: row.insert(0, idx) if nbins > 1: - if ed <= len(strcols) and i < nbins - 1: + if end <= len(strcols) and i < nbins - 1: row.append([" \\"] + [" "] * (nrows - 1)) else: row.append([" "] * nrows) str_lst.append(self.adj.adjoin(adjoin_width, *row)) - st = ed + start = end return "\n\n".join(str_lst) def to_string( From bbccb743b3622f044af2ba6b71f5a9d342bfea66 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 13:51:43 +0700 Subject: [PATCH 45/75] REF: get rid of magic number 3 Here 3 means the number of dimension info lines (two new lines and one actual info line). This commit makes it explicitly. --- pandas/io/formats/format.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 86c2bd9c11939..6886b8aa36db1 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -763,7 +763,7 @@ def _get_number_of_auxillary_rows(self) -> int: prompt_row = 1 if self.show_dimensions: - show_dimension_rows = 3 + show_dimension_rows = len(self._dimensions_info.splitlines()) if self.header: header_row = 1 @@ -953,11 +953,11 @@ def write_result(self, buf: IO[str]) -> None: buf.writelines(text) if self.should_show_dimensions: - dim_str = ( - f"\n\n[{len(self.frame)} rows x " - f"{len(self.frame.columns)} columns]" - ) - buf.write(dim_str) + buf.write(self._dimensions_info) + + @property + def _dimensions_info(self): + return f"\n\n[{len(self.frame)} rows x {len(self.frame.columns)} columns]" def _get_string_representation(self): if self.frame.empty: From d43dd064338d4341e72672220d426deb80bcd8a5 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 13:59:56 +0700 Subject: [PATCH 46/75] REF: incrementally add numbers of auxillary rows --- pandas/io/formats/format.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 6886b8aa36db1..f435ea63435d2 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -761,16 +761,15 @@ def max_rows_adj(self) -> Optional[int]: def _get_number_of_auxillary_rows(self) -> int: dot_row = 1 prompt_row = 1 + num_rows = dot_row + prompt_row if self.show_dimensions: - show_dimension_rows = len(self._dimensions_info.splitlines()) + num_rows += len(self._dimensions_info.splitlines()) if self.header: - header_row = 1 - else: - header_row = 0 + num_rows += 1 - return header_row + dot_row + show_dimension_rows + prompt_row + return num_rows def _is_screen_short(self, max_height) -> bool: return bool(self.max_rows == 0 and len(self.frame) > max_height) From 6051f67eee5179df67bbc892233dfa675b304989 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 14:35:40 +0700 Subject: [PATCH 47/75] CLN: rename _chk_truncate -> _truncate This is more reasonable name for what it does. --- pandas/io/formats/format.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index f435ea63435d2..a67222c433a5f 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -621,7 +621,7 @@ def __init__( # expression has type "Optional[Sequence[str]]", variable has type "Index" self.columns = columns # type: ignore[assignment] - self._chk_truncate() + self._truncate() self.adj = get_adjustment() @property @@ -786,10 +786,9 @@ def is_truncated_vertically(self) -> bool: def is_truncated(self) -> bool: return bool(self.is_truncated_horizontally or self.is_truncated_vertically) - def _chk_truncate(self) -> None: + def _truncate(self) -> None: """ - Checks whether the frame should be truncated. If so, slices - the frame up. + Check whether the frame should be truncated. If so, slice the frame up. """ self.tr_frame = self.frame.copy() @@ -1009,9 +1008,9 @@ def _fit_strcols_to_terminal_width(self, strcols): max_cols_adj = max(max_cols_adj, 2) self._max_cols_adj = max_cols_adj - # Call again _chk_truncate to cut frame appropriately + # Call again _truncate to cut frame appropriately # and then generate string representation - self._chk_truncate() + self._truncate() strcols = self._to_str_columns() return self.adj.adjoin(1, *strcols) From f0d034e8aec94e41004e3af6cb9e3188ec9030ed Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 16:55:09 +0700 Subject: [PATCH 48/75] REF: _truncate_horizontally without concatenation --- pandas/io/formats/format.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index a67222c433a5f..1c71c1fdd4c93 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -10,6 +10,7 @@ from functools import partial from io import StringIO import math +from operator import itemgetter import re from shutil import get_terminal_size from typing import ( @@ -808,17 +809,18 @@ def _truncate_horizontally(self): col_num = max_cols else: col_num = max_cols_adj // 2 - self.tr_frame = concat( - (self.tr_frame.iloc[:, :col_num], self.tr_frame.iloc[:, -col_num:]), - axis=1, - ) + + cols_to_keep = [ + x for x in range(self.frame.shape[1]) + if x < col_num or x >= len(self.frame.columns) - col_num + ] + self.tr_frame = self.tr_frame.iloc[:, cols_to_keep] + # truncate formatter if isinstance(self.formatters, (list, tuple)): - truncate_fmt = self.formatters - self._formatters = [ - *truncate_fmt[:col_num], - *truncate_fmt[-col_num:], - ] + slicer = itemgetter(*cols_to_keep) + self._formatters = slicer(self.formatters) + self.tr_col_num = col_num def _truncate_vertically(self): From 16330477871f93e810bc019c4a189752d606f84f Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 17:06:26 +0700 Subject: [PATCH 49/75] CLN: simplify logic in _truncate_horizontally --- pandas/io/formats/format.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 1c71c1fdd4c93..0afaabbe58ee4 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -803,13 +803,10 @@ def _truncate_horizontally(self): # cast here since if is_truncated_horizontally is True # max_cols_adj is not None max_cols_adj = cast(int, self.max_cols_adj) - if max_cols_adj == 1: - max_cols = cast(int, self.max_cols) - self.tr_frame = self.tr_frame.iloc[:, :max_cols] - col_num = max_cols - else: - col_num = max_cols_adj // 2 + col_num = max_cols_adj // 2 + + if col_num >= 1: cols_to_keep = [ x for x in range(self.frame.shape[1]) if x < col_num or x >= len(self.frame.columns) - col_num @@ -820,6 +817,10 @@ def _truncate_horizontally(self): if isinstance(self.formatters, (list, tuple)): slicer = itemgetter(*cols_to_keep) self._formatters = slicer(self.formatters) + else: + max_cols = cast(int, self.max_cols) + self.tr_frame = self.tr_frame.iloc[:, :max_cols] + col_num = max_cols self.tr_col_num = col_num From fd7c03f2a0d67e86b29595b265c384f83f92deea Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 17:09:05 +0700 Subject: [PATCH 50/75] CLN: simplify logic in _truncate_vertically --- pandas/io/formats/format.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 0afaabbe58ee4..abfd93f3d276a 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -805,7 +805,6 @@ def _truncate_horizontally(self): max_cols_adj = cast(int, self.max_cols_adj) col_num = max_cols_adj // 2 - if col_num >= 1: cols_to_keep = [ x for x in range(self.frame.shape[1]) @@ -819,8 +818,8 @@ def _truncate_horizontally(self): self._formatters = slicer(self.formatters) else: max_cols = cast(int, self.max_cols) - self.tr_frame = self.tr_frame.iloc[:, :max_cols] col_num = max_cols + self.tr_frame = self.tr_frame.iloc[:, :col_num] self.tr_col_num = col_num @@ -828,14 +827,14 @@ def _truncate_vertically(self): # cast here since if is_truncated_vertically is True # max_rows_adj is not None max_rows_adj = cast(int, self.max_rows_adj) - if max_rows_adj == 1: - row_num = self.max_rows - self.tr_frame = self.tr_frame.iloc[:row_num, :] - else: - row_num = max_rows_adj // 2 + row_num = max_rows_adj // 2 + if row_num >= 1: self.tr_frame = concat( (self.tr_frame.iloc[:row_num, :], self.tr_frame.iloc[-row_num:, :]) ) + else: + row_num = self.max_rows + self.tr_frame = self.tr_frame.iloc[:row_num, :] self.tr_row_num = row_num def _get_strcols_without_index(self): From a71c0b9d2552ec039a9b19877b7545cbb286dc0b Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 17:10:59 +0700 Subject: [PATCH 51/75] REF: _truncate_vertically without concatenation --- pandas/io/formats/format.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index abfd93f3d276a..b863e693ed1f7 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -829,9 +829,11 @@ def _truncate_vertically(self): max_rows_adj = cast(int, self.max_rows_adj) row_num = max_rows_adj // 2 if row_num >= 1: - self.tr_frame = concat( - (self.tr_frame.iloc[:row_num, :], self.tr_frame.iloc[-row_num:, :]) - ) + rows_to_keep = [ + x for x in range(self.frame.shape[0]) + if x < row_num or x >= len(self.frame) - row_num + ] + self.tr_frame = self.tr_frame.iloc[rows_to_keep, :] else: row_num = self.max_rows self.tr_frame = self.tr_frame.iloc[:row_num, :] From b53fabeafbbb73933e4045334f9db18b336036ba Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 17:19:39 +0700 Subject: [PATCH 52/75] LINT: list comprehension --- pandas/io/formats/format.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index b863e693ed1f7..0e6fe5b2a97a9 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -807,7 +807,8 @@ def _truncate_horizontally(self): col_num = max_cols_adj // 2 if col_num >= 1: cols_to_keep = [ - x for x in range(self.frame.shape[1]) + x + for x in range(self.frame.shape[1]) if x < col_num or x >= len(self.frame.columns) - col_num ] self.tr_frame = self.tr_frame.iloc[:, cols_to_keep] @@ -830,7 +831,8 @@ def _truncate_vertically(self): row_num = max_rows_adj // 2 if row_num >= 1: rows_to_keep = [ - x for x in range(self.frame.shape[0]) + x + for x in range(self.frame.shape[0]) if x < row_num or x >= len(self.frame) - row_num ] self.tr_frame = self.tr_frame.iloc[rows_to_keep, :] From 03d0303a5f39a7f8d21e25ab2390b2ed1a169a42 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 17:44:01 +0700 Subject: [PATCH 53/75] LINT: remove extra concat import --- pandas/io/formats/format.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 0e6fe5b2a97a9..519634dca5c26 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -263,8 +263,6 @@ def __init__( self._chk_truncate() def _chk_truncate(self) -> None: - from pandas.core.reshape.concat import concat - self.tr_row_num: Optional[int] min_rows = self.min_rows From 72612437201799ab682e6fced247e14806f25511 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 20:24:11 +0700 Subject: [PATCH 54/75] TYP: remove type casting --- pandas/io/formats/format.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 519634dca5c26..007c6c6708b42 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -920,15 +920,15 @@ def _truncate_strcols(self, strcols): if self.is_truncated_vertically: n_header_rows = len(str_index) - len(self.tr_frame) row_num = self.tr_row_num - # cast here since if is_truncated_vertically is True - # self.tr_row_num is not None - row_num = cast(int, row_num) for ix, col in enumerate(strcols): # infer from above row cwidth = self.adj.len(col[row_num]) - is_dot_col = False + if self.is_truncated_horizontally: is_dot_col = ix == self.tr_col_num + 1 + else: + is_dot_col = False + if cwidth > 3 or is_dot_col: dots = "..." else: @@ -941,6 +941,7 @@ def _truncate_strcols(self, strcols): dot_mode = "right" else: dot_mode = "right" + dot_str = self.adj.justify([dots], cwidth, mode=dot_mode)[0] col.insert(row_num + n_header_rows, dot_str) From 498ad90124ef108b3b4cf34aa19bb798d39df7d7 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 20:43:05 +0700 Subject: [PATCH 55/75] REF: extract _insert_dot_separator_vertical method --- pandas/io/formats/format.py | 50 +++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 007c6c6708b42..9223d01ad5567 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -913,38 +913,44 @@ def _to_str_columns(self) -> List[List[str]]: def _truncate_strcols(self, strcols): str_index = self._get_formatted_index(self.tr_frame) + index_length = len(str_index) if self.is_truncated_horizontally: strcols.insert(self.tr_col_num + 1, [" ..."] * (len(str_index))) if self.is_truncated_vertically: - n_header_rows = len(str_index) - len(self.tr_frame) - row_num = self.tr_row_num - for ix, col in enumerate(strcols): - # infer from above row - cwidth = self.adj.len(col[row_num]) + strcols = self._insert_dot_separator_vertical(strcols, index_length) - if self.is_truncated_horizontally: - is_dot_col = ix == self.tr_col_num + 1 - else: - is_dot_col = False + return strcols - if cwidth > 3 or is_dot_col: - dots = "..." - else: - dots = ".." + def _insert_dot_separator_vertical( + self, strcols: List[List[str]], index_length: int + ) -> List[List[str]]: + n_header_rows = index_length - len(self.tr_frame) + row_num = self.tr_row_num + for ix, col in enumerate(strcols): + cwidth = self.adj.len(col[row_num]) - if ix == 0: - dot_mode = "left" - elif is_dot_col: - cwidth = 4 - dot_mode = "right" - else: - dot_mode = "right" + if self.is_truncated_horizontally: + is_dot_col = ix == self.tr_col_num + 1 + else: + is_dot_col = False + + if cwidth > 3 or is_dot_col: + dots = "..." + else: + dots = ".." - dot_str = self.adj.justify([dots], cwidth, mode=dot_mode)[0] - col.insert(row_num + n_header_rows, dot_str) + if ix == 0: + dot_mode = "left" + elif is_dot_col: + cwidth = 4 + dot_mode = "right" + else: + dot_mode = "right" + dot_str = self.adj.justify([dots], cwidth, mode=dot_mode)[0] + col.insert(row_num + n_header_rows, dot_str) return strcols def write_result(self, buf: IO[str]) -> None: From 0bc30eed51a508912a48f9f345fb6e164d5f7742 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 20:45:50 +0700 Subject: [PATCH 56/75] REF: extract _insert_dot_separator_horizontal --- pandas/io/formats/format.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 9223d01ad5567..03da2ac2170d8 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -916,13 +916,19 @@ def _truncate_strcols(self, strcols): index_length = len(str_index) if self.is_truncated_horizontally: - strcols.insert(self.tr_col_num + 1, [" ..."] * (len(str_index))) + strcols = self._insert_dot_separator_horizontal(strcols, index_length) if self.is_truncated_vertically: strcols = self._insert_dot_separator_vertical(strcols, index_length) return strcols + def _insert_dot_separator_horizontal( + self, strcols: List[List[str]], index_length: int + ) -> List[List[str]]: + strcols.insert(self.tr_col_num + 1, [" ..."] * index_length) + return strcols + def _insert_dot_separator_vertical( self, strcols: List[List[str]], index_length: int ) -> List[List[str]]: From 37f9312f0ceda1029a77a9ab3d3bbe8ef6563030 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 20:47:29 +0700 Subject: [PATCH 57/75] CLN: _truncate_strcols -> _insert_dot_separators After previous methods extractions it became more clear that this method actually only inserts dots to the already truncated strcols. --- pandas/io/formats/format.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 03da2ac2170d8..8e73b533e0ecf 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -907,11 +907,11 @@ def _to_str_columns(self) -> List[List[str]]: strcols = self._get_strcols() if self.is_truncated: - strcols = self._truncate_strcols(strcols) + strcols = self._insert_dot_separators(strcols) return strcols - def _truncate_strcols(self, strcols): + def _insert_dot_separators(self, strcols): str_index = self._get_formatted_index(self.tr_frame) index_length = len(str_index) From 2d3fad267d1beb86bc2efb00cf57f2251106a387 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 20:55:17 +0700 Subject: [PATCH 58/75] TYP: add annotations on new methods --- pandas/io/formats/format.py | 21 +++++++++------------ pandas/io/formats/html.py | 3 --- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 8e73b533e0ecf..eada0b169159c 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -797,11 +797,10 @@ def _truncate(self) -> None: if self.is_truncated_vertically: self._truncate_vertically() - def _truncate_horizontally(self): + def _truncate_horizontally(self) -> None: # cast here since if is_truncated_horizontally is True # max_cols_adj is not None max_cols_adj = cast(int, self.max_cols_adj) - col_num = max_cols_adj // 2 if col_num >= 1: cols_to_keep = [ @@ -816,13 +815,11 @@ def _truncate_horizontally(self): slicer = itemgetter(*cols_to_keep) self._formatters = slicer(self.formatters) else: - max_cols = cast(int, self.max_cols) - col_num = max_cols + col_num = cast(int, self.max_cols) self.tr_frame = self.tr_frame.iloc[:, :col_num] - self.tr_col_num = col_num - def _truncate_vertically(self): + def _truncate_vertically(self) -> None: # cast here since if is_truncated_vertically is True # max_rows_adj is not None max_rows_adj = cast(int, self.max_rows_adj) @@ -835,11 +832,11 @@ def _truncate_vertically(self): ] self.tr_frame = self.tr_frame.iloc[rows_to_keep, :] else: - row_num = self.max_rows + row_num = cast(int, self.max_rows) self.tr_frame = self.tr_frame.iloc[:row_num, :] self.tr_row_num = row_num - def _get_strcols_without_index(self): + def _get_strcols_without_index(self) -> List[List[str]]: # TODO check this comment validity # this method is not used by to_html where self.col_space # could be a string so safe to cast @@ -911,7 +908,7 @@ def _to_str_columns(self) -> List[List[str]]: return strcols - def _insert_dot_separators(self, strcols): + def _insert_dot_separators(self, strcols: List[List[str]]) -> List[List[str]]: str_index = self._get_formatted_index(self.tr_frame) index_length = len(str_index) @@ -971,10 +968,10 @@ def write_result(self, buf: IO[str]) -> None: buf.write(self._dimensions_info) @property - def _dimensions_info(self): + def _dimensions_info(self) -> str: return f"\n\n[{len(self.frame)} rows x {len(self.frame.columns)} columns]" - def _get_string_representation(self): + def _get_string_representation(self) -> str: if self.frame.empty: info_line = ( f"Empty {type(self.frame).__name__}\n" @@ -996,7 +993,7 @@ def _get_string_representation(self): # max_cols == 0. Try to fit frame to terminal return self._fit_strcols_to_terminal_width(strcols) - def _fit_strcols_to_terminal_width(self, strcols): + def _fit_strcols_to_terminal_width(self, strcols) -> str: from pandas import Series lines = self.adj.adjoin(1, *strcols).split("\n") diff --git a/pandas/io/formats/html.py b/pandas/io/formats/html.py index ce98ac930f864..c8eb89afdd849 100644 --- a/pandas/io/formats/html.py +++ b/pandas/io/formats/html.py @@ -456,9 +456,6 @@ def _write_hierarchical_rows( # Insert ... row and adjust idx_values and # level_lengths to take this into account. ins_row = self.fmt.tr_row_num - # cast here since if is_truncated_vertically is True - # self.fmt.tr_row_num is not None - ins_row = cast(int, ins_row) inserted = False for lnum, records in enumerate(level_lengths): rec_new = {} From 58084a954412c25a3686739e227abc6b0db41d3f Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Fri, 18 Sep 2020 21:03:03 +0700 Subject: [PATCH 59/75] TYP: replace casting with asserts --- pandas/io/formats/format.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index eada0b169159c..1df6d65d9e4b0 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -798,10 +798,8 @@ def _truncate(self) -> None: self._truncate_vertically() def _truncate_horizontally(self) -> None: - # cast here since if is_truncated_horizontally is True - # max_cols_adj is not None - max_cols_adj = cast(int, self.max_cols_adj) - col_num = max_cols_adj // 2 + assert self.max_cols_adj is not None + col_num = self.max_cols_adj // 2 if col_num >= 1: cols_to_keep = [ x @@ -820,10 +818,8 @@ def _truncate_horizontally(self) -> None: self.tr_col_num = col_num def _truncate_vertically(self) -> None: - # cast here since if is_truncated_vertically is True - # max_rows_adj is not None - max_rows_adj = cast(int, self.max_rows_adj) - row_num = max_rows_adj // 2 + assert self.max_rows_adj is not None + row_num = self.max_rows_adj // 2 if row_num >= 1: rows_to_keep = [ x From c1beb4d2ab62c516144e2474cf3f97ff632a1cba Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Sat, 19 Sep 2020 13:31:53 +0700 Subject: [PATCH 60/75] DOC: docstring to _get_number_of_auxillary_rows --- pandas/io/formats/format.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 1df6d65d9e4b0..121ca97487834 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -758,6 +758,7 @@ def max_rows_adj(self) -> Optional[int]: return max_rows def _get_number_of_auxillary_rows(self) -> int: + """Get number of rows occupied by prompt, dots and dimension info.""" dot_row = 1 prompt_row = 1 num_rows = dot_row + prompt_row From 8db72214f6a058302f91072f8b8298c8f144b03e Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Sat, 19 Sep 2020 13:32:42 +0700 Subject: [PATCH 61/75] CLN: place _is_screen_narrow/short funcs together --- pandas/io/formats/format.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 121ca97487834..a13a41dcbcb5f 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -734,6 +734,9 @@ def max_cols_adj(self) -> Optional[int]: def _is_screen_narrow(self, max_width) -> bool: return bool(self.max_cols == 0 and len(self.frame.columns) > max_width) + def _is_screen_short(self, max_height) -> bool: + return bool(self.max_rows == 0 and len(self.frame) > max_height) + @property def max_rows_adj(self) -> Optional[int]: """Number of rows fitting the screen.""" @@ -771,9 +774,6 @@ def _get_number_of_auxillary_rows(self) -> int: return num_rows - def _is_screen_short(self, max_height) -> bool: - return bool(self.max_rows == 0 and len(self.frame) > max_height) - @property def is_truncated_horizontally(self) -> bool: return bool(self.max_cols_adj and (len(self.columns) > self.max_cols_adj)) From a5e21b3c8de6161749c37d634531efb8c63411b9 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Sat, 19 Sep 2020 13:40:09 +0700 Subject: [PATCH 62/75] DOC: docstrings for _truncate_horizontally/vert --- pandas/io/formats/format.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index a13a41dcbcb5f..12dcbc23086e0 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -799,6 +799,13 @@ def _truncate(self) -> None: self._truncate_vertically() def _truncate_horizontally(self) -> None: + """Remove columns, which are not to be displayed and adjust formatters. + + Attributes affected: + - tr_frame + - formatters + - tr_col_num + """ assert self.max_cols_adj is not None col_num = self.max_cols_adj // 2 if col_num >= 1: @@ -819,6 +826,12 @@ def _truncate_horizontally(self) -> None: self.tr_col_num = col_num def _truncate_vertically(self) -> None: + """Remove rows, which are not to be displayed. + + Attributes affected: + - tr_frame + - tr_row_num + """ assert self.max_rows_adj is not None row_num = self.max_rows_adj // 2 if row_num >= 1: From 4524bc7ac7f8bbae7f836c255d032844abf723be Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Sat, 19 Sep 2020 13:49:42 +0700 Subject: [PATCH 63/75] TYP: add for strcols, justify and sparsify --- pandas/io/formats/format.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 12dcbc23086e0..9f9c44b821c94 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -583,7 +583,7 @@ def __init__( ): self.frame = frame self.show_index_names = index_names - self.sparsify = sparsify + self.sparsify = sparsify # type: ignore[assignment] self.float_format = float_format # Ignoring error @@ -612,7 +612,7 @@ def __init__( self.show_dimensions = show_dimensions self.table_id = table_id self.render_links = render_links - self.justify = justify + self.justify = justify # type: ignore[assignment] self.bold_rows = bold_rows self.escape = escape @@ -624,11 +624,11 @@ def __init__( self.adj = get_adjustment() @property - def sparsify(self): + def sparsify(self) -> bool: return self._sparsify @sparsify.setter - def sparsify(self, sparsify): + def sparsify(self, sparsify: Optional[bool]) -> None: if sparsify is None: self._sparsify = get_option("display.multi_sparse") else: @@ -653,11 +653,11 @@ def formatters(self, formatters: Optional[FormattersType]) -> None: assert self._formatters is not None @property - def justify(self): + def justify(self) -> str: return self._justify @justify.setter - def justify(self, justify): + def justify(self, justify: Optional[str]) -> None: if justify is None: self._justify = get_option("display.colheader_justify") else: @@ -853,10 +853,9 @@ def _get_strcols_without_index(self) -> List[List[str]]: col_space = {k: cast(int, v) for k, v in self.col_space.items()} frame = self.tr_frame - # may include levels names also + strcols: List[List[str]] = [] if not is_list_like(self.header) and not self.header: - strcols = [] for i, c in enumerate(frame): fmt_values = self._format_col(i) fmt_values = _make_fixed_width( @@ -881,7 +880,6 @@ def _get_strcols_without_index(self) -> List[List[str]]: for x in str_columns: x.append("") - strcols = [] for i, c in enumerate(frame): cheader = str_columns[i] header_colwidth = max( From 186c98ff2e51fa0d3cec069edd0e8544c01aaab3 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Sat, 19 Sep 2020 14:30:02 +0700 Subject: [PATCH 64/75] REF: replace sparsify setter with initialization --- pandas/io/formats/format.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 9f9c44b821c94..30810cdfcb3a0 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -583,7 +583,7 @@ def __init__( ): self.frame = frame self.show_index_names = index_names - self.sparsify = sparsify # type: ignore[assignment] + self.sparsify = self._initialize_sparsify(sparsify) self.float_format = float_format # Ignoring error @@ -623,16 +623,10 @@ def __init__( self._truncate() self.adj = get_adjustment() - @property - def sparsify(self) -> bool: - return self._sparsify - - @sparsify.setter - def sparsify(self, sparsify: Optional[bool]) -> None: + def _initialize_sparsify(self, sparsify: Optional[bool]) -> bool: if sparsify is None: - self._sparsify = get_option("display.multi_sparse") - else: - self._sparsify = sparsify + return get_option("display.multi_sparse") + return sparsify @property def formatters(self) -> FormattersType: From 52d6a1611052fc0b66e0e075d13c51f856a0c0d7 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Sat, 19 Sep 2020 14:41:31 +0700 Subject: [PATCH 65/75] REF: replace formatters setter with initialization --- pandas/io/formats/format.py | 36 ++++++++---------------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 30810cdfcb3a0..1f98fe7aacc0a 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -453,20 +453,13 @@ def get_adjustment() -> TextAdjustment: class TableFormatter: show_dimensions: Union[bool, str] + formatters: FormattersType @property def is_truncated(self) -> bool: self._is_truncated: bool return self._is_truncated - @property - def formatters(self) -> FormattersType: - return self._formatters - - @formatters.setter - def formatters(self, formatters: FormattersType) -> None: - self._formatters: FormattersType = formatters - @property def columns(self) -> Index: return self._columns @@ -585,15 +578,7 @@ def __init__( self.show_index_names = index_names self.sparsify = self._initialize_sparsify(sparsify) self.float_format = float_format - - # Ignoring error - # expression has type "Union[List[Callable[..., Any]], - # Tuple[Callable[..., Any], ...], - # Mapping[Union[str, int], Callable[..., Any]], None]", - # variable has type "Union[List[Callable[..., Any]], - # Tuple[Callable[..., Any], ...], Mapping[Union[str, int], - # Callable[..., Any]]]") - self.formatters = formatters # type: ignore[assignment] + self.formatters = self._initialize_formatters(formatters) self.na_rep = na_rep self.decimal = decimal @@ -628,23 +613,18 @@ def _initialize_sparsify(self, sparsify: Optional[bool]) -> bool: return get_option("display.multi_sparse") return sparsify - @property - def formatters(self) -> FormattersType: - return self._formatters - - @formatters.setter - def formatters(self, formatters: Optional[FormattersType]) -> None: - self._formatters: FormattersType + def _initialize_formatters( + self, formatters: Optional[FormattersType] + ) -> FormattersType: if formatters is None: - self._formatters = {} + return {} elif len(self.frame.columns) == len(formatters) or isinstance(formatters, dict): - self._formatters = formatters + return formatters else: raise ValueError( f"Formatters length({len(formatters)}) should match " f"DataFrame number of columns({len(self.frame.columns)})" ) - assert self._formatters is not None @property def justify(self) -> str: @@ -813,7 +793,7 @@ def _truncate_horizontally(self) -> None: # truncate formatter if isinstance(self.formatters, (list, tuple)): slicer = itemgetter(*cols_to_keep) - self._formatters = slicer(self.formatters) + self.formatters = slicer(self.formatters) else: col_num = cast(int, self.max_cols) self.tr_frame = self.tr_frame.iloc[:, :col_num] From e7483708ed800b427d5d07ba0f4a68738553b04d Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Sat, 19 Sep 2020 20:50:02 +0700 Subject: [PATCH 66/75] REF: replace col_space setter with initialization --- pandas/io/formats/format.py | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 1f98fe7aacc0a..8916e4a0c78a6 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -581,12 +581,7 @@ def __init__( self.formatters = self._initialize_formatters(formatters) self.na_rep = na_rep self.decimal = decimal - - # Ignoring error - # expression has type "Union[str, int, Sequence[Union[str, int]], - # Mapping[Optional[Hashable], Union[str, int]], None]", - # variable has type "Mapping[Optional[Hashable], Union[str, int]]" - self.col_space = col_space # type: ignore[assignment] + self.col_space = self._initialize_colspace(col_space) self.header = header self.index = index @@ -650,33 +645,31 @@ def columns(self, columns: Optional[Sequence[str]]) -> None: self._columns = self.frame.columns assert self._columns is not None - @property - def col_space(self) -> ColspaceType: - return self._col_space + def _initialize_colspace( + self, col_space: Optional[ColspaceArgType] + ) -> ColspaceType: + result: ColspaceType - @col_space.setter - def col_space(self, col_space: Optional[ColspaceArgType]) -> None: - self._col_space: ColspaceType if col_space is None: - self._col_space = {} + result = {} elif isinstance(col_space, (int, str)): - self._col_space = {"": col_space} - self._col_space.update({column: col_space for column in self.frame.columns}) + result = {"": col_space} + result.update({column: col_space for column in self.frame.columns}) elif isinstance(col_space, Mapping): for column in col_space.keys(): if column not in self.frame.columns and column != "": raise ValueError( f"Col_space is defined for an unknown column: {column}" ) - self._col_space = col_space + result = col_space else: if len(self.frame.columns) != len(col_space): raise ValueError( f"Col_space length({len(col_space)}) should match " f"DataFrame number of columns({len(self.frame.columns)})" ) - self._col_space = dict(zip(self.frame.columns, col_space)) - assert self._col_space is not None + result = dict(zip(self.frame.columns, col_space)) + return result @property def max_rows_displayed(self) -> int: From 2721f9838dcab436981c7d61e3a2d607a7187c9f Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Sat, 19 Sep 2020 20:56:53 +0700 Subject: [PATCH 67/75] REF: replace columns setter with initialization --- pandas/io/formats/format.py | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 8916e4a0c78a6..881d2b2eefb85 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -454,20 +454,13 @@ class TableFormatter: show_dimensions: Union[bool, str] formatters: FormattersType + columns: Index @property def is_truncated(self) -> bool: self._is_truncated: bool return self._is_truncated - @property - def columns(self) -> Index: - return self._columns - - @columns.setter - def columns(self, columns: Index) -> None: - self._columns: Index = columns - @property def should_show_dimensions(self) -> bool: return self.show_dimensions is True or ( @@ -582,7 +575,6 @@ def __init__( self.na_rep = na_rep self.decimal = decimal self.col_space = self._initialize_colspace(col_space) - self.header = header self.index = index self.line_width = line_width @@ -595,10 +587,7 @@ def __init__( self.justify = justify # type: ignore[assignment] self.bold_rows = bold_rows self.escape = escape - - # Ignoring error: - # expression has type "Optional[Sequence[str]]", variable has type "Index" - self.columns = columns # type: ignore[assignment] + self.columns = self._initialize_columns(columns) self._truncate() self.adj = get_adjustment() @@ -632,18 +621,13 @@ def justify(self, justify: Optional[str]) -> None: else: self._justify = justify - @property - def columns(self) -> Index: - return self._columns - - @columns.setter - def columns(self, columns: Optional[Sequence[str]]) -> None: + def _initialize_columns(self, columns: Optional[Sequence[str]]) -> Index: if columns is not None: - self._columns = ensure_index(columns) - self.frame = self.frame[self._columns] + cols = ensure_index(columns) + self.frame = self.frame[cols] + return cols else: - self._columns = self.frame.columns - assert self._columns is not None + return self.frame.columns def _initialize_colspace( self, col_space: Optional[ColspaceArgType] From 92c10abbd0cd70f20d68928f8824fcd163186c9a Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Sat, 19 Sep 2020 21:01:50 +0700 Subject: [PATCH 68/75] REF: replace justify setter with initialization --- pandas/io/formats/format.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 881d2b2eefb85..c9523827e56d2 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -584,7 +584,7 @@ def __init__( self.show_dimensions = show_dimensions self.table_id = table_id self.render_links = render_links - self.justify = justify # type: ignore[assignment] + self.justify = self._initialize_justify(justify) self.bold_rows = bold_rows self.escape = escape self.columns = self._initialize_columns(columns) @@ -610,16 +610,11 @@ def _initialize_formatters( f"DataFrame number of columns({len(self.frame.columns)})" ) - @property - def justify(self) -> str: - return self._justify - - @justify.setter - def justify(self, justify: Optional[str]) -> None: + def _initialize_justify(self, justify: Optional[str]) -> str: if justify is None: - self._justify = get_option("display.colheader_justify") + return get_option("display.colheader_justify") else: - self._justify = justify + return justify def _initialize_columns(self, columns: Optional[Sequence[str]]) -> Index: if columns is not None: From 8849a98cdbbdb93e0dd2ca2f44e6d8f7d0c42996 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Sat, 19 Sep 2020 21:42:54 +0700 Subject: [PATCH 69/75] CLN: reorder terminal/screen related methods --- pandas/io/formats/format.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index c9523827e56d2..ff2b54c48c218 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -654,10 +654,6 @@ def _initialize_colspace( def max_rows_displayed(self) -> int: return min(self.max_rows or len(self.frame), len(self.frame)) - def _is_in_terminal(self) -> bool: - """Check if the output is to be shown in terminal.""" - return bool(self.max_cols == 0 or self.max_rows == 0) - @property def max_cols_adj(self) -> Optional[int]: """Number of columns fitting the screen.""" @@ -677,12 +673,6 @@ def max_cols_adj(self) -> Optional[int]: self._max_cols_adj = self.max_cols return self._max_cols_adj - def _is_screen_narrow(self, max_width) -> bool: - return bool(self.max_cols == 0 and len(self.frame.columns) > max_width) - - def _is_screen_short(self, max_height) -> bool: - return bool(self.max_rows == 0 and len(self.frame) > max_height) - @property def max_rows_adj(self) -> Optional[int]: """Number of rows fitting the screen.""" @@ -706,6 +696,16 @@ def max_rows_adj(self) -> Optional[int]: max_rows = min(self.min_rows, max_rows) return max_rows + def _is_in_terminal(self) -> bool: + """Check if the output is to be shown in terminal.""" + return bool(self.max_cols == 0 or self.max_rows == 0) + + def _is_screen_narrow(self, max_width) -> bool: + return bool(self.max_cols == 0 and len(self.frame.columns) > max_width) + + def _is_screen_short(self, max_height) -> bool: + return bool(self.max_rows == 0 and len(self.frame) > max_height) + def _get_number_of_auxillary_rows(self) -> int: """Get number of rows occupied by prompt, dots and dimension info.""" dot_row = 1 From 1b9ff3f0883de5868a4b01f47947b683755a427b Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Sat, 19 Sep 2020 21:56:20 +0700 Subject: [PATCH 70/75] TYP: replace casting with actual int conversion --- pandas/io/formats/format.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index ff2b54c48c218..ab2cefbe398f3 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -793,19 +793,16 @@ def _truncate_vertically(self) -> None: self.tr_row_num = row_num def _get_strcols_without_index(self) -> List[List[str]]: - # TODO check this comment validity - # this method is not used by to_html where self.col_space - # could be a string so safe to cast - col_space = {k: cast(int, v) for k, v in self.col_space.items()} - - frame = self.tr_frame strcols: List[List[str]] = [] if not is_list_like(self.header) and not self.header: - for i, c in enumerate(frame): + for i, c in enumerate(self.tr_frame): fmt_values = self._format_col(i) fmt_values = _make_fixed_width( - fmt_values, self.justify, minimum=col_space.get(c, 0), adj=self.adj + strings=fmt_values, + justify=self.justify, + minimum=int(self.col_space.get(c, 0)), + adj=self.adj, ) strcols.append(fmt_values) return strcols @@ -820,16 +817,16 @@ def _get_strcols_without_index(self) -> List[List[str]]: ) str_columns = [[label] for label in self.header] else: - str_columns = self._get_formatted_column_labels(frame) + str_columns = self._get_formatted_column_labels(self.tr_frame) if self.show_row_idx_names: for x in str_columns: x.append("") - for i, c in enumerate(frame): + for i, c in enumerate(self.tr_frame): cheader = str_columns[i] header_colwidth = max( - col_space.get(c, 0), *(self.adj.len(x) for x in cheader) + int(self.col_space.get(c, 0)), *(self.adj.len(x) for x in cheader) ) fmt_values = self._format_col(i) fmt_values = _make_fixed_width( From dcb2b7003fb7bb8ab29be7f9fa8307930e4afb0a Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Sat, 19 Sep 2020 22:01:42 +0700 Subject: [PATCH 71/75] TYP: assert instead of casting --- pandas/io/formats/format.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index ab2cefbe398f3..6a3b3981b91d4 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -808,8 +808,7 @@ def _get_strcols_without_index(self) -> List[List[str]]: return strcols if is_list_like(self.header): - # cast here since can't be bool if is_list_like - self.header = cast(List[str], self.header) + assert isinstance(self.header, list) if len(self.header) != len(self.columns): raise ValueError( f"Writing {len(self.columns)} cols " From e2b75e509b0306e109831774d91d0e3f47abf523 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Sat, 19 Sep 2020 22:58:12 +0700 Subject: [PATCH 72/75] CLN: max_rows/cols_adj -> max_rows/cols_fitted --- pandas/io/formats/format.py | 44 ++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 6a3b3981b91d4..2a3ad5983a76f 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -655,27 +655,27 @@ def max_rows_displayed(self) -> int: return min(self.max_rows or len(self.frame), len(self.frame)) @property - def max_cols_adj(self) -> Optional[int]: + def max_cols_fitted(self) -> Optional[int]: """Number of columns fitting the screen.""" - self._max_cols_adj: Optional[int] + self._max_cols_fitted: Optional[int] - if hasattr(self, "_max_cols_adj"): - return self._max_cols_adj + if hasattr(self, "_max_cols_fitted"): + return self._max_cols_fitted if not self._is_in_terminal(): - self._max_cols_adj = self.max_cols - return self._max_cols_adj + self._max_cols_fitted = self.max_cols + return self._max_cols_fitted width, _ = get_terminal_size() if self._is_screen_narrow(width): - self._max_cols_adj = width + self._max_cols_fitted = width else: - self._max_cols_adj = self.max_cols - return self._max_cols_adj + self._max_cols_fitted = self.max_cols + return self._max_cols_fitted @property - def max_rows_adj(self) -> Optional[int]: - """Number of rows fitting the screen.""" + def max_rows_fitted(self) -> Optional[int]: + """Number of rows with data fitting the screen.""" if not self._is_in_terminal(): return self.max_rows @@ -722,11 +722,11 @@ def _get_number_of_auxillary_rows(self) -> int: @property def is_truncated_horizontally(self) -> bool: - return bool(self.max_cols_adj and (len(self.columns) > self.max_cols_adj)) + return bool(self.max_cols_fitted and (len(self.columns) > self.max_cols_fitted)) @property def is_truncated_vertically(self) -> bool: - return bool(self.max_rows_adj and (len(self.frame) > self.max_rows_adj)) + return bool(self.max_rows_fitted and (len(self.frame) > self.max_rows_fitted)) @property def is_truncated(self) -> bool: @@ -752,8 +752,8 @@ def _truncate_horizontally(self) -> None: - formatters - tr_col_num """ - assert self.max_cols_adj is not None - col_num = self.max_cols_adj // 2 + assert self.max_cols_fitted is not None + col_num = self.max_cols_fitted // 2 if col_num >= 1: cols_to_keep = [ x @@ -778,8 +778,8 @@ def _truncate_vertically(self) -> None: - tr_frame - tr_row_num """ - assert self.max_rows_adj is not None - row_num = self.max_rows_adj // 2 + assert self.max_rows_fitted is not None + row_num = self.max_rows_fitted // 2 if row_num >= 1: rows_to_keep = [ x @@ -967,10 +967,10 @@ def _fit_strcols_to_terminal_width(self, strcols) -> str: n_cols = len(col_lens) # subtract index column - max_cols_adj = n_cols - self.index + max_cols_fitted = n_cols - self.index # GH-21180. Ensure that we print at least two. - max_cols_adj = max(max_cols_adj, 2) - self._max_cols_adj = max_cols_adj + max_cols_fitted = max(max_cols_fitted, 2) + self._max_cols_fitted = max_cols_fitted # Call again _truncate to cut frame appropriately # and then generate string representation @@ -996,8 +996,8 @@ def _join_multiline(self, *args) -> str: nbins = len(col_bins) if self.is_truncated_vertically: - assert self.max_rows_adj is not None - nrows = self.max_rows_adj + 1 + assert self.max_rows_fitted is not None + nrows = self.max_rows_fitted + 1 else: nrows = len(self.frame) From 72db492944ee721521d9994deb6d016f4714483a Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Sat, 19 Sep 2020 23:12:35 +0700 Subject: [PATCH 73/75] TYP: move _is_truncated type declaration on top --- 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 2a3ad5983a76f..0abfd68e83b11 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -455,10 +455,10 @@ class TableFormatter: show_dimensions: Union[bool, str] formatters: FormattersType columns: Index + _is_truncated: bool @property def is_truncated(self) -> bool: - self._is_truncated: bool return self._is_truncated @property From 027a8280472752180dd810c319c597f10117e623 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Sun, 20 Sep 2020 01:03:25 +0700 Subject: [PATCH 74/75] Revert "TYP: assert instead of casting" This reverts commit dcb2b7003fb7bb8ab29be7f9fa8307930e4afb0a. --- pandas/io/formats/format.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 0abfd68e83b11..2325d565c4e57 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -808,7 +808,8 @@ def _get_strcols_without_index(self) -> List[List[str]]: return strcols if is_list_like(self.header): - assert isinstance(self.header, list) + # cast here since can't be bool if is_list_like + self.header = cast(List[str], self.header) if len(self.header) != len(self.columns): raise ValueError( f"Writing {len(self.columns)} cols " From 9203a5429dcfb246dce85a678dd992712e7477f8 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Sun, 20 Sep 2020 01:24:33 +0700 Subject: [PATCH 75/75] REF: assign to max_cols/rows_fitted at init This allows one avoid multiple calls of the properties, which were there originally. --- pandas/io/formats/format.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 2325d565c4e57..7eb31daa894c9 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -589,6 +589,9 @@ def __init__( self.escape = escape self.columns = self._initialize_columns(columns) + self.max_cols_fitted = self._calc_max_cols_fitted() + self.max_rows_fitted = self._calc_max_rows_fitted() + self._truncate() self.adj = get_adjustment() @@ -654,27 +657,18 @@ def _initialize_colspace( def max_rows_displayed(self) -> int: return min(self.max_rows or len(self.frame), len(self.frame)) - @property - def max_cols_fitted(self) -> Optional[int]: + def _calc_max_cols_fitted(self) -> Optional[int]: """Number of columns fitting the screen.""" - self._max_cols_fitted: Optional[int] - - if hasattr(self, "_max_cols_fitted"): - return self._max_cols_fitted - if not self._is_in_terminal(): - self._max_cols_fitted = self.max_cols - return self._max_cols_fitted + return self.max_cols width, _ = get_terminal_size() if self._is_screen_narrow(width): - self._max_cols_fitted = width + return width else: - self._max_cols_fitted = self.max_cols - return self._max_cols_fitted + return self.max_cols - @property - def max_rows_fitted(self) -> Optional[int]: + def _calc_max_rows_fitted(self) -> Optional[int]: """Number of rows with data fitting the screen.""" if not self._is_in_terminal(): return self.max_rows @@ -971,7 +965,7 @@ def _fit_strcols_to_terminal_width(self, strcols) -> str: max_cols_fitted = n_cols - self.index # GH-21180. Ensure that we print at least two. max_cols_fitted = max(max_cols_fitted, 2) - self._max_cols_fitted = max_cols_fitted + self.max_cols_fitted = max_cols_fitted # Call again _truncate to cut frame appropriately # and then generate string representation