From d917e072a918f93eff4ff193b8876ef2ad55f1da Mon Sep 17 00:00:00 2001 From: benjamin Date: Thu, 1 May 2014 22:37:49 +0200 Subject: [PATCH] ENH: Centrally truncate DataFrames --- doc/source/_static/trunc_after.png | Bin 0 -> 29195 bytes doc/source/_static/trunc_before.png | Bin 0 -> 50913 bytes doc/source/v0.14.0.txt | 20 ++ pandas/core/format.py | 234 ++++++++++------ pandas/tests/test_format.py | 400 +++++++++++++++++++++++++++- 5 files changed, 557 insertions(+), 97 deletions(-) create mode 100644 doc/source/_static/trunc_after.png create mode 100644 doc/source/_static/trunc_before.png diff --git a/doc/source/_static/trunc_after.png b/doc/source/_static/trunc_after.png new file mode 100644 index 0000000000000000000000000000000000000000..950690de8d1ee7c788938106af79217f65bf1657 GIT binary patch literal 29195 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 h`G6zizrUP+f^*VuTSt(*o&f` +- :ref:`Groupby API Changes ` + - :ref:`Performance Improvements ` - :ref:`Prior Deprecations ` @@ -216,6 +218,24 @@ API changes Display Changes ~~~~~~~~~~~~~~~ +- The default way of printing large DataFrames has changed. DataFrames + exceeding ``max_rows`` and/or ``max_columns`` are now displayed in a + centrally truncated view, consistent with the printing of a + :class:`pandas.Series` (:issue:`5603`). + + In previous versions, a DataFrame was truncated once the dimension + constraints were reached and an ellipse (...) signaled that part of + the data was cut off. + + .. image:: _static/trunc_before.png + :alt: The previous look of truncate. + + In the current version, large DataFrames are centrally truncated, + showing a preview of head and tail in both dimensions. + + .. image:: _static/trunc_after.png + :alt: The new look. + - allow option ``'truncate'`` for ``display.show_dimensions`` to only show the dimensions if the frame is truncated (:issue:`6547`). diff --git a/pandas/core/format.py b/pandas/core/format.py index 49e98fe9911c5..0905640c85ac1 100644 --- a/pandas/core/format.py +++ b/pandas/core/format.py @@ -1,8 +1,10 @@ + #coding: utf-8 from __future__ import print_function # pylint: disable=W0141 import sys +import re from pandas.core.base import PandasObject from pandas.core.common import adjoin, isnull, notnull @@ -309,38 +311,65 @@ def __init__(self, frame, buf=None, columns=None, col_space=None, else: self.columns = frame.columns + self._chk_truncate() + + def _chk_truncate(self): + from pandas.tools.merge import concat + + truncate_h = self.max_cols and (len(self.columns) > self.max_cols) + truncate_v = self.max_rows and (len(self.frame) > self.max_rows) + + # Cut the data to the information actually printed + max_cols = self.max_cols + max_rows = self.max_rows + frame = self.frame + if truncate_h: + if max_cols > 1: + col_num = (max_cols // 2) + frame = concat( (frame.iloc[:,:col_num],frame.iloc[:,-col_num:]),axis=1 ) + else: + col_num = max_cols + frame = frame.iloc[:,:max_cols] + self.tr_col_num = col_num + if truncate_v: + if max_rows > 1: + row_num = max_rows // 2 + frame = concat( (frame.iloc[:row_num,:],frame.iloc[-row_num:,:]) ) + else: + row_num = max_rows + frame = frame.iloc[:max_rows,:] + self.tr_row_num = row_num + + self.tr_frame = frame + self.truncate_h = truncate_h + self.truncate_v = truncate_v + self.is_truncated = self.truncate_h or self.truncate_v + def _to_str_columns(self): """ Render a DataFrame to a list of columns (as lists of strings). """ + _strlen = _strlen_func() + frame = self.tr_frame # may include levels names also - str_index = self._get_formatted_index() - str_columns = self._get_formatted_column_labels() - - _strlen = _strlen_func() + str_index = self._get_formatted_index(frame) - cols_to_show = self.columns[:self.max_cols] - self.truncated_h = truncate_h = self.max_cols and (len(self.columns) > self.max_cols) - self.truncated_v = truncate_v = self.max_rows and (len(self.frame) > self.max_rows) - self.is_truncated = self.truncated_h or self.truncated_v - if truncate_h: - cols_to_show = self.columns[:self.max_cols] - else: - cols_to_show = self.columns + str_columns = self._get_formatted_column_labels(frame) if self.header: stringified = [] - for i, c in enumerate(cols_to_show): - fmt_values = self._format_col(i) + col_headers = frame.columns + for i, c in enumerate(frame): cheader = str_columns[i] - max_colwidth = max(self.col_space or 0, *(_strlen(x) for x in cheader)) + fmt_values = self._format_col(i) + fmt_values = _make_fixed_width(fmt_values, self.justify, - minimum=max_colwidth, - truncated=truncate_v) + minimum=max_colwidth) + max_len = max(np.max([_strlen(x) for x in fmt_values]), max_colwidth) @@ -351,16 +380,47 @@ def _to_str_columns(self): stringified.append(cheader + fmt_values) else: - stringified = [_make_fixed_width(self._format_col(i), self.justify, - truncated=truncate_v) - for i, c in enumerate(cols_to_show)] + stringified = [] + for i, c in enumerate(frame): + formatter = self._get_formatter(i) + fmt_values = self._format_col(i) + fmt_values = _make_fixed_width(fmt_values, self.justify) + + stringified.append(fmt_values) strcols = stringified if self.index: strcols.insert(0, str_index) + + # Add ... to signal truncated + truncate_h = self.truncate_h + truncate_v = self.truncate_v + if truncate_h: - strcols.append(([''] * len(str_columns[-1])) - + (['...'] * min(len(self.frame), self.max_rows))) + col_num = self.tr_col_num + col_width = len(strcols[col_num][0]) # infer from column header + strcols.insert(col_num + 1, ['...'.center(col_width)] * (len(str_index))) + if truncate_v: + n_header_rows = len(str_index) - len(frame) + row_num = self.tr_row_num + for ix,col in enumerate(strcols): + cwidth = len(strcols[ix][row_num]) # infer from above row + is_dot_col = False + if truncate_h: + is_dot_col = ix == col_num + 1 + if cwidth > 3 or is_dot_col: + my_str = '...' + else: + my_str = '..' + + if ix == 0: + dot_str = my_str.ljust(cwidth) + elif is_dot_col: + dot_str = my_str.center(cwidth) + else: + dot_str = my_str.rjust(cwidth) + + strcols[ix].insert(row_num + n_header_rows, dot_str) return strcols @@ -510,9 +570,10 @@ def write(buf, frame, column_format, strcols, longtable=False): 'method') def _format_col(self, i): + frame = self.tr_frame formatter = self._get_formatter(i) return format_array( - (self.frame.iloc[:self.max_rows_displayed, i]).get_values(), + (frame.iloc[:, i]).get_values(), formatter, float_format=self.float_format, na_rep=self.na_rep, space=self.col_space ) @@ -533,16 +594,13 @@ def to_html(self, classes=None): raise TypeError('buf is not a file name and it has no write ' ' method') - def _get_formatted_column_labels(self): + def _get_formatted_column_labels(self,frame): from pandas.core.index import _sparsify def is_numeric_dtype(dtype): return issubclass(dtype.type, np.number) - if self.max_cols: - columns = self.columns[:self.max_cols] - else: - columns = self.columns + columns = frame.columns if isinstance(columns, MultiIndex): fmt_columns = columns.format(sparsify=False, adjoin=False) @@ -580,13 +638,10 @@ def has_index_names(self): def has_column_names(self): return _has_names(self.frame.columns) - def _get_formatted_index(self): + def _get_formatted_index(self,frame): # Note: this is only used by to_string(), not by to_html(). - if self.max_rows: - index = self.frame.index[:self.max_rows] - else: - index = self.frame.index - columns = self.frame.columns + index = frame.index + columns = frame.columns show_index_names = self.show_index_names and self.has_index_names show_col_names = (self.show_index_names and self.has_column_names) @@ -633,7 +688,7 @@ def __init__(self, formatter, classes=None, max_rows=None, max_cols=None): self.classes = classes self.frame = self.fmt.frame - self.columns = formatter.columns + self.columns = self.fmt.tr_frame.columns self.elements = [] self.bold_rows = self.fmt.kwds.get('bold_rows', False) self.escape = self.fmt.kwds.get('escape', True) @@ -724,6 +779,7 @@ def write_result(self, buf): _put_lines(buf, self.elements) def _write_header(self, indent): + truncate_h = self.fmt.truncate_h if not self.fmt.header: # write nothing return indent @@ -745,9 +801,7 @@ def _column_header(): else: if self.fmt.index: row.append(self.columns.name or '') - row.extend(self.columns[:self.max_cols]) - if len(self.columns) > self.max_cols: - row.append('') + row.extend(self.columns) return row self.write('', indent) @@ -758,16 +812,13 @@ def _column_header(): if isinstance(self.columns, MultiIndex): template = 'colspan="%d" halign="left"' - # GH3547 - sentinel = com.sentinel_factory() - levels = self.columns.format(sparsify=sentinel, adjoin=False, - names=False) - # Truncate column names - if len(levels[0]) > self.max_cols: - levels = [lev[:self.max_cols] for lev in levels] - truncated = True + if self.fmt.sparsify: + # GH3547 + sentinel = com.sentinel_factory() else: - truncated = False + sentinel = None + levels = self.columns.format(sparsify=sentinel, + adjoin=False, names=False) level_lengths = _get_level_lengths(levels, sentinel) @@ -778,7 +829,6 @@ def _column_header(): name = self.columns.names[lnum] row = [''] * (row_levels - 1) + ['' if name is None else com.pprint_thing(name)] - tags = {} j = len(row) for i, v in enumerate(values): @@ -789,9 +839,16 @@ def _column_header(): continue j += 1 row.append(v) - - if truncated: - row.append('') + if truncate_h: + if self.fmt.sparsify and lnum == 0: + ins_col = row_levels + self.fmt.tr_col_num - 1 + row.insert(ins_col, '...') + + for tag in list(tags.keys()): + if tag >= ins_col: + tags[tag+1] = tags.pop(tag) + else: + row.insert(row_levels + self.fmt.tr_col_num, '...') self.write_tr(row, indent, self.indent_delta, tags=tags, header=True) @@ -799,6 +856,9 @@ def _column_header(): col_row = _column_header() align = self.fmt.justify + if truncate_h: + col_row.insert(self.fmt.tr_col_num + 1, '...') + self.write_tr(col_row, indent, self.indent_delta, header=True, align=align) @@ -820,14 +880,13 @@ def _write_body(self, indent): fmt_values = {} for i in range(min(len(self.columns), self.max_cols)): fmt_values[i] = self.fmt._format_col(i) - truncated = (len(self.columns) > self.max_cols) # write values if self.fmt.index: if isinstance(self.frame.index, MultiIndex): self._write_hierarchical_rows(fmt_values, indent) else: - self._write_regular_rows(fmt_values, indent, truncated) + self._write_regular_rows(fmt_values, indent) else: for i in range(len(self.frame)): row = [fmt_values[j][i] for j in range(len(self.columns))] @@ -839,55 +898,62 @@ def _write_body(self, indent): return indent - def _write_regular_rows(self, fmt_values, indent, truncated): - ncols = min(len(self.columns), self.max_cols) - nrows = min(len(self.frame), self.max_rows) + def _write_regular_rows(self, fmt_values, indent): + truncate_h = self.fmt.truncate_h + truncate_v = self.fmt.truncate_v + + ncols = len(self.fmt.tr_frame.columns) + nrows = len(self.fmt.tr_frame) fmt = self.fmt._get_formatter('__index__') if fmt is not None: - index_values = self.frame.index[:nrows].map(fmt) + index_values = self.fmt.tr_frame.index.map(fmt) else: - index_values = self.frame.index[:nrows].format() + index_values = self.fmt.tr_frame.index.format() for i in range(nrows): + + if truncate_v and i == (self.fmt.tr_row_num): + str_sep_row = [ '...' for ele in row ] + self.write_tr(str_sep_row, indent, self.indent_delta, tags=None, + nindex_levels=1) + row = [] row.append(index_values[i]) row.extend(fmt_values[j][i] for j in range(ncols)) - if truncated: - row.append('...') - self.write_tr(row, indent, self.indent_delta, tags=None, - nindex_levels=1) - if len(self.frame) > self.max_rows: - row = [''] + (['...'] * ncols) + if truncate_h: + dot_col_ix = self.fmt.tr_col_num + 1 + row.insert(dot_col_ix, '...') self.write_tr(row, indent, self.indent_delta, tags=None, nindex_levels=1) def _write_hierarchical_rows(self, fmt_values, indent): template = 'rowspan="%d" valign="top"' - frame = self.frame - ncols = min(len(self.columns), self.max_cols) - nrows = min(len(self.frame), self.max_rows) - - truncate = (len(frame) > self.max_rows) + truncate_h = self.fmt.truncate_h + truncate_v = self.fmt.truncate_v + frame = self.fmt.tr_frame + ncols = len(frame.columns) + nrows = len(frame) + row_levels = self.frame.index.nlevels - idx_values = frame.index[:nrows].format(sparsify=False, adjoin=False, + idx_values = frame.index.format(sparsify=False, adjoin=False, names=False) idx_values = lzip(*idx_values) if self.fmt.sparsify: - # GH3547 sentinel = com.sentinel_factory() - levels = frame.index[:nrows].format(sparsify=sentinel, + levels = frame.index.format(sparsify=sentinel, adjoin=False, names=False) - # Truncate row names - if truncate: - levels = [lev[:self.max_rows] for lev in levels] level_lengths = _get_level_lengths(levels, sentinel) - for i in range(min(len(frame), self.max_rows)): + for i in range(nrows): + if truncate_v and i == (self.fmt.tr_row_num): + str_sep_row = [ '...' ] * (len(row) + sparse_offset) + self.write_tr(str_sep_row, indent, self.indent_delta, tags=None) + row = [] tags = {} @@ -905,6 +971,8 @@ def _write_hierarchical_rows(self, fmt_values, indent): row.append(v) row.extend(fmt_values[j][i] for j in range(ncols)) + if truncate_h: + row.insert(row_levels - sparse_offset + self.fmt.tr_col_num, '...') self.write_tr(row, indent, self.indent_delta, tags=tags, nindex_levels=len(levels) - sparse_offset) else: @@ -915,15 +983,11 @@ def _write_hierarchical_rows(self, fmt_values, indent): row = [] row.extend(idx_values[i]) row.extend(fmt_values[j][i] for j in range(ncols)) + if truncate_h: + row.insert(row_levels + self.fmt.tr_col_num, '...') self.write_tr(row, indent, self.indent_delta, tags=None, nindex_levels=frame.index.nlevels) - # Truncation markers (...) - if truncate: - row = ([''] * frame.index.nlevels) + (['...'] * ncols) - self.write_tr(row, indent, self.indent_delta, tags=None) - - def _get_level_lengths(levels, sentinel=''): from itertools import groupby @@ -1877,8 +1941,7 @@ def impl(x): return impl -def _make_fixed_width(strings, justify='right', minimum=None, truncated=False): - +def _make_fixed_width(strings, justify='right', minimum=None): if len(strings) == 0 or justify == 'all': return strings @@ -1909,9 +1972,6 @@ def just(x): result = [just(x) for x in strings] - if truncated: - result.append(justfunc('...'[:max_len], max_len)) - return result diff --git a/pandas/tests/test_format.py b/pandas/tests/test_format.py index f61bda686c88b..61d2de458fdc9 100644 --- a/pandas/tests/test_format.py +++ b/pandas/tests/test_format.py @@ -1,5 +1,6 @@ from __future__ import print_function # -*- coding: utf-8 -*- +import re from pandas.compat import range, zip, lrange, StringIO, PY3, lzip, u import pandas.compat as compat @@ -45,12 +46,25 @@ def has_non_verbose_info_repr(df): return has_info and nv def has_horizontally_truncated_repr(df): + try: # Check header row + fst_line = np.array(repr(df).splitlines()[0].split()) + cand_col = np.where(fst_line=='...')[0][0] + except: + return False + # Make sure each row has this ... in the same place r = repr(df) - return any(l.strip().endswith('...') for l in r.splitlines()) + for ix,l in enumerate(r.splitlines()): + if not r.split()[cand_col] == '...': + return False + return True def has_vertically_truncated_repr(df): r = repr(df) - return '..' in r.splitlines()[-3] + only_dot_row = False + for row in r.splitlines(): + if re.match('^[\.\ ]+$',row): + only_dot_row = True + return only_dot_row def has_truncated_repr(df): return has_horizontally_truncated_repr(df) or has_vertically_truncated_repr(df) @@ -382,6 +396,40 @@ def test_to_string_with_col_space(self): c30 = len(df.to_string(col_space=30).split("\n")[1]) self.assertTrue(c10 < c20 < c30) + def test_to_string_truncate_indices(self): + for index in [ tm.makeStringIndex, tm.makeUnicodeIndex, tm.makeIntIndex, + tm.makeDateIndex, tm.makePeriodIndex ]: + for column in [ tm.makeStringIndex ]: + for h in [10,20]: + for w in [10,20]: + with option_context("display.expand_frame_repr",False): + df = DataFrame(index=index(h), columns=column(w)) + with option_context("display.max_rows", 15): + if h == 20: + self.assertTrue(has_vertically_truncated_repr(df)) + else: + self.assertFalse(has_vertically_truncated_repr(df)) + with option_context("display.max_columns", 15): + if w == 20: + print(df) + print(repr(df)) + self.assertTrue(has_horizontally_truncated_repr(df)) + else: + self.assertFalse(has_horizontally_truncated_repr(df)) + with option_context("display.max_rows", 15,"display.max_columns", 15): + if h == 20 and w == 20: + self.assertTrue(has_doubly_truncated_repr(df)) + else: + self.assertFalse(has_doubly_truncated_repr(df)) + + def test_to_string_truncate_multilevel(self): + arrays = [['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'], + ['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two']] + df = pd.DataFrame(index=arrays,columns=arrays) + with option_context("display.max_rows", 7,"display.max_columns", 7): + self.assertTrue(has_doubly_truncated_repr(df)) + + def test_to_html_with_col_space(self): def check_with_width(df, col_space): import re @@ -735,6 +783,338 @@ def test_to_html_regression_GH6098(self): # it works df.pivot_table(index=[u('clé1')], columns=[u('clé2')])._repr_html_() + + + + + def test_to_html_truncate(self): + index = pd.DatetimeIndex(start='20010101',freq='D',periods=20) + df = pd.DataFrame(index=index,columns=range(20)) + fmt.set_option('display.max_rows',8) + fmt.set_option('display.max_columns',4) + result = df._repr_html_() + expected = '''\ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
01...1819
2001-01-01 NaN NaN... NaN NaN
2001-01-02 NaN NaN... NaN NaN
2001-01-03 NaN NaN... NaN NaN
2001-01-04 NaN NaN... NaN NaN
..................
2001-01-17 NaN NaN... NaN NaN
2001-01-18 NaN NaN... NaN NaN
2001-01-19 NaN NaN... NaN NaN
2001-01-20 NaN NaN... NaN NaN
+

