3
3
DataFrames and Series.
4
4
"""
5
5
from functools import partial
6
+ from itertools import product
6
7
from contextlib import contextmanager
7
8
from uuid import uuid1
8
9
import copy
9
- from collections import defaultdict
10
+ from collections import defaultdict , MutableMapping
10
11
11
12
try :
12
13
from jinja2 import Template
18
19
19
20
import numpy as np
20
21
import pandas as pd
21
- from pandas .compat import lzip
22
+ from pandas .compat import lzip , range
23
+ import pandas .core .common as com
22
24
from pandas .core .indexing import _maybe_numeric_slice , _non_reducing_slice
23
25
try :
24
26
import matplotlib .pyplot as plt
@@ -117,11 +119,7 @@ class Styler(object):
117
119
<tr>
118
120
{% for c in r %}
119
121
<{{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 }}
125
123
{% endfor %}
126
124
</tr>
127
125
{% endfor %}
@@ -152,6 +150,15 @@ def __init__(self, data, precision=None, table_styles=None, uuid=None,
152
150
precision = pd .options .display .precision
153
151
self .precision = precision
154
152
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 )
155
162
156
163
def _repr_html_ (self ):
157
164
"""Hooks into Jupyter notebook rich display system."""
@@ -199,10 +206,12 @@ def _translate(self):
199
206
"class" : " " .join ([BLANK_CLASS ])}] * n_rlvls
200
207
for c in range (len (clabels [0 ])):
201
208
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 ]
204
212
row_es .append ({"type" : "th" ,
205
- "value" : clabels [r ][c ],
213
+ "value" : value ,
214
+ "display_value" : value ,
206
215
"class" : " " .join (cs )})
207
216
head .append (row_es )
208
217
@@ -231,15 +240,22 @@ def _translate(self):
231
240
cell_context .get ("row_headings" , {}).get (r , {}).get (c , []))
232
241
row_es = [{"type" : "th" ,
233
242
"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 ]))]
235
246
236
247
for c , col in enumerate (self .data .columns ):
237
248
cs = [DATA_CLASS , "row%s" % r , "col%s" % c ]
238
249
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
+ })
243
259
props = []
244
260
for x in ctx [r , c ]:
245
261
# have to handle empty styles like ['']
@@ -255,6 +271,71 @@ def _translate(self):
255
271
precision = precision , table_styles = table_styles ,
256
272
caption = caption , table_attributes = self .table_attributes )
257
273
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
+
258
339
def render (self ):
259
340
"""
260
341
Render the built up styles to HTML
@@ -376,7 +457,7 @@ def apply(self, func, axis=0, subset=None, **kwargs):
376
457
377
458
Returns
378
459
-------
379
- self
460
+ self : Styler
380
461
381
462
Notes
382
463
-----
@@ -415,7 +496,7 @@ def applymap(self, func, subset=None, **kwargs):
415
496
416
497
Returns
417
498
-------
418
- self
499
+ self : Styler
419
500
420
501
"""
421
502
self ._todo .append ((lambda instance : getattr (instance , '_applymap' ),
@@ -434,7 +515,7 @@ def set_precision(self, precision):
434
515
435
516
Returns
436
517
-------
437
- self
518
+ self : Styler
438
519
"""
439
520
self .precision = precision
440
521
return self
@@ -453,7 +534,7 @@ def set_table_attributes(self, attributes):
453
534
454
535
Returns
455
536
-------
456
- self
537
+ self : Styler
457
538
"""
458
539
self .table_attributes = attributes
459
540
return self
@@ -489,7 +570,7 @@ def use(self, styles):
489
570
490
571
Returns
491
572
-------
492
- self
573
+ self : Styler
493
574
494
575
See Also
495
576
--------
@@ -510,7 +591,7 @@ def set_uuid(self, uuid):
510
591
511
592
Returns
512
593
-------
513
- self
594
+ self : Styler
514
595
"""
515
596
self .uuid = uuid
516
597
return self
@@ -527,7 +608,7 @@ def set_caption(self, caption):
527
608
528
609
Returns
529
610
-------
530
- self
611
+ self : Styler
531
612
"""
532
613
self .caption = caption
533
614
return self
@@ -550,7 +631,7 @@ def set_table_styles(self, table_styles):
550
631
551
632
Returns
552
633
-------
553
- self
634
+ self : Styler
554
635
555
636
Examples
556
637
--------
@@ -583,7 +664,7 @@ def highlight_null(self, null_color='red'):
583
664
584
665
Returns
585
666
-------
586
- self
667
+ self : Styler
587
668
"""
588
669
self .applymap (self ._highlight_null , null_color = null_color )
589
670
return self
@@ -610,7 +691,7 @@ def background_gradient(self, cmap='PuBu', low=0, high=0, axis=0,
610
691
611
692
Returns
612
693
-------
613
- self
694
+ self : Styler
614
695
615
696
Notes
616
697
-----
@@ -695,7 +776,7 @@ def bar(self, subset=None, axis=0, color='#d65f5f', width=100):
695
776
696
777
Returns
697
778
-------
698
- self
779
+ self : Styler
699
780
"""
700
781
subset = _maybe_numeric_slice (self .data , subset )
701
782
subset = _non_reducing_slice (subset )
@@ -720,7 +801,7 @@ def highlight_max(self, subset=None, color='yellow', axis=0):
720
801
721
802
Returns
722
803
-------
723
- self
804
+ self : Styler
724
805
"""
725
806
return self ._highlight_handler (subset = subset , color = color , axis = axis ,
726
807
max_ = True )
@@ -742,7 +823,7 @@ def highlight_min(self, subset=None, color='yellow', axis=0):
742
823
743
824
Returns
744
825
-------
745
- self
826
+ self : Styler
746
827
"""
747
828
return self ._highlight_handler (subset = subset , color = color , axis = axis ,
748
829
max_ = False )
@@ -771,3 +852,14 @@ def _highlight_extrema(data, color='yellow', max_=True):
771
852
extrema = data == data .min ().min ()
772
853
return pd .DataFrame (np .where (extrema , attr , '' ),
773
854
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