8
8
import copy
9
9
from functools import partial
10
10
from itertools import product
11
- from typing import Optional
11
+ from typing import Any , Callable , DefaultDict , Dict , List , Optional , Sequence , Tuple
12
12
from uuid import uuid1
13
13
14
14
import numpy as np
@@ -71,6 +71,11 @@ class Styler:
71
71
The ``id`` takes the form ``T_<uuid>_row<num_row>_col<num_col>``
72
72
where ``<uuid>`` is the unique identifier, ``<num_row>`` is the row
73
73
number and ``<num_col>`` is the column number.
74
+ na_rep : str, optional
75
+ Representation for missing values.
76
+ If ``na_rep`` is None, no special formatting is applied
77
+
78
+ .. versionadded:: 1.0.0
74
79
75
80
Attributes
76
81
----------
@@ -126,9 +131,10 @@ def __init__(
126
131
caption = None ,
127
132
table_attributes = None ,
128
133
cell_ids = True ,
134
+ na_rep : Optional [str ] = None ,
129
135
):
130
- self .ctx = defaultdict (list )
131
- self ._todo = []
136
+ self .ctx : DefaultDict [ Tuple [ int , int ], List [ str ]] = defaultdict (list )
137
+ self ._todo : List [ Tuple [ Callable , Tuple , Dict ]] = []
132
138
133
139
if not isinstance (data , (pd .Series , pd .DataFrame )):
134
140
raise TypeError ("``data`` must be a Series or DataFrame" )
@@ -149,19 +155,24 @@ def __init__(
149
155
self .precision = precision
150
156
self .table_attributes = table_attributes
151
157
self .hidden_index = False
152
- self .hidden_columns = []
158
+ self .hidden_columns : Sequence [ int ] = []
153
159
self .cell_ids = cell_ids
160
+ self .na_rep = na_rep
154
161
155
162
# display_funcs maps (row, col) -> formatting function
156
163
157
164
def default_display_func (x ):
158
- if is_float (x ):
165
+ if self .na_rep is not None and pd .isna (x ):
166
+ return self .na_rep
167
+ elif is_float (x ):
159
168
display_format = "{0:.{precision}f}" .format (x , precision = self .precision )
160
169
return display_format
161
170
else :
162
171
return x
163
172
164
- self ._display_funcs = defaultdict (lambda : default_display_func )
173
+ self ._display_funcs : DefaultDict [
174
+ Tuple [int , int ], Callable [[Any ], str ]
175
+ ] = defaultdict (lambda : default_display_func )
165
176
166
177
def _repr_html_ (self ):
167
178
"""
@@ -416,16 +427,22 @@ def format_attr(pair):
416
427
table_attributes = table_attr ,
417
428
)
418
429
419
- def format (self , formatter , subset = None ):
430
+ def format (self , formatter , subset = None , na_rep : Optional [ str ] = None ):
420
431
"""
421
432
Format the text display value of cells.
422
433
423
434
Parameters
424
435
----------
425
- formatter : str, callable, or dict
436
+ formatter : str, callable, dict or None
437
+ If ``formatter`` is None, the default formatter is used
426
438
subset : IndexSlice
427
439
An argument to ``DataFrame.loc`` that restricts which elements
428
440
``formatter`` is applied to.
441
+ na_rep : str, optional
442
+ Representation for missing values.
443
+ If ``na_rep`` is None, no special formatting is applied
444
+
445
+ .. versionadded:: 1.0.0
429
446
430
447
Returns
431
448
-------
@@ -451,6 +468,10 @@ def format(self, formatter, subset=None):
451
468
>>> df['c'] = ['a', 'b', 'c', 'd']
452
469
>>> df.style.format({'c': str.upper})
453
470
"""
471
+ if formatter is None :
472
+ assert self ._display_funcs .default_factory is not None
473
+ formatter = self ._display_funcs .default_factory ()
474
+
454
475
if subset is None :
455
476
row_locs = range (len (self .data ))
456
477
col_locs = range (len (self .data .columns ))
@@ -466,16 +487,16 @@ def format(self, formatter, subset=None):
466
487
if is_dict_like (formatter ):
467
488
for col , col_formatter in formatter .items ():
468
489
# formatter must be callable, so '{}' are converted to lambdas
469
- col_formatter = _maybe_wrap_formatter (col_formatter )
490
+ col_formatter = _maybe_wrap_formatter (col_formatter , na_rep )
470
491
col_num = self .data .columns .get_indexer_for ([col ])[0 ]
471
492
472
493
for row_num in row_locs :
473
494
self ._display_funcs [(row_num , col_num )] = col_formatter
474
495
else :
475
496
# single scalar to format all cells with
497
+ formatter = _maybe_wrap_formatter (formatter , na_rep )
476
498
locs = product (* (row_locs , col_locs ))
477
499
for i , j in locs :
478
- formatter = _maybe_wrap_formatter (formatter )
479
500
self ._display_funcs [(i , j )] = formatter
480
501
return self
481
502
@@ -553,6 +574,7 @@ def _copy(self, deepcopy=False):
553
574
caption = self .caption ,
554
575
uuid = self .uuid ,
555
576
table_styles = self .table_styles ,
577
+ na_rep = self .na_rep ,
556
578
)
557
579
if deepcopy :
558
580
styler .ctx = copy .deepcopy (self .ctx )
@@ -896,6 +918,23 @@ def set_table_styles(self, table_styles):
896
918
self .table_styles = table_styles
897
919
return self
898
920
921
+ def set_na_rep (self , na_rep : str ) -> "Styler" :
922
+ """
923
+ Set the missing data representation on a Styler.
924
+
925
+ .. versionadded:: 1.0.0
926
+
927
+ Parameters
928
+ ----------
929
+ na_rep : str
930
+
931
+ Returns
932
+ -------
933
+ self : Styler
934
+ """
935
+ self .na_rep = na_rep
936
+ return self
937
+
899
938
def hide_index (self ):
900
939
"""
901
940
Hide any indices from rendering.
@@ -1487,14 +1526,22 @@ def _get_level_lengths(index, hidden_elements=None):
1487
1526
return non_zero_lengths
1488
1527
1489
1528
1490
- def _maybe_wrap_formatter (formatter ):
1529
+ def _maybe_wrap_formatter (formatter , na_rep : Optional [ str ] ):
1491
1530
if isinstance (formatter , str ):
1492
- return lambda x : formatter .format (x )
1531
+ formatter_func = lambda x : formatter .format (x )
1493
1532
elif callable (formatter ):
1494
- return formatter
1533
+ formatter_func = formatter
1495
1534
else :
1496
1535
msg = (
1497
1536
"Expected a template string or callable, got {formatter} "
1498
1537
"instead" .format (formatter = formatter )
1499
1538
)
1500
1539
raise TypeError (msg )
1540
+
1541
+ if na_rep is None :
1542
+ return formatter_func
1543
+ elif isinstance (na_rep , str ):
1544
+ return lambda x : na_rep if pd .isna (x ) else formatter_func (x )
1545
+ else :
1546
+ msg = "Expected a string, got {na_rep} instead" .format (na_rep = na_rep )
1547
+ raise TypeError (msg )
0 commit comments