diff --git a/pandas/core/common.py b/pandas/core/common.py index 5f50cd0dbf33e..09cc3e2c5429f 100644 --- a/pandas/core/common.py +++ b/pandas/core/common.py @@ -346,7 +346,8 @@ def _try_sort(iterable): except Exception: return listed -def set_printoptions(precision=None, column_space=None): +def set_printoptions(precision=None, column_space=None, max_rows=None, + max_columns=None): """ Alter default behavior of DataFrame.toString @@ -354,13 +355,24 @@ def set_printoptions(precision=None, column_space=None): Floating point output precision column_space : int Default space for DataFrame columns, defaults to 12 + max_rows : int + max_columns : int + max_rows and max_columns are used in __repr__() methods to decide if + to_string() or info() is used to render an object to a string. + Either one, or both can be set to 0 (experimental). Pandas will figure + out how big the terminal is and will not display more rows or/and + columns that can fit on it. """ - global _float_format, _column_space + global _float_format, _column_space, _max_rows, _max_columns if precision is not None: float_format = '%.' + '%d' % precision + 'g' _float_format = lambda x: float_format % x if column_space is not None: _column_space = column_space + if max_rows is not None: + _max_rows = max_rows + if max_columns is not None: + _max_columns = max_columns class EngFormatter(object): """ @@ -467,6 +479,8 @@ def set_eng_float_format(precision=3, use_eng_prefix=False): _float_format = lambda x: '%.4g' % x _column_space = 12 +_max_rows = 500 +_max_columns = 10 def _pfixed(s, space, na_rep=None, float_format=None): if isinstance(s, float): diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 7b2fbb4050d2d..623c41be0d70d 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -23,7 +23,8 @@ import numpy.ma as ma from pandas.core.common import (isnull, notnull, PandasError, _try_sort, - _default_index, _stringify, _maybe_upcast) + _default_index, _stringify, _maybe_upcast, + _max_rows, _max_columns) from pandas.core.daterange import DateRange from pandas.core.generic import NDFrame, AxisProperty from pandas.core.index import Index, MultiIndex, NULL_INDEX, _ensure_index @@ -31,6 +32,7 @@ from pandas.core.internals import BlockManager, make_block, form_blocks from pandas.core.series import Series, _is_bool_indexer from pandas.util import py3compat +from pandas.util.terminal import get_terminal_size import pandas.core.nanops as nanops import pandas.core.common as com import pandas.core.datetools as datetools @@ -316,13 +318,33 @@ def __repr__(self): """ Return a string representation for a particular DataFrame """ - buf = StringIO() - if len(self.index) < 500 and len(self.columns) <= 10: - self.to_string(buf=buf) - else: - self.info(buf=buf, verbose=self._verbose_info) + terminal_width, terminal_height = get_terminal_size() + max_rows = terminal_height if com._max_rows == 0 else com._max_rows + max_columns = com._max_columns - return buf.getvalue() + if max_columns > 0: + buf = StringIO() + if len(self.index) < max_rows and \ + len(self.columns) <= max_columns: + self.to_string(buf=buf) + else: + self.info(buf=buf, verbose=self._verbose_info) + return buf.getvalue() + else: + if len(self.index) > max_rows: + buf = StringIO() + self.info(buf=buf, verbose=self._verbose_info) + return buf.getvalue() + else: + buf = StringIO() + self.to_string(buf=buf) + value = buf.getvalue() + if max([len(l) for l in value.split('\n')]) <= terminal_width: + return value + else: + buf = StringIO() + self.info(buf=buf, verbose=self._verbose_info) + return buf.getvalue() def __iter__(self): """ diff --git a/pandas/core/series.py b/pandas/core/series.py index a003249c97abd..51cadceb45a92 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -20,6 +20,7 @@ from pandas.core.index import Index, MultiIndex, _ensure_index from pandas.core.indexing import _SeriesIndexer, _maybe_droplevels from pandas.util import py3compat +from pandas.util.terminal import get_terminal_size import pandas.core.common as common import pandas.core.datetools as datetools import pandas.core.nanops as nanops @@ -413,8 +414,10 @@ def __setslice__(self, i, j, value): def __repr__(self): """Clean string representation of a Series""" - if len(self.index) > 500: - result = self._tidy_repr(30) + width, height = get_terminal_size() + max_rows = height if common._max_rows == 0 else common._max_rows + if len(self.index) > max_rows: + result = self._tidy_repr(min(30, max_rows - 4)) elif len(self.index) > 0: result = self._get_repr(print_header=True, length=len(self) > 50, diff --git a/pandas/util/terminal.py b/pandas/util/terminal.py new file mode 100644 index 0000000000000..42d81d924cd6b --- /dev/null +++ b/pandas/util/terminal.py @@ -0,0 +1,105 @@ +""" +get_terminal_size() -- return width and height of console as a tuple + +code from: +http://stackoverflow.com/questions/566746/how-to-get-console- window-width-in- +python + +written by +Harco Kuppens (http://stackoverflow.com/users/825214/harco-kuppens) + +It is mentioned in the stackoverflow response that this code works +on linux, os x, windows and cygwin (windows). +""" + +__all__=['get_terminal_size'] + + +def get_terminal_size(): + import platform + current_os = platform.system() + tuple_xy=None + if current_os == 'Windows': + tuple_xy = _get_terminal_size_windows() + if tuple_xy is None: + tuple_xy = _get_terminal_size_tput() + # needed for window's python in cygwin's xterm! + if current_os == 'Linux' or \ + current_os == 'Darwin' or \ + current_os.startswith('CYGWIN'): + tuple_xy = _get_terminal_size_linux() + if tuple_xy is None: + tuple_xy = (80, 25) # default value + return tuple_xy + +def _get_terminal_size_windows(): + res=None + try: + from ctypes import windll, create_string_buffer + + # stdin handle is -10 + # stdout handle is -11 + # stderr handle is -12 + + h = windll.kernel32.GetStdHandle(-12) + csbi = create_string_buffer(22) + res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi) + except: + return None + if res: + import struct + (bufx, bufy, curx, cury, wattr, left, top, right, bottom, maxx, + maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw) + sizex = right - left + 1 + sizey = bottom - top + 1 + return sizex, sizey + else: + return None + +def _get_terminal_size_tput(): + # get terminal width + # src: http://stackoverflow.com/questions/263890/how-do-i-find-the-width + # -height-of-a-terminal-window + try: + import subprocess + proc = subprocess.Popen(["tput", "cols"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + output=proc.communicate(input=None) + cols=int(output[0]) + proc=subprocess.Popen(["tput", "lines"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + output=proc.communicate(input=None) + rows=int(output[0]) + return (cols,rows) + except: + return None + + +def _get_terminal_size_linux(): + def ioctl_GWINSZ(fd): + try: + import fcntl, termios, struct, os + cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ,'1234')) + except: + return None + return cr + cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) + if not cr: + try: + fd = os.open(os.ctermid(), os.O_RDONLY) + cr = ioctl_GWINSZ(fd) + os.close(fd) + except: + pass + if not cr: + try: + cr = (env['LINES'], env['COLUMNS']) + except: + return None + return int(cr[1]), int(cr[0]) + +if __name__ == "__main__": + sizex, sizey = get_terminal_size() + print 'width =', sizex, 'height =', sizey