33DataFrames and Series.
44"""
55from functools import partial
6+ from itertools import product
67from contextlib import contextmanager
78from uuid import uuid1
89import copy
9- from collections import defaultdict
10+ from collections import defaultdict , MutableMapping
1011
1112try :
1213 from jinja2 import Template
1819
1920import numpy as np
2021import pandas as pd
21- from pandas .compat import lzip
22+ from pandas .compat import lzip , range
23+ import pandas .core .common as com
2224from pandas .core .indexing import _maybe_numeric_slice , _non_reducing_slice
2325try :
2426 import matplotlib .pyplot as plt
@@ -117,11 +119,7 @@ class Styler(object):
117119 <tr>
118120 {% for c in r %}
119121 <{{c.type}} id="T_{{uuid}}{{c.id}}" class="{{c.class}}">
120- {% if c.value is number %}
121- {{c.value|round(precision)}}
122- {% else %}
123- {{c.value}}
124- {% endif %}
122+ {{ c.display_value }}
125123 {% endfor %}
126124 </tr>
127125 {% endfor %}
@@ -152,6 +150,15 @@ def __init__(self, data, precision=None, table_styles=None, uuid=None,
152150 precision = pd .options .display .precision
153151 self .precision = precision
154152 self .table_attributes = table_attributes
153+ # display_funcs maps (row, col) -> formatting function
154+
155+ def default_display_func (x ):
156+ if com .is_float (x ):
157+ return '{:>.{precision}g}' .format (x , precision = self .precision )
158+ else :
159+ return x
160+
161+ self ._display_funcs = defaultdict (lambda : default_display_func )
155162
156163 def _repr_html_ (self ):
157164 """Hooks into Jupyter notebook rich display system."""
@@ -199,10 +206,12 @@ def _translate(self):
199206 "class" : " " .join ([BLANK_CLASS ])}] * n_rlvls
200207 for c in range (len (clabels [0 ])):
201208 cs = [COL_HEADING_CLASS , "level%s" % r , "col%s" % c ]
202- cs .extend (
203- cell_context .get ("col_headings" , {}).get (r , {}).get (c , []))
209+ cs .extend (cell_context .get (
210+ "col_headings" , {}).get (r , {}).get (c , []))
211+ value = clabels [r ][c ]
204212 row_es .append ({"type" : "th" ,
205- "value" : clabels [r ][c ],
213+ "value" : value ,
214+ "display_value" : value ,
206215 "class" : " " .join (cs )})
207216 head .append (row_es )
208217
@@ -231,15 +240,22 @@ def _translate(self):
231240 cell_context .get ("row_headings" , {}).get (r , {}).get (c , []))
232241 row_es = [{"type" : "th" ,
233242 "value" : rlabels [r ][c ],
234- "class" : " " .join (cs )} for c in range (len (rlabels [r ]))]
243+ "class" : " " .join (cs ),
244+ "display_value" : rlabels [r ][c ]}
245+ for c in range (len (rlabels [r ]))]
235246
236247 for c , col in enumerate (self .data .columns ):
237248 cs = [DATA_CLASS , "row%s" % r , "col%s" % c ]
238249 cs .extend (cell_context .get ("data" , {}).get (r , {}).get (c , []))
239- row_es .append ({"type" : "td" ,
240- "value" : self .data .iloc [r ][c ],
241- "class" : " " .join (cs ),
242- "id" : "_" .join (cs [1 :])})
250+ formatter = self ._display_funcs [(r , c )]
251+ value = self .data .iloc [r , c ]
252+ row_es .append ({
253+ "type" : "td" ,
254+ "value" : value ,
255+ "class" : " " .join (cs ),
256+ "id" : "_" .join (cs [1 :]),
257+ "display_value" : formatter (value )
258+ })
243259 props = []
244260 for x in ctx [r , c ]:
245261 # have to handle empty styles like ['']
@@ -255,6 +271,71 @@ def _translate(self):
255271 precision = precision , table_styles = table_styles ,
256272 caption = caption , table_attributes = self .table_attributes )
257273
274+ def format (self , formatter , subset = None ):
275+ """
276+ Format the text display value of cells.
277+
278+ .. versionadded:: 0.18.0
279+
280+ Parameters
281+ ----------
282+ formatter: str, callable, or dict
283+ subset: IndexSlice
284+ A argument to DataFrame.loc that restricts which elements
285+ ``formatter`` is applied to.
286+
287+ Returns
288+ -------
289+ self : Styler
290+
291+ Notes
292+ -----
293+
294+ ``formatter`` is either an ``a`` or a dict ``{column name: a}`` where
295+ ``a`` is one of
296+
297+ - str: this will be wrapped in: ``a.format(x)``
298+ - callable: called with the value of an individual cell
299+
300+ The default display value for numeric values is the "general" (``g``)
301+ format with ``pd.options.display.precision`` precision.
302+
303+ Examples
304+ --------
305+
306+ >>> df = pd.DataFrame(np.random.randn(4, 2), columns=['a', 'b'])
307+ >>> df.style.format("{:.2%}")
308+ >>> df['c'] = ['a', 'b', 'c', 'd']
309+ >>> df.style.format({'C': str.upper})
310+ """
311+ if subset is None :
312+ row_locs = range (len (self .data ))
313+ col_locs = range (len (self .data .columns ))
314+ else :
315+ subset = _non_reducing_slice (subset )
316+ if len (subset ) == 1 :
317+ subset = subset , self .data .columns
318+
319+ sub_df = self .data .loc [subset ]
320+ row_locs = self .data .index .get_indexer_for (sub_df .index )
321+ col_locs = self .data .columns .get_indexer_for (sub_df .columns )
322+
323+ if isinstance (formatter , MutableMapping ):
324+ for col , col_formatter in formatter .items ():
325+ # formatter must be callable, so '{}' are converted to lambdas
326+ col_formatter = _maybe_wrap_formatter (col_formatter )
327+ col_num = self .data .columns .get_indexer_for ([col ])[0 ]
328+
329+ for row_num in row_locs :
330+ self ._display_funcs [(row_num , col_num )] = col_formatter
331+ else :
332+ # single scalar to format all cells with
333+ locs = product (* (row_locs , col_locs ))
334+ for i , j in locs :
335+ formatter = _maybe_wrap_formatter (formatter )
336+ self ._display_funcs [(i , j )] = formatter
337+ return self
338+
258339 def render (self ):
259340 """
260341 Render the built up styles to HTML
@@ -376,7 +457,7 @@ def apply(self, func, axis=0, subset=None, **kwargs):
376457
377458 Returns
378459 -------
379- self
460+ self : Styler
380461
381462 Notes
382463 -----
@@ -415,7 +496,7 @@ def applymap(self, func, subset=None, **kwargs):
415496
416497 Returns
417498 -------
418- self
499+ self : Styler
419500
420501 """
421502 self ._todo .append ((lambda instance : getattr (instance , '_applymap' ),
@@ -434,7 +515,7 @@ def set_precision(self, precision):
434515
435516 Returns
436517 -------
437- self
518+ self : Styler
438519 """
439520 self .precision = precision
440521 return self
@@ -453,7 +534,7 @@ def set_table_attributes(self, attributes):
453534
454535 Returns
455536 -------
456- self
537+ self : Styler
457538 """
458539 self .table_attributes = attributes
459540 return self
@@ -489,7 +570,7 @@ def use(self, styles):
489570
490571 Returns
491572 -------
492- self
573+ self : Styler
493574
494575 See Also
495576 --------
@@ -510,7 +591,7 @@ def set_uuid(self, uuid):
510591
511592 Returns
512593 -------
513- self
594+ self : Styler
514595 """
515596 self .uuid = uuid
516597 return self
@@ -527,7 +608,7 @@ def set_caption(self, caption):
527608
528609 Returns
529610 -------
530- self
611+ self : Styler
531612 """
532613 self .caption = caption
533614 return self
@@ -550,7 +631,7 @@ def set_table_styles(self, table_styles):
550631
551632 Returns
552633 -------
553- self
634+ self : Styler
554635
555636 Examples
556637 --------
@@ -583,7 +664,7 @@ def highlight_null(self, null_color='red'):
583664
584665 Returns
585666 -------
586- self
667+ self : Styler
587668 """
588669 self .applymap (self ._highlight_null , null_color = null_color )
589670 return self
@@ -610,7 +691,7 @@ def background_gradient(self, cmap='PuBu', low=0, high=0, axis=0,
610691
611692 Returns
612693 -------
613- self
694+ self : Styler
614695
615696 Notes
616697 -----
@@ -695,7 +776,7 @@ def bar(self, subset=None, axis=0, color='#d65f5f', width=100):
695776
696777 Returns
697778 -------
698- self
779+ self : Styler
699780 """
700781 subset = _maybe_numeric_slice (self .data , subset )
701782 subset = _non_reducing_slice (subset )
@@ -720,7 +801,7 @@ def highlight_max(self, subset=None, color='yellow', axis=0):
720801
721802 Returns
722803 -------
723- self
804+ self : Styler
724805 """
725806 return self ._highlight_handler (subset = subset , color = color , axis = axis ,
726807 max_ = True )
@@ -742,7 +823,7 @@ def highlight_min(self, subset=None, color='yellow', axis=0):
742823
743824 Returns
744825 -------
745- self
826+ self : Styler
746827 """
747828 return self ._highlight_handler (subset = subset , color = color , axis = axis ,
748829 max_ = False )
@@ -771,3 +852,14 @@ def _highlight_extrema(data, color='yellow', max_=True):
771852 extrema = data == data .min ().min ()
772853 return pd .DataFrame (np .where (extrema , attr , '' ),
773854 index = data .index , columns = data .columns )
855+
856+
857+ def _maybe_wrap_formatter (formatter ):
858+ if com .is_string_like (formatter ):
859+ return lambda x : formatter .format (x )
860+ elif callable (formatter ):
861+ return formatter
862+ else :
863+ msg = "Expected a template string or callable, got {} instead" .format (
864+ formatter )
865+ raise TypeError (msg )
0 commit comments