diff --git a/pandas/core/style.py b/pandas/core/style.py index 59bf6118a79c6..3b7ef77172124 100644 --- a/pandas/core/style.py +++ b/pandas/core/style.py @@ -19,6 +19,7 @@ import numpy as np import pandas as pd from pandas.compat import lzip +import pandas.core.common as com from pandas.core.indexing import _maybe_numeric_slice, _non_reducing_slice try: import matplotlib.pyplot as plt @@ -117,11 +118,7 @@ class Styler(object): {% for c in r %} <{{c.type}} id="T_{{uuid}}{{c.id}}" class="{{c.class}}"> - {% if c.value is number %} - {{c.value|round(precision)}} - {% else %} - {{c.value}} - {% endif %} + {{c.display_value}} {% endfor %} {% endfor %} @@ -152,6 +149,13 @@ def __init__(self, data, precision=None, table_styles=None, uuid=None, precision = pd.options.display.precision self.precision = precision self.table_attributes = table_attributes + # display_funcs maps (row, col) -> formatting function + def default_display_func(x): + if com.is_float(x): + return '{:>.{precision}g}'.format(x, precision=self.precision) + else: + return x + self._display_funcs = defaultdict(lambda: default_display_func) def _repr_html_(self): ''' @@ -202,7 +206,10 @@ def _translate(self): cs = [COL_HEADING_CLASS, "level%s" % r, "col%s" % c] cs.extend(cell_context.get( "col_headings", {}).get(r, {}).get(c, [])) - row_es.append({"type": "th", "value": clabels[r][c], + value = clabels[r][c] + row_es.append({"type": "th", + "value": value, + "display_value": value, "class": " ".join(cs)}) head.append(row_es) @@ -213,14 +220,21 @@ def _translate(self): "row_headings", {}).get(r, {}).get(c, [])) row_es = [{"type": "th", "value": rlabels[r][c], - "class": " ".join(cs)} + "class": " ".join(cs), + "display_value": rlabels[r][c]} for c in range(len(rlabels[r]))] for c, col in enumerate(self.data.columns): cs = [DATA_CLASS, "row%s" % r, "col%s" % c] cs.extend(cell_context.get("data", {}).get(r, {}).get(c, [])) - row_es.append({"type": "td", "value": self.data.iloc[r][c], - "class": " ".join(cs), "id": "_".join(cs[1:])}) + formatter = self._display_funcs[(r, c)] + value = self.data.iloc[r, c] + row_es.append({"type": "td", + "value": value, + "class": " ".join(cs), + "id": "_".join(cs[1:]), + "display_value": formatter(value) + }) props = [] for x in ctx[r, c]: # have to handle empty styles like [''] @@ -238,6 +252,48 @@ def _translate(self): precision=precision, table_styles=table_styles, caption=caption, table_attributes=self.table_attributes) + def format(self, formatter, subset=None): + ''' + formatter is either an `a` or a dict `{column_name: a}` where + a is one of + - str: wrapped in: ``a.format(x)`` + - callable: called with x. + + now `.set_precision(2)` becomes + + ``` + .format('{:2g}') + ``` + ''' + from itertools import product + from collections.abc import MutableMapping + + just_row_subset = (com.is_list_like(subset) and + not isinstance(subset, tuple)) + + if issubclass(type(formatter), MutableMapping): + # formatter is a dict + for col, col_formatter in formatter.items(): + # get the row index locations out of subset + j = self.data.columns.get_indexer_for([col])[0] + if not callable(col_formatter): + formatter_func = lambda x: col_formatter.format(x) + + if subset is not None and just_row_subset: + locs = self.data.index.get_indexer_for(subset) + elif subset is not None: + locs = self.data.index.get_indexer_for(subset) + else: + locs = range(len(self.data)) + for i in locs: + self._display_funcs[(i, j)] = formatter_func + # single scalar to format all cells with + else: + locs = product(*(range(x) for x in self.data.shape)) + for i, j in locs: + self._display_funcs[(i, j)] = lambda x: formatter.format(x) + return self + def render(self): """ Render the built up styles to HTML