20 rows × 20 columns

+
''' + if sys.version_info[0] < 3: + expected = expected.decode('utf-8') + self.assertEqual(result, expected) + + def test_to_html_truncate_multi_index(self): + arrays = [['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'], + ['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two']] + df = pd.DataFrame(index=arrays,columns=arrays) + fmt.set_option('display.max_rows',7) + fmt.set_option('display.max_columns',7) + result = df._repr_html_() + expected = '''\ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
barbaz...fooqux
onetwoone...twoonetwo
barone NaN NaN NaN... NaN NaN NaN
two NaN NaN NaN... NaN NaN NaN
bazone NaN NaN NaN... NaN NaN NaN
...........................
footwo NaN NaN NaN... NaN NaN NaN
quxone NaN NaN NaN... NaN NaN NaN
two NaN NaN NaN... NaN NaN NaN
+

8 rows × 8 columns

+
''' + if sys.version_info[0] < 3: + expected = expected.decode('utf-8') + self.assertEqual(result, expected) + + def test_to_html_truncate_multi_index_sparse_off(self): + arrays = [['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'], + ['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two']] + df = pd.DataFrame(index=arrays,columns=arrays) + fmt.set_option('display.max_rows',7) + fmt.set_option('display.max_columns',7) + fmt.set_option('display.multi_sparse',False) + result = df._repr_html_() + expected = '''\ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
barbarbaz...fooquxqux
onetwoone...twoonetwo
barone NaN NaN NaN... NaN NaN NaN
bartwo NaN NaN NaN... NaN NaN NaN
bazone NaN NaN NaN... NaN NaN NaN
footwo NaN NaN NaN... NaN NaN NaN
quxone NaN NaN NaN... NaN NaN NaN
quxtwo NaN NaN NaN... NaN NaN NaN
+

8 rows × 8 columns

+
''' + if sys.version_info[0] < 3: + expected = expected.decode('utf-8') + self.assertEqual(result, expected) + + + def test_nonunicode_nonascii_alignment(self): df = DataFrame([["aa\xc3\xa4\xc3\xa4", 1], ["bbbb", 2]]) rep_str = df.to_string() @@ -1505,14 +1885,14 @@ def test_repr_html_long(self): h = max_rows - 1 df = pandas.DataFrame({'A':np.arange(1,1+h), 'B':np.arange(41, 41+h)}) reg_repr = df._repr_html_() - assert '...' not in reg_repr - assert str(40 + h) in reg_repr + assert '..' not in reg_repr + assert str(41 + max_rows // 2) in reg_repr h = max_rows + 1 df = pandas.DataFrame({'A':np.arange(1,1+h), 'B':np.arange(41, 41+h)}) long_repr = df._repr_html_() - assert '...' in long_repr - assert str(40 + h) not in long_repr + assert '..' in long_repr + assert str(41 + max_rows // 2) not in long_repr assert u('%d rows ') % h in long_repr assert u('2 columns') in long_repr @@ -1521,14 +1901,14 @@ def test_repr_html_float(self): h = max_rows - 1 df = pandas.DataFrame({'idx':np.linspace(-10,10,h), 'A':np.arange(1,1+h), 'B': np.arange(41, 41+h) }).set_index('idx') reg_repr = df._repr_html_() - assert '...' not in reg_repr + assert '..' not in reg_repr assert str(40 + h) in reg_repr h = max_rows + 1 df = pandas.DataFrame({'idx':np.linspace(-10,10,h), 'A':np.arange(1,1+h), 'B': np.arange(41, 41+h) }).set_index('idx') long_repr = df._repr_html_() - assert '...' in long_repr - assert str(40 + h) not in long_repr + assert '..' in long_repr + assert '31' not in long_repr assert u('%d rows ') % h in long_repr assert u('2 columns') in long_repr @@ -1575,7 +1955,7 @@ def test_info_repr(self): # Wide h, w = max_rows-1, max_cols+1 df = pandas.DataFrame(dict((k,np.arange(1,1+h)) for k in np.arange(w))) - assert has_vertically_truncated_repr(df) + assert has_horizontally_truncated_repr(df) with option_context('display.large_repr', 'info'): assert has_info_repr(df)