Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/pandas/io/formats/style.py: 34%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

611 statements  

1""" 

2Module for applying conditional formatting to DataFrames and Series. 

3""" 

4from __future__ import annotations 

5 

6from contextlib import contextmanager 

7import copy 

8from functools import partial 

9import operator 

10from typing import ( 

11 TYPE_CHECKING, 

12 Any, 

13 Callable, 

14 overload, 

15) 

16import warnings 

17 

18import numpy as np 

19 

20from pandas._config import get_option 

21 

22from pandas.compat._optional import import_optional_dependency 

23from pandas.util._decorators import ( 

24 Substitution, 

25 doc, 

26) 

27from pandas.util._exceptions import find_stack_level 

28 

29import pandas as pd 

30from pandas import ( 

31 IndexSlice, 

32 RangeIndex, 

33) 

34import pandas.core.common as com 

35from pandas.core.frame import ( 

36 DataFrame, 

37 Series, 

38) 

39from pandas.core.generic import NDFrame 

40from pandas.core.shared_docs import _shared_docs 

41 

42from pandas.io.formats.format import save_to_buffer 

43 

44jinja2 = import_optional_dependency("jinja2", extra="DataFrame.style requires jinja2.") 

45 

46from pandas.io.formats.style_render import ( 

47 CSSProperties, 

48 CSSStyles, 

49 ExtFormatter, 

50 StylerRenderer, 

51 Subset, 

52 Tooltips, 

53 format_table_styles, 

54 maybe_convert_css_to_tuples, 

55 non_reducing_slice, 

56 refactor_levels, 

57) 

58 

59if TYPE_CHECKING: 

60 from collections.abc import ( 

61 Generator, 

62 Hashable, 

63 Sequence, 

64 ) 

65 

66 from matplotlib.colors import Colormap 

67 

68 from pandas._typing import ( 

69 Axis, 

70 AxisInt, 

71 FilePath, 

72 IndexLabel, 

73 IntervalClosedType, 

74 Level, 

75 QuantileInterpolation, 

76 Scalar, 

77 StorageOptions, 

78 WriteBuffer, 

79 WriteExcelBuffer, 

80 ) 

81 

82 from pandas import ExcelWriter 

83 

84try: 

85 import matplotlib as mpl 

86 import matplotlib.pyplot as plt 

87 

88 has_mpl = True 

89except ImportError: 

90 has_mpl = False 

91 

92 

93@contextmanager 

94def _mpl(func: Callable) -> Generator[tuple[Any, Any], None, None]: 

95 if has_mpl: 

96 yield plt, mpl 

97 else: 

98 raise ImportError(f"{func.__name__} requires matplotlib.") 

99 

100 

101#### 

102# Shared Doc Strings 

103 

104subset_args = """subset : label, array-like, IndexSlice, optional 

105 A valid 2d input to `DataFrame.loc[<subset>]`, or, in the case of a 1d input 

106 or single key, to `DataFrame.loc[:, <subset>]` where the columns are 

107 prioritised, to limit ``data`` to *before* applying the function.""" 

108 

109properties_args = """props : str, default None 

110 CSS properties to use for highlighting. If ``props`` is given, ``color`` 

111 is not used.""" 

112 

113coloring_args = """color : str, default '{default}' 

114 Background color to use for highlighting.""" 

115 

116buffering_args = """buf : str, path object, file-like object, optional 

117 String, path object (implementing ``os.PathLike[str]``), or file-like 

118 object implementing a string ``write()`` function. If ``None``, the result is 

119 returned as a string.""" 

120 

121encoding_args = """encoding : str, optional 

122 Character encoding setting for file output (and meta tags if available). 

123 Defaults to ``pandas.options.styler.render.encoding`` value of "utf-8".""" 

124 

125# 

126### 

127 

128 

129class Styler(StylerRenderer): 

130 r""" 

131 Helps style a DataFrame or Series according to the data with HTML and CSS. 

132 

133 Parameters 

134 ---------- 

135 data : Series or DataFrame 

136 Data to be styled - either a Series or DataFrame. 

137 precision : int, optional 

138 Precision to round floats to. If not given defaults to 

139 ``pandas.options.styler.format.precision``. 

140 

141 .. versionchanged:: 1.4.0 

142 table_styles : list-like, default None 

143 List of {selector: (attr, value)} dicts; see Notes. 

144 uuid : str, default None 

145 A unique identifier to avoid CSS collisions; generated automatically. 

146 caption : str, tuple, default None 

147 String caption to attach to the table. Tuple only used for LaTeX dual captions. 

148 table_attributes : str, default None 

149 Items that show up in the opening ``<table>`` tag 

150 in addition to automatic (by default) id. 

151 cell_ids : bool, default True 

152 If True, each cell will have an ``id`` attribute in their HTML tag. 

153 The ``id`` takes the form ``T_<uuid>_row<num_row>_col<num_col>`` 

154 where ``<uuid>`` is the unique identifier, ``<num_row>`` is the row 

155 number and ``<num_col>`` is the column number. 

156 na_rep : str, optional 

157 Representation for missing values. 

158 If ``na_rep`` is None, no special formatting is applied, and falls back to 

159 ``pandas.options.styler.format.na_rep``. 

160 

161 uuid_len : int, default 5 

162 If ``uuid`` is not specified, the length of the ``uuid`` to randomly generate 

163 expressed in hex characters, in range [0, 32]. 

164 decimal : str, optional 

165 Character used as decimal separator for floats, complex and integers. If not 

166 given uses ``pandas.options.styler.format.decimal``. 

167 

168 .. versionadded:: 1.3.0 

169 

170 thousands : str, optional, default None 

171 Character used as thousands separator for floats, complex and integers. If not 

172 given uses ``pandas.options.styler.format.thousands``. 

173 

174 .. versionadded:: 1.3.0 

175 

176 escape : str, optional 

177 Use 'html' to replace the characters ``&``, ``<``, ``>``, ``'``, and ``"`` 

178 in cell display string with HTML-safe sequences. 

179 Use 'latex' to replace the characters ``&``, ``%``, ``$``, ``#``, ``_``, 

180 ``{``, ``}``, ``~``, ``^``, and ``\`` in the cell display string with 

181 LaTeX-safe sequences. Use 'latex-math' to replace the characters 

182 the same way as in 'latex' mode, except for math substrings, 

183 which either are surrounded by two characters ``$`` or start with 

184 the character ``\(`` and end with ``\)``. 

185 If not given uses ``pandas.options.styler.format.escape``. 

186 

187 .. versionadded:: 1.3.0 

188 formatter : str, callable, dict, optional 

189 Object to define how values are displayed. See ``Styler.format``. If not given 

190 uses ``pandas.options.styler.format.formatter``. 

191 

192 .. versionadded:: 1.4.0 

193 

194 Attributes 

195 ---------- 

196 env : Jinja2 jinja2.Environment 

197 template_html : Jinja2 Template 

198 template_html_table : Jinja2 Template 

199 template_html_style : Jinja2 Template 

200 template_latex : Jinja2 Template 

201 loader : Jinja2 Loader 

202 

203 See Also 

204 -------- 

205 DataFrame.style : Return a Styler object containing methods for building 

206 a styled HTML representation for the DataFrame. 

207 

208 Notes 

209 ----- 

210 Most styling will be done by passing style functions into 

211 ``Styler.apply`` or ``Styler.map``. Style functions should 

212 return values with strings containing CSS ``'attr: value'`` that will 

213 be applied to the indicated cells. 

214 

215 If using in the Jupyter notebook, Styler has defined a ``_repr_html_`` 

216 to automatically render itself. Otherwise call Styler.to_html to get 

217 the generated HTML. 

218 

219 CSS classes are attached to the generated HTML 

220 

221 * Index and Column names include ``index_name`` and ``level<k>`` 

222 where `k` is its level in a MultiIndex 

223 * Index label cells include 

224 

225 * ``row_heading`` 

226 * ``row<n>`` where `n` is the numeric position of the row 

227 * ``level<k>`` where `k` is the level in a MultiIndex 

228 

229 * Column label cells include 

230 * ``col_heading`` 

231 * ``col<n>`` where `n` is the numeric position of the column 

232 * ``level<k>`` where `k` is the level in a MultiIndex 

233 

234 * Blank cells include ``blank`` 

235 * Data cells include ``data`` 

236 * Trimmed cells include ``col_trim`` or ``row_trim``. 

237 

238 Any, or all, or these classes can be renamed by using the ``css_class_names`` 

239 argument in ``Styler.set_table_classes``, giving a value such as 

240 *{"row": "MY_ROW_CLASS", "col_trim": "", "row_trim": ""}*. 

241 

242 Examples 

243 -------- 

244 >>> df = pd.DataFrame([[1.0, 2.0, 3.0], [4, 5, 6]], index=['a', 'b'], 

245 ... columns=['A', 'B', 'C']) 

246 >>> pd.io.formats.style.Styler(df, precision=2, 

247 ... caption="My table") # doctest: +SKIP 

248 

249 Please see: 

250 `Table Visualization <../../user_guide/style.ipynb>`_ for more examples. 

251 """ 

252 

253 def __init__( 

254 self, 

255 data: DataFrame | Series, 

256 precision: int | None = None, 

257 table_styles: CSSStyles | None = None, 

258 uuid: str | None = None, 

259 caption: str | tuple | list | None = None, 

260 table_attributes: str | None = None, 

261 cell_ids: bool = True, 

262 na_rep: str | None = None, 

263 uuid_len: int = 5, 

264 decimal: str | None = None, 

265 thousands: str | None = None, 

266 escape: str | None = None, 

267 formatter: ExtFormatter | None = None, 

268 ) -> None: 

269 super().__init__( 

270 data=data, 

271 uuid=uuid, 

272 uuid_len=uuid_len, 

273 table_styles=table_styles, 

274 table_attributes=table_attributes, 

275 caption=caption, 

276 cell_ids=cell_ids, 

277 precision=precision, 

278 ) 

279 

280 # validate ordered args 

281 thousands = thousands or get_option("styler.format.thousands") 

282 decimal = decimal or get_option("styler.format.decimal") 

283 na_rep = na_rep or get_option("styler.format.na_rep") 

284 escape = escape or get_option("styler.format.escape") 

285 formatter = formatter or get_option("styler.format.formatter") 

286 # precision is handled by superclass as default for performance 

287 

288 self.format( 

289 formatter=formatter, 

290 precision=precision, 

291 na_rep=na_rep, 

292 escape=escape, 

293 decimal=decimal, 

294 thousands=thousands, 

295 ) 

296 

297 def concat(self, other: Styler) -> Styler: 

298 """ 

299 Append another Styler to combine the output into a single table. 

300 

301 .. versionadded:: 1.5.0 

302 

303 Parameters 

304 ---------- 

305 other : Styler 

306 The other Styler object which has already been styled and formatted. The 

307 data for this Styler must have the same columns as the original, and the 

308 number of index levels must also be the same to render correctly. 

309 

310 Returns 

311 ------- 

312 Styler 

313 

314 Notes 

315 ----- 

316 The purpose of this method is to extend existing styled dataframes with other 

317 metrics that may be useful but may not conform to the original's structure. 

318 For example adding a sub total row, or displaying metrics such as means, 

319 variance or counts. 

320 

321 Styles that are applied using the ``apply``, ``map``, ``apply_index`` 

322 and ``map_index``, and formatting applied with ``format`` and 

323 ``format_index`` will be preserved. 

324 

325 .. warning:: 

326 Only the output methods ``to_html``, ``to_string`` and ``to_latex`` 

327 currently work with concatenated Stylers. 

328 

329 Other output methods, including ``to_excel``, **do not** work with 

330 concatenated Stylers. 

331 

332 The following should be noted: 

333 

334 - ``table_styles``, ``table_attributes``, ``caption`` and ``uuid`` are all 

335 inherited from the original Styler and not ``other``. 

336 - hidden columns and hidden index levels will be inherited from the 

337 original Styler 

338 - ``css`` will be inherited from the original Styler, and the value of 

339 keys ``data``, ``row_heading`` and ``row`` will be prepended with 

340 ``foot0_``. If more concats are chained, their styles will be prepended 

341 with ``foot1_``, ''foot_2'', etc., and if a concatenated style have 

342 another concatanated style, the second style will be prepended with 

343 ``foot{parent}_foot{child}_``. 

344 

345 A common use case is to concatenate user defined functions with 

346 ``DataFrame.agg`` or with described statistics via ``DataFrame.describe``. 

347 See examples. 

348 

349 Examples 

350 -------- 

351 A common use case is adding totals rows, or otherwise, via methods calculated 

352 in ``DataFrame.agg``. 

353 

354 >>> df = pd.DataFrame([[4, 6], [1, 9], [3, 4], [5, 5], [9, 6]], 

355 ... columns=["Mike", "Jim"], 

356 ... index=["Mon", "Tue", "Wed", "Thurs", "Fri"]) 

357 >>> styler = df.style.concat(df.agg(["sum"]).style) # doctest: +SKIP 

358 

359 .. figure:: ../../_static/style/footer_simple.png 

360 

361 Since the concatenated object is a Styler the existing functionality can be 

362 used to conditionally format it as well as the original. 

363 

364 >>> descriptors = df.agg(["sum", "mean", lambda s: s.dtype]) 

365 >>> descriptors.index = ["Total", "Average", "dtype"] 

366 >>> other = (descriptors.style 

367 ... .highlight_max(axis=1, subset=(["Total", "Average"], slice(None))) 

368 ... .format(subset=("Average", slice(None)), precision=2, decimal=",") 

369 ... .map(lambda v: "font-weight: bold;")) 

370 >>> styler = (df.style 

371 ... .highlight_max(color="salmon") 

372 ... .set_table_styles([{"selector": ".foot_row0", 

373 ... "props": "border-top: 1px solid black;"}])) 

374 >>> styler.concat(other) # doctest: +SKIP 

375 

376 .. figure:: ../../_static/style/footer_extended.png 

377 

378 When ``other`` has fewer index levels than the original Styler it is possible 

379 to extend the index in ``other``, with placeholder levels. 

380 

381 >>> df = pd.DataFrame([[1], [2]], 

382 ... index=pd.MultiIndex.from_product([[0], [1, 2]])) 

383 >>> descriptors = df.agg(["sum"]) 

384 >>> descriptors.index = pd.MultiIndex.from_product([[""], descriptors.index]) 

385 >>> df.style.concat(descriptors.style) # doctest: +SKIP 

386 """ 

387 if not isinstance(other, Styler): 

388 raise TypeError("`other` must be of type `Styler`") 

389 if not self.data.columns.equals(other.data.columns): 

390 raise ValueError("`other.data` must have same columns as `Styler.data`") 

391 if not self.data.index.nlevels == other.data.index.nlevels: 

392 raise ValueError( 

393 "number of index levels must be same in `other` " 

394 "as in `Styler`. See documentation for suggestions." 

395 ) 

396 self.concatenated.append(other) 

397 return self 

398 

399 def _repr_html_(self) -> str | None: 

400 """ 

401 Hooks into Jupyter notebook rich display system, which calls _repr_html_ by 

402 default if an object is returned at the end of a cell. 

403 """ 

404 if get_option("styler.render.repr") == "html": 

405 return self.to_html() 

406 return None 

407 

408 def _repr_latex_(self) -> str | None: 

409 if get_option("styler.render.repr") == "latex": 

410 return self.to_latex() 

411 return None 

412 

413 def set_tooltips( 

414 self, 

415 ttips: DataFrame, 

416 props: CSSProperties | None = None, 

417 css_class: str | None = None, 

418 ) -> Styler: 

419 """ 

420 Set the DataFrame of strings on ``Styler`` generating ``:hover`` tooltips. 

421 

422 These string based tooltips are only applicable to ``<td>`` HTML elements, 

423 and cannot be used for column or index headers. 

424 

425 .. versionadded:: 1.3.0 

426 

427 Parameters 

428 ---------- 

429 ttips : DataFrame 

430 DataFrame containing strings that will be translated to tooltips, mapped 

431 by identical column and index values that must exist on the underlying 

432 Styler data. None, NaN values, and empty strings will be ignored and 

433 not affect the rendered HTML. 

434 props : list-like or str, optional 

435 List of (attr, value) tuples or a valid CSS string. If ``None`` adopts 

436 the internal default values described in notes. 

437 css_class : str, optional 

438 Name of the tooltip class used in CSS, should conform to HTML standards. 

439 Only useful if integrating tooltips with external CSS. If ``None`` uses the 

440 internal default value 'pd-t'. 

441 

442 Returns 

443 ------- 

444 Styler 

445 

446 Notes 

447 ----- 

448 Tooltips are created by adding `<span class="pd-t"></span>` to each data cell 

449 and then manipulating the table level CSS to attach pseudo hover and pseudo 

450 after selectors to produce the required the results. 

451 

452 The default properties for the tooltip CSS class are: 

453 

454 - visibility: hidden 

455 - position: absolute 

456 - z-index: 1 

457 - background-color: black 

458 - color: white 

459 - transform: translate(-20px, -20px) 

460 

461 The property 'visibility: hidden;' is a key prerequisite to the hover 

462 functionality, and should always be included in any manual properties 

463 specification, using the ``props`` argument. 

464 

465 Tooltips are not designed to be efficient, and can add large amounts of 

466 additional HTML for larger tables, since they also require that ``cell_ids`` 

467 is forced to `True`. 

468 

469 Examples 

470 -------- 

471 Basic application 

472 

473 >>> df = pd.DataFrame(data=[[0, 1], [2, 3]]) 

474 >>> ttips = pd.DataFrame( 

475 ... data=[["Min", ""], [np.nan, "Max"]], columns=df.columns, index=df.index 

476 ... ) 

477 >>> s = df.style.set_tooltips(ttips).to_html() 

478 

479 Optionally controlling the tooltip visual display 

480 

481 >>> df.style.set_tooltips(ttips, css_class='tt-add', props=[ 

482 ... ('visibility', 'hidden'), 

483 ... ('position', 'absolute'), 

484 ... ('z-index', 1)]) # doctest: +SKIP 

485 >>> df.style.set_tooltips(ttips, css_class='tt-add', 

486 ... props='visibility:hidden; position:absolute; z-index:1;') 

487 ... # doctest: +SKIP 

488 """ 

489 if not self.cell_ids: 

490 # tooltips not optimised for individual cell check. requires reasonable 

491 # redesign and more extensive code for a feature that might be rarely used. 

492 raise NotImplementedError( 

493 "Tooltips can only render with 'cell_ids' is True." 

494 ) 

495 if not ttips.index.is_unique or not ttips.columns.is_unique: 

496 raise KeyError( 

497 "Tooltips render only if `ttips` has unique index and columns." 

498 ) 

499 if self.tooltips is None: # create a default instance if necessary 

500 self.tooltips = Tooltips() 

501 self.tooltips.tt_data = ttips 

502 if props: 

503 self.tooltips.class_properties = props 

504 if css_class: 

505 self.tooltips.class_name = css_class 

506 

507 return self 

508 

509 @doc( 

510 NDFrame.to_excel, 

511 klass="Styler", 

512 storage_options=_shared_docs["storage_options"], 

513 storage_options_versionadded="1.5.0", 

514 ) 

515 def to_excel( 

516 self, 

517 excel_writer: FilePath | WriteExcelBuffer | ExcelWriter, 

518 sheet_name: str = "Sheet1", 

519 na_rep: str = "", 

520 float_format: str | None = None, 

521 columns: Sequence[Hashable] | None = None, 

522 header: Sequence[Hashable] | bool = True, 

523 index: bool = True, 

524 index_label: IndexLabel | None = None, 

525 startrow: int = 0, 

526 startcol: int = 0, 

527 engine: str | None = None, 

528 merge_cells: bool = True, 

529 encoding: str | None = None, 

530 inf_rep: str = "inf", 

531 verbose: bool = True, 

532 freeze_panes: tuple[int, int] | None = None, 

533 storage_options: StorageOptions | None = None, 

534 ) -> None: 

535 from pandas.io.formats.excel import ExcelFormatter 

536 

537 formatter = ExcelFormatter( 

538 self, 

539 na_rep=na_rep, 

540 cols=columns, 

541 header=header, 

542 float_format=float_format, 

543 index=index, 

544 index_label=index_label, 

545 merge_cells=merge_cells, 

546 inf_rep=inf_rep, 

547 ) 

548 formatter.write( 

549 excel_writer, 

550 sheet_name=sheet_name, 

551 startrow=startrow, 

552 startcol=startcol, 

553 freeze_panes=freeze_panes, 

554 engine=engine, 

555 storage_options=storage_options, 

556 ) 

557 

558 @overload 

559 def to_latex( 

560 self, 

561 buf: FilePath | WriteBuffer[str], 

562 *, 

563 column_format: str | None = ..., 

564 position: str | None = ..., 

565 position_float: str | None = ..., 

566 hrules: bool | None = ..., 

567 clines: str | None = ..., 

568 label: str | None = ..., 

569 caption: str | tuple | None = ..., 

570 sparse_index: bool | None = ..., 

571 sparse_columns: bool | None = ..., 

572 multirow_align: str | None = ..., 

573 multicol_align: str | None = ..., 

574 siunitx: bool = ..., 

575 environment: str | None = ..., 

576 encoding: str | None = ..., 

577 convert_css: bool = ..., 

578 ) -> None: 

579 ... 

580 

581 @overload 

582 def to_latex( 

583 self, 

584 buf: None = ..., 

585 *, 

586 column_format: str | None = ..., 

587 position: str | None = ..., 

588 position_float: str | None = ..., 

589 hrules: bool | None = ..., 

590 clines: str | None = ..., 

591 label: str | None = ..., 

592 caption: str | tuple | None = ..., 

593 sparse_index: bool | None = ..., 

594 sparse_columns: bool | None = ..., 

595 multirow_align: str | None = ..., 

596 multicol_align: str | None = ..., 

597 siunitx: bool = ..., 

598 environment: str | None = ..., 

599 encoding: str | None = ..., 

600 convert_css: bool = ..., 

601 ) -> str: 

602 ... 

603 

604 def to_latex( 

605 self, 

606 buf: FilePath | WriteBuffer[str] | None = None, 

607 *, 

608 column_format: str | None = None, 

609 position: str | None = None, 

610 position_float: str | None = None, 

611 hrules: bool | None = None, 

612 clines: str | None = None, 

613 label: str | None = None, 

614 caption: str | tuple | None = None, 

615 sparse_index: bool | None = None, 

616 sparse_columns: bool | None = None, 

617 multirow_align: str | None = None, 

618 multicol_align: str | None = None, 

619 siunitx: bool = False, 

620 environment: str | None = None, 

621 encoding: str | None = None, 

622 convert_css: bool = False, 

623 ) -> str | None: 

624 r""" 

625 Write Styler to a file, buffer or string in LaTeX format. 

626 

627 .. versionadded:: 1.3.0 

628 

629 Parameters 

630 ---------- 

631 buf : str, path object, file-like object, or None, default None 

632 String, path object (implementing ``os.PathLike[str]``), or file-like 

633 object implementing a string ``write()`` function. If None, the result is 

634 returned as a string. 

635 column_format : str, optional 

636 The LaTeX column specification placed in location: 

637 

638 \\begin{tabular}{<column_format>} 

639 

640 Defaults to 'l' for index and 

641 non-numeric data columns, and, for numeric data columns, 

642 to 'r' by default, or 'S' if ``siunitx`` is ``True``. 

643 position : str, optional 

644 The LaTeX positional argument (e.g. 'h!') for tables, placed in location: 

645 

646 ``\\begin{table}[<position>]``. 

647 position_float : {"centering", "raggedleft", "raggedright"}, optional 

648 The LaTeX float command placed in location: 

649 

650 \\begin{table}[<position>] 

651 

652 \\<position_float> 

653 

654 Cannot be used if ``environment`` is "longtable". 

655 hrules : bool 

656 Set to `True` to add \\toprule, \\midrule and \\bottomrule from the 

657 {booktabs} LaTeX package. 

658 Defaults to ``pandas.options.styler.latex.hrules``, which is `False`. 

659 

660 .. versionchanged:: 1.4.0 

661 clines : str, optional 

662 Use to control adding \\cline commands for the index labels separation. 

663 Possible values are: 

664 

665 - `None`: no cline commands are added (default). 

666 - `"all;data"`: a cline is added for every index value extending the 

667 width of the table, including data entries. 

668 - `"all;index"`: as above with lines extending only the width of the 

669 index entries. 

670 - `"skip-last;data"`: a cline is added for each index value except the 

671 last level (which is never sparsified), extending the widtn of the 

672 table. 

673 - `"skip-last;index"`: as above with lines extending only the width of the 

674 index entries. 

675 

676 .. versionadded:: 1.4.0 

677 label : str, optional 

678 The LaTeX label included as: \\label{<label>}. 

679 This is used with \\ref{<label>} in the main .tex file. 

680 caption : str, tuple, optional 

681 If string, the LaTeX table caption included as: \\caption{<caption>}. 

682 If tuple, i.e ("full caption", "short caption"), the caption included 

683 as: \\caption[<caption[1]>]{<caption[0]>}. 

684 sparse_index : bool, optional 

685 Whether to sparsify the display of a hierarchical index. Setting to False 

686 will display each explicit level element in a hierarchical key for each row. 

687 Defaults to ``pandas.options.styler.sparse.index``, which is `True`. 

688 sparse_columns : bool, optional 

689 Whether to sparsify the display of a hierarchical index. Setting to False 

690 will display each explicit level element in a hierarchical key for each 

691 column. Defaults to ``pandas.options.styler.sparse.columns``, which 

692 is `True`. 

693 multirow_align : {"c", "t", "b", "naive"}, optional 

694 If sparsifying hierarchical MultiIndexes whether to align text centrally, 

695 at the top or bottom using the multirow package. If not given defaults to 

696 ``pandas.options.styler.latex.multirow_align``, which is `"c"`. 

697 If "naive" is given renders without multirow. 

698 

699 .. versionchanged:: 1.4.0 

700 multicol_align : {"r", "c", "l", "naive-l", "naive-r"}, optional 

701 If sparsifying hierarchical MultiIndex columns whether to align text at 

702 the left, centrally, or at the right. If not given defaults to 

703 ``pandas.options.styler.latex.multicol_align``, which is "r". 

704 If a naive option is given renders without multicol. 

705 Pipe decorators can also be added to non-naive values to draw vertical 

706 rules, e.g. "\|r" will draw a rule on the left side of right aligned merged 

707 cells. 

708 

709 .. versionchanged:: 1.4.0 

710 siunitx : bool, default False 

711 Set to ``True`` to structure LaTeX compatible with the {siunitx} package. 

712 environment : str, optional 

713 If given, the environment that will replace 'table' in ``\\begin{table}``. 

714 If 'longtable' is specified then a more suitable template is 

715 rendered. If not given defaults to 

716 ``pandas.options.styler.latex.environment``, which is `None`. 

717 

718 .. versionadded:: 1.4.0 

719 encoding : str, optional 

720 Character encoding setting. Defaults 

721 to ``pandas.options.styler.render.encoding``, which is "utf-8". 

722 convert_css : bool, default False 

723 Convert simple cell-styles from CSS to LaTeX format. Any CSS not found in 

724 conversion table is dropped. A style can be forced by adding option 

725 `--latex`. See notes. 

726 

727 Returns 

728 ------- 

729 str or None 

730 If `buf` is None, returns the result as a string. Otherwise returns `None`. 

731 

732 See Also 

733 -------- 

734 Styler.format: Format the text display value of cells. 

735 

736 Notes 

737 ----- 

738 **Latex Packages** 

739 

740 For the following features we recommend the following LaTeX inclusions: 

741 

742 ===================== ========================================================== 

743 Feature Inclusion 

744 ===================== ========================================================== 

745 sparse columns none: included within default {tabular} environment 

746 sparse rows \\usepackage{multirow} 

747 hrules \\usepackage{booktabs} 

748 colors \\usepackage[table]{xcolor} 

749 siunitx \\usepackage{siunitx} 

750 bold (with siunitx) | \\usepackage{etoolbox} 

751 | \\robustify\\bfseries 

752 | \\sisetup{detect-all = true} *(within {document})* 

753 italic (with siunitx) | \\usepackage{etoolbox} 

754 | \\robustify\\itshape 

755 | \\sisetup{detect-all = true} *(within {document})* 

756 environment \\usepackage{longtable} if arg is "longtable" 

757 | or any other relevant environment package 

758 hyperlinks \\usepackage{hyperref} 

759 ===================== ========================================================== 

760 

761 **Cell Styles** 

762 

763 LaTeX styling can only be rendered if the accompanying styling functions have 

764 been constructed with appropriate LaTeX commands. All styling 

765 functionality is built around the concept of a CSS ``(<attribute>, <value>)`` 

766 pair (see `Table Visualization <../../user_guide/style.ipynb>`_), and this 

767 should be replaced by a LaTeX 

768 ``(<command>, <options>)`` approach. Each cell will be styled individually 

769 using nested LaTeX commands with their accompanied options. 

770 

771 For example the following code will highlight and bold a cell in HTML-CSS: 

772 

773 >>> df = pd.DataFrame([[1,2], [3,4]]) 

774 >>> s = df.style.highlight_max(axis=None, 

775 ... props='background-color:red; font-weight:bold;') 

776 >>> s.to_html() # doctest: +SKIP 

777 

778 The equivalent using LaTeX only commands is the following: 

779 

780 >>> s = df.style.highlight_max(axis=None, 

781 ... props='cellcolor:{red}; bfseries: ;') 

782 >>> s.to_latex() # doctest: +SKIP 

783 

784 Internally these structured LaTeX ``(<command>, <options>)`` pairs 

785 are translated to the 

786 ``display_value`` with the default structure: 

787 ``\<command><options> <display_value>``. 

788 Where there are multiple commands the latter is nested recursively, so that 

789 the above example highlighted cell is rendered as 

790 ``\cellcolor{red} \bfseries 4``. 

791 

792 Occasionally this format does not suit the applied command, or 

793 combination of LaTeX packages that is in use, so additional flags can be 

794 added to the ``<options>``, within the tuple, to result in different 

795 positions of required braces (the **default** being the same as ``--nowrap``): 

796 

797 =================================== ============================================ 

798 Tuple Format Output Structure 

799 =================================== ============================================ 

800 (<command>,<options>) \\<command><options> <display_value> 

801 (<command>,<options> ``--nowrap``) \\<command><options> <display_value> 

802 (<command>,<options> ``--rwrap``) \\<command><options>{<display_value>} 

803 (<command>,<options> ``--wrap``) {\\<command><options> <display_value>} 

804 (<command>,<options> ``--lwrap``) {\\<command><options>} <display_value> 

805 (<command>,<options> ``--dwrap``) {\\<command><options>}{<display_value>} 

806 =================================== ============================================ 

807 

808 For example the `textbf` command for font-weight 

809 should always be used with `--rwrap` so ``('textbf', '--rwrap')`` will render a 

810 working cell, wrapped with braces, as ``\textbf{<display_value>}``. 

811 

812 A more comprehensive example is as follows: 

813 

814 >>> df = pd.DataFrame([[1, 2.2, "dogs"], [3, 4.4, "cats"], [2, 6.6, "cows"]], 

815 ... index=["ix1", "ix2", "ix3"], 

816 ... columns=["Integers", "Floats", "Strings"]) 

817 >>> s = df.style.highlight_max( 

818 ... props='cellcolor:[HTML]{FFFF00}; color:{red};' 

819 ... 'textit:--rwrap; textbf:--rwrap;' 

820 ... ) 

821 >>> s.to_latex() # doctest: +SKIP 

822 

823 .. figure:: ../../_static/style/latex_1.png 

824 

825 **Table Styles** 

826 

827 Internally Styler uses its ``table_styles`` object to parse the 

828 ``column_format``, ``position``, ``position_float``, and ``label`` 

829 input arguments. These arguments are added to table styles in the format: 

830 

831 .. code-block:: python 

832 

833 set_table_styles([ 

834 {"selector": "column_format", "props": f":{column_format};"}, 

835 {"selector": "position", "props": f":{position};"}, 

836 {"selector": "position_float", "props": f":{position_float};"}, 

837 {"selector": "label", "props": f":{{{label.replace(':','§')}}};"} 

838 ], overwrite=False) 

839 

840 Exception is made for the ``hrules`` argument which, in fact, controls all three 

841 commands: ``toprule``, ``bottomrule`` and ``midrule`` simultaneously. Instead of 

842 setting ``hrules`` to ``True``, it is also possible to set each 

843 individual rule definition, by manually setting the ``table_styles``, 

844 for example below we set a regular ``toprule``, set an ``hline`` for 

845 ``bottomrule`` and exclude the ``midrule``: 

846 

847 .. code-block:: python 

848 

849 set_table_styles([ 

850 {'selector': 'toprule', 'props': ':toprule;'}, 

851 {'selector': 'bottomrule', 'props': ':hline;'}, 

852 ], overwrite=False) 

853 

854 If other ``commands`` are added to table styles they will be detected, and 

855 positioned immediately above the '\\begin{tabular}' command. For example to 

856 add odd and even row coloring, from the {colortbl} package, in format 

857 ``\rowcolors{1}{pink}{red}``, use: 

858 

859 .. code-block:: python 

860 

861 set_table_styles([ 

862 {'selector': 'rowcolors', 'props': ':{1}{pink}{red};'} 

863 ], overwrite=False) 

864 

865 A more comprehensive example using these arguments is as follows: 

866 

867 >>> df.columns = pd.MultiIndex.from_tuples([ 

868 ... ("Numeric", "Integers"), 

869 ... ("Numeric", "Floats"), 

870 ... ("Non-Numeric", "Strings") 

871 ... ]) 

872 >>> df.index = pd.MultiIndex.from_tuples([ 

873 ... ("L0", "ix1"), ("L0", "ix2"), ("L1", "ix3") 

874 ... ]) 

875 >>> s = df.style.highlight_max( 

876 ... props='cellcolor:[HTML]{FFFF00}; color:{red}; itshape:; bfseries:;' 

877 ... ) 

878 >>> s.to_latex( 

879 ... column_format="rrrrr", position="h", position_float="centering", 

880 ... hrules=True, label="table:5", caption="Styled LaTeX Table", 

881 ... multirow_align="t", multicol_align="r" 

882 ... ) # doctest: +SKIP 

883 

884 .. figure:: ../../_static/style/latex_2.png 

885 

886 **Formatting** 

887 

888 To format values :meth:`Styler.format` should be used prior to calling 

889 `Styler.to_latex`, as well as other methods such as :meth:`Styler.hide` 

890 for example: 

891 

892 >>> s.clear() 

893 >>> s.table_styles = [] 

894 >>> s.caption = None 

895 >>> s.format({ 

896 ... ("Numeric", "Integers"): '\${}', 

897 ... ("Numeric", "Floats"): '{:.3f}', 

898 ... ("Non-Numeric", "Strings"): str.upper 

899 ... }) # doctest: +SKIP 

900 Numeric Non-Numeric 

901 Integers Floats Strings 

902 L0 ix1 $1 2.200 DOGS 

903 ix2 $3 4.400 CATS 

904 L1 ix3 $2 6.600 COWS 

905 

906 >>> s.to_latex() # doctest: +SKIP 

907 \begin{tabular}{llrrl} 

908 {} & {} & \multicolumn{2}{r}{Numeric} & {Non-Numeric} \\ 

909 {} & {} & {Integers} & {Floats} & {Strings} \\ 

910 \multirow[c]{2}{*}{L0} & ix1 & \\$1 & 2.200 & DOGS \\ 

911 & ix2 & \$3 & 4.400 & CATS \\ 

912 L1 & ix3 & \$2 & 6.600 & COWS \\ 

913 \end{tabular} 

914 

915 **CSS Conversion** 

916 

917 This method can convert a Styler constructured with HTML-CSS to LaTeX using 

918 the following limited conversions. 

919 

920 ================== ==================== ============= ========================== 

921 CSS Attribute CSS value LaTeX Command LaTeX Options 

922 ================== ==================== ============= ========================== 

923 font-weight | bold | bfseries 

924 | bolder | bfseries 

925 font-style | italic | itshape 

926 | oblique | slshape 

927 background-color | red cellcolor | {red}--lwrap 

928 | #fe01ea | [HTML]{FE01EA}--lwrap 

929 | #f0e | [HTML]{FF00EE}--lwrap 

930 | rgb(128,255,0) | [rgb]{0.5,1,0}--lwrap 

931 | rgba(128,0,0,0.5) | [rgb]{0.5,0,0}--lwrap 

932 | rgb(25%,255,50%) | [rgb]{0.25,1,0.5}--lwrap 

933 color | red color | {red} 

934 | #fe01ea | [HTML]{FE01EA} 

935 | #f0e | [HTML]{FF00EE} 

936 | rgb(128,255,0) | [rgb]{0.5,1,0} 

937 | rgba(128,0,0,0.5) | [rgb]{0.5,0,0} 

938 | rgb(25%,255,50%) | [rgb]{0.25,1,0.5} 

939 ================== ==================== ============= ========================== 

940 

941 It is also possible to add user-defined LaTeX only styles to a HTML-CSS Styler 

942 using the ``--latex`` flag, and to add LaTeX parsing options that the 

943 converter will detect within a CSS-comment. 

944 

945 >>> df = pd.DataFrame([[1]]) 

946 >>> df.style.set_properties( 

947 ... **{"font-weight": "bold /* --dwrap */", "Huge": "--latex--rwrap"} 

948 ... ).to_latex(convert_css=True) # doctest: +SKIP 

949 \begin{tabular}{lr} 

950 {} & {0} \\ 

951 0 & {\bfseries}{\Huge{1}} \\ 

952 \end{tabular} 

953 

954 Examples 

955 -------- 

956 Below we give a complete step by step example adding some advanced features 

957 and noting some common gotchas. 

958 

959 First we create the DataFrame and Styler as usual, including MultiIndex rows 

960 and columns, which allow for more advanced formatting options: 

961 

962 >>> cidx = pd.MultiIndex.from_arrays([ 

963 ... ["Equity", "Equity", "Equity", "Equity", 

964 ... "Stats", "Stats", "Stats", "Stats", "Rating"], 

965 ... ["Energy", "Energy", "Consumer", "Consumer", "", "", "", "", ""], 

966 ... ["BP", "Shell", "H&M", "Unilever", 

967 ... "Std Dev", "Variance", "52w High", "52w Low", ""] 

968 ... ]) 

969 >>> iidx = pd.MultiIndex.from_arrays([ 

970 ... ["Equity", "Equity", "Equity", "Equity"], 

971 ... ["Energy", "Energy", "Consumer", "Consumer"], 

972 ... ["BP", "Shell", "H&M", "Unilever"] 

973 ... ]) 

974 >>> styler = pd.DataFrame([ 

975 ... [1, 0.8, 0.66, 0.72, 32.1678, 32.1678**2, 335.12, 240.89, "Buy"], 

976 ... [0.8, 1.0, 0.69, 0.79, 1.876, 1.876**2, 14.12, 19.78, "Hold"], 

977 ... [0.66, 0.69, 1.0, 0.86, 7, 7**2, 210.9, 140.6, "Buy"], 

978 ... [0.72, 0.79, 0.86, 1.0, 213.76, 213.76**2, 2807, 3678, "Sell"], 

979 ... ], columns=cidx, index=iidx).style 

980 

981 Second we will format the display and, since our table is quite wide, will 

982 hide the repeated level-0 of the index: 

983 

984 >>> (styler.format(subset="Equity", precision=2) 

985 ... .format(subset="Stats", precision=1, thousands=",") 

986 ... .format(subset="Rating", formatter=str.upper) 

987 ... .format_index(escape="latex", axis=1) 

988 ... .format_index(escape="latex", axis=0) 

989 ... .hide(level=0, axis=0)) # doctest: +SKIP 

990 

991 Note that one of the string entries of the index and column headers is "H&M". 

992 Without applying the `escape="latex"` option to the `format_index` method the 

993 resultant LaTeX will fail to render, and the error returned is quite 

994 difficult to debug. Using the appropriate escape the "&" is converted to "\\&". 

995 

996 Thirdly we will apply some (CSS-HTML) styles to our object. We will use a 

997 builtin method and also define our own method to highlight the stock 

998 recommendation: 

999 

1000 >>> def rating_color(v): 

1001 ... if v == "Buy": color = "#33ff85" 

1002 ... elif v == "Sell": color = "#ff5933" 

1003 ... else: color = "#ffdd33" 

1004 ... return f"color: {color}; font-weight: bold;" 

1005 >>> (styler.background_gradient(cmap="inferno", subset="Equity", vmin=0, vmax=1) 

1006 ... .map(rating_color, subset="Rating")) # doctest: +SKIP 

1007 

1008 All the above styles will work with HTML (see below) and LaTeX upon conversion: 

1009 

1010 .. figure:: ../../_static/style/latex_stocks_html.png 

1011 

1012 However, we finally want to add one LaTeX only style 

1013 (from the {graphicx} package), that is not easy to convert from CSS and 

1014 pandas does not support it. Notice the `--latex` flag used here, 

1015 as well as `--rwrap` to ensure this is formatted correctly and 

1016 not ignored upon conversion. 

1017 

1018 >>> styler.map_index( 

1019 ... lambda v: "rotatebox:{45}--rwrap--latex;", level=2, axis=1 

1020 ... ) # doctest: +SKIP 

1021 

1022 Finally we render our LaTeX adding in other options as required: 

1023 

1024 >>> styler.to_latex( 

1025 ... caption="Selected stock correlation and simple statistics.", 

1026 ... clines="skip-last;data", 

1027 ... convert_css=True, 

1028 ... position_float="centering", 

1029 ... multicol_align="|c|", 

1030 ... hrules=True, 

1031 ... ) # doctest: +SKIP 

1032 \begin{table} 

1033 \centering 

1034 \caption{Selected stock correlation and simple statistics.} 

1035 \begin{tabular}{llrrrrrrrrl} 

1036 \toprule 

1037 & & \multicolumn{4}{|c|}{Equity} & \multicolumn{4}{|c|}{Stats} & Rating \\ 

1038 & & \multicolumn{2}{|c|}{Energy} & \multicolumn{2}{|c|}{Consumer} & 

1039 \multicolumn{4}{|c|}{} & \\ 

1040 & & \rotatebox{45}{BP} & \rotatebox{45}{Shell} & \rotatebox{45}{H\&M} & 

1041 \rotatebox{45}{Unilever} & \rotatebox{45}{Std Dev} & \rotatebox{45}{Variance} & 

1042 \rotatebox{45}{52w High} & \rotatebox{45}{52w Low} & \rotatebox{45}{} \\ 

1043 \midrule 

1044 \multirow[c]{2}{*}{Energy} & BP & {\cellcolor[HTML]{FCFFA4}} 

1045 \color[HTML]{000000} 1.00 & {\cellcolor[HTML]{FCA50A}} \color[HTML]{000000} 

1046 0.80 & {\cellcolor[HTML]{EB6628}} \color[HTML]{F1F1F1} 0.66 & 

1047 {\cellcolor[HTML]{F68013}} \color[HTML]{F1F1F1} 0.72 & 32.2 & 1,034.8 & 335.1 

1048 & 240.9 & \color[HTML]{33FF85} \bfseries BUY \\ 

1049 & Shell & {\cellcolor[HTML]{FCA50A}} \color[HTML]{000000} 0.80 & 

1050 {\cellcolor[HTML]{FCFFA4}} \color[HTML]{000000} 1.00 & 

1051 {\cellcolor[HTML]{F1731D}} \color[HTML]{F1F1F1} 0.69 & 

1052 {\cellcolor[HTML]{FCA108}} \color[HTML]{000000} 0.79 & 1.9 & 3.5 & 14.1 & 

1053 19.8 & \color[HTML]{FFDD33} \bfseries HOLD \\ 

1054 \cline{1-11} 

1055 \multirow[c]{2}{*}{Consumer} & H\&M & {\cellcolor[HTML]{EB6628}} 

1056 \color[HTML]{F1F1F1} 0.66 & {\cellcolor[HTML]{F1731D}} \color[HTML]{F1F1F1} 

1057 0.69 & {\cellcolor[HTML]{FCFFA4}} \color[HTML]{000000} 1.00 & 

1058 {\cellcolor[HTML]{FAC42A}} \color[HTML]{000000} 0.86 & 7.0 & 49.0 & 210.9 & 

1059 140.6 & \color[HTML]{33FF85} \bfseries BUY \\ 

1060 & Unilever & {\cellcolor[HTML]{F68013}} \color[HTML]{F1F1F1} 0.72 & 

1061 {\cellcolor[HTML]{FCA108}} \color[HTML]{000000} 0.79 & 

1062 {\cellcolor[HTML]{FAC42A}} \color[HTML]{000000} 0.86 & 

1063 {\cellcolor[HTML]{FCFFA4}} \color[HTML]{000000} 1.00 & 213.8 & 45,693.3 & 

1064 2,807.0 & 3,678.0 & \color[HTML]{FF5933} \bfseries SELL \\ 

1065 \cline{1-11} 

1066 \bottomrule 

1067 \end{tabular} 

1068 \end{table} 

1069 

1070 .. figure:: ../../_static/style/latex_stocks.png 

1071 """ 

1072 obj = self._copy(deepcopy=True) # manipulate table_styles on obj, not self 

1073 

1074 table_selectors = ( 

1075 [style["selector"] for style in self.table_styles] 

1076 if self.table_styles is not None 

1077 else [] 

1078 ) 

1079 

1080 if column_format is not None: 

1081 # add more recent setting to table_styles 

1082 obj.set_table_styles( 

1083 [{"selector": "column_format", "props": f":{column_format}"}], 

1084 overwrite=False, 

1085 ) 

1086 elif "column_format" in table_selectors: 

1087 pass # adopt what has been previously set in table_styles 

1088 else: 

1089 # create a default: set float, complex, int cols to 'r' ('S'), index to 'l' 

1090 _original_columns = self.data.columns 

1091 self.data.columns = RangeIndex(stop=len(self.data.columns)) 

1092 numeric_cols = self.data._get_numeric_data().columns.to_list() 

1093 self.data.columns = _original_columns 

1094 column_format = "" 

1095 for level in range(self.index.nlevels): 

1096 column_format += "" if self.hide_index_[level] else "l" 

1097 for ci, _ in enumerate(self.data.columns): 

1098 if ci not in self.hidden_columns: 

1099 column_format += ( 

1100 ("r" if not siunitx else "S") if ci in numeric_cols else "l" 

1101 ) 

1102 obj.set_table_styles( 

1103 [{"selector": "column_format", "props": f":{column_format}"}], 

1104 overwrite=False, 

1105 ) 

1106 

1107 if position: 

1108 obj.set_table_styles( 

1109 [{"selector": "position", "props": f":{position}"}], 

1110 overwrite=False, 

1111 ) 

1112 

1113 if position_float: 

1114 if environment == "longtable": 

1115 raise ValueError( 

1116 "`position_float` cannot be used in 'longtable' `environment`" 

1117 ) 

1118 if position_float not in ["raggedright", "raggedleft", "centering"]: 

1119 raise ValueError( 

1120 f"`position_float` should be one of " 

1121 f"'raggedright', 'raggedleft', 'centering', " 

1122 f"got: '{position_float}'" 

1123 ) 

1124 obj.set_table_styles( 

1125 [{"selector": "position_float", "props": f":{position_float}"}], 

1126 overwrite=False, 

1127 ) 

1128 

1129 hrules = get_option("styler.latex.hrules") if hrules is None else hrules 

1130 if hrules: 

1131 obj.set_table_styles( 

1132 [ 

1133 {"selector": "toprule", "props": ":toprule"}, 

1134 {"selector": "midrule", "props": ":midrule"}, 

1135 {"selector": "bottomrule", "props": ":bottomrule"}, 

1136 ], 

1137 overwrite=False, 

1138 ) 

1139 

1140 if label: 

1141 obj.set_table_styles( 

1142 [{"selector": "label", "props": f":{{{label.replace(':', '§')}}}"}], 

1143 overwrite=False, 

1144 ) 

1145 

1146 if caption: 

1147 obj.set_caption(caption) 

1148 

1149 if sparse_index is None: 

1150 sparse_index = get_option("styler.sparse.index") 

1151 if sparse_columns is None: 

1152 sparse_columns = get_option("styler.sparse.columns") 

1153 environment = environment or get_option("styler.latex.environment") 

1154 multicol_align = multicol_align or get_option("styler.latex.multicol_align") 

1155 multirow_align = multirow_align or get_option("styler.latex.multirow_align") 

1156 latex = obj._render_latex( 

1157 sparse_index=sparse_index, 

1158 sparse_columns=sparse_columns, 

1159 multirow_align=multirow_align, 

1160 multicol_align=multicol_align, 

1161 environment=environment, 

1162 convert_css=convert_css, 

1163 siunitx=siunitx, 

1164 clines=clines, 

1165 ) 

1166 

1167 encoding = ( 

1168 (encoding or get_option("styler.render.encoding")) 

1169 if isinstance(buf, str) # i.e. a filepath 

1170 else encoding 

1171 ) 

1172 return save_to_buffer(latex, buf=buf, encoding=encoding) 

1173 

1174 @overload 

1175 def to_html( 

1176 self, 

1177 buf: FilePath | WriteBuffer[str], 

1178 *, 

1179 table_uuid: str | None = ..., 

1180 table_attributes: str | None = ..., 

1181 sparse_index: bool | None = ..., 

1182 sparse_columns: bool | None = ..., 

1183 bold_headers: bool = ..., 

1184 caption: str | None = ..., 

1185 max_rows: int | None = ..., 

1186 max_columns: int | None = ..., 

1187 encoding: str | None = ..., 

1188 doctype_html: bool = ..., 

1189 exclude_styles: bool = ..., 

1190 **kwargs, 

1191 ) -> None: 

1192 ... 

1193 

1194 @overload 

1195 def to_html( 

1196 self, 

1197 buf: None = ..., 

1198 *, 

1199 table_uuid: str | None = ..., 

1200 table_attributes: str | None = ..., 

1201 sparse_index: bool | None = ..., 

1202 sparse_columns: bool | None = ..., 

1203 bold_headers: bool = ..., 

1204 caption: str | None = ..., 

1205 max_rows: int | None = ..., 

1206 max_columns: int | None = ..., 

1207 encoding: str | None = ..., 

1208 doctype_html: bool = ..., 

1209 exclude_styles: bool = ..., 

1210 **kwargs, 

1211 ) -> str: 

1212 ... 

1213 

1214 @Substitution(buf=buffering_args, encoding=encoding_args) 

1215 def to_html( 

1216 self, 

1217 buf: FilePath | WriteBuffer[str] | None = None, 

1218 *, 

1219 table_uuid: str | None = None, 

1220 table_attributes: str | None = None, 

1221 sparse_index: bool | None = None, 

1222 sparse_columns: bool | None = None, 

1223 bold_headers: bool = False, 

1224 caption: str | None = None, 

1225 max_rows: int | None = None, 

1226 max_columns: int | None = None, 

1227 encoding: str | None = None, 

1228 doctype_html: bool = False, 

1229 exclude_styles: bool = False, 

1230 **kwargs, 

1231 ) -> str | None: 

1232 """ 

1233 Write Styler to a file, buffer or string in HTML-CSS format. 

1234 

1235 .. versionadded:: 1.3.0 

1236 

1237 Parameters 

1238 ---------- 

1239 %(buf)s 

1240 table_uuid : str, optional 

1241 Id attribute assigned to the <table> HTML element in the format: 

1242 

1243 ``<table id="T_<table_uuid>" ..>`` 

1244 

1245 If not given uses Styler's initially assigned value. 

1246 table_attributes : str, optional 

1247 Attributes to assign within the `<table>` HTML element in the format: 

1248 

1249 ``<table .. <table_attributes> >`` 

1250 

1251 If not given defaults to Styler's preexisting value. 

1252 sparse_index : bool, optional 

1253 Whether to sparsify the display of a hierarchical index. Setting to False 

1254 will display each explicit level element in a hierarchical key for each row. 

1255 Defaults to ``pandas.options.styler.sparse.index`` value. 

1256 

1257 .. versionadded:: 1.4.0 

1258 sparse_columns : bool, optional 

1259 Whether to sparsify the display of a hierarchical index. Setting to False 

1260 will display each explicit level element in a hierarchical key for each 

1261 column. Defaults to ``pandas.options.styler.sparse.columns`` value. 

1262 

1263 .. versionadded:: 1.4.0 

1264 bold_headers : bool, optional 

1265 Adds "font-weight: bold;" as a CSS property to table style header cells. 

1266 

1267 .. versionadded:: 1.4.0 

1268 caption : str, optional 

1269 Set, or overwrite, the caption on Styler before rendering. 

1270 

1271 .. versionadded:: 1.4.0 

1272 max_rows : int, optional 

1273 The maximum number of rows that will be rendered. Defaults to 

1274 ``pandas.options.styler.render.max_rows/max_columns``. 

1275 

1276 .. versionadded:: 1.4.0 

1277 max_columns : int, optional 

1278 The maximum number of columns that will be rendered. Defaults to 

1279 ``pandas.options.styler.render.max_columns``, which is None. 

1280 

1281 Rows and columns may be reduced if the number of total elements is 

1282 large. This value is set to ``pandas.options.styler.render.max_elements``, 

1283 which is 262144 (18 bit browser rendering). 

1284 

1285 .. versionadded:: 1.4.0 

1286 %(encoding)s 

1287 doctype_html : bool, default False 

1288 Whether to output a fully structured HTML file including all 

1289 HTML elements, or just the core ``<style>`` and ``<table>`` elements. 

1290 exclude_styles : bool, default False 

1291 Whether to include the ``<style>`` element and all associated element 

1292 ``class`` and ``id`` identifiers, or solely the ``<table>`` element without 

1293 styling identifiers. 

1294 **kwargs 

1295 Any additional keyword arguments are passed through to the jinja2 

1296 ``self.template.render`` process. This is useful when you need to provide 

1297 additional variables for a custom template. 

1298 

1299 Returns 

1300 ------- 

1301 str or None 

1302 If `buf` is None, returns the result as a string. Otherwise returns `None`. 

1303 

1304 See Also 

1305 -------- 

1306 DataFrame.to_html: Write a DataFrame to a file, buffer or string in HTML format. 

1307 

1308 Examples 

1309 -------- 

1310 >>> df = pd.DataFrame({'A': [1, 2], 'B': [3, 4]}) 

1311 >>> print(df.style.to_html()) # doctest: +SKIP 

1312 <style type="text/css"> 

1313 </style> 

1314 <table id="T_1e78e"> 

1315 <thead> 

1316 <tr> 

1317 <th class="blank level0" >&nbsp;</th> 

1318 <th id="T_1e78e_level0_col0" class="col_heading level0 col0" >A</th> 

1319 <th id="T_1e78e_level0_col1" class="col_heading level0 col1" >B</th> 

1320 </tr> 

1321 ... 

1322 """ 

1323 obj = self._copy(deepcopy=True) # manipulate table_styles on obj, not self 

1324 

1325 if table_uuid: 

1326 obj.set_uuid(table_uuid) 

1327 

1328 if table_attributes: 

1329 obj.set_table_attributes(table_attributes) 

1330 

1331 if sparse_index is None: 

1332 sparse_index = get_option("styler.sparse.index") 

1333 if sparse_columns is None: 

1334 sparse_columns = get_option("styler.sparse.columns") 

1335 

1336 if bold_headers: 

1337 obj.set_table_styles( 

1338 [{"selector": "th", "props": "font-weight: bold;"}], overwrite=False 

1339 ) 

1340 

1341 if caption is not None: 

1342 obj.set_caption(caption) 

1343 

1344 # Build HTML string.. 

1345 html = obj._render_html( 

1346 sparse_index=sparse_index, 

1347 sparse_columns=sparse_columns, 

1348 max_rows=max_rows, 

1349 max_cols=max_columns, 

1350 exclude_styles=exclude_styles, 

1351 encoding=encoding or get_option("styler.render.encoding"), 

1352 doctype_html=doctype_html, 

1353 **kwargs, 

1354 ) 

1355 

1356 return save_to_buffer( 

1357 html, buf=buf, encoding=(encoding if buf is not None else None) 

1358 ) 

1359 

1360 @overload 

1361 def to_string( 

1362 self, 

1363 buf: FilePath | WriteBuffer[str], 

1364 *, 

1365 encoding: str | None = ..., 

1366 sparse_index: bool | None = ..., 

1367 sparse_columns: bool | None = ..., 

1368 max_rows: int | None = ..., 

1369 max_columns: int | None = ..., 

1370 delimiter: str = ..., 

1371 ) -> None: 

1372 ... 

1373 

1374 @overload 

1375 def to_string( 

1376 self, 

1377 buf: None = ..., 

1378 *, 

1379 encoding: str | None = ..., 

1380 sparse_index: bool | None = ..., 

1381 sparse_columns: bool | None = ..., 

1382 max_rows: int | None = ..., 

1383 max_columns: int | None = ..., 

1384 delimiter: str = ..., 

1385 ) -> str: 

1386 ... 

1387 

1388 @Substitution(buf=buffering_args, encoding=encoding_args) 

1389 def to_string( 

1390 self, 

1391 buf: FilePath | WriteBuffer[str] | None = None, 

1392 *, 

1393 encoding: str | None = None, 

1394 sparse_index: bool | None = None, 

1395 sparse_columns: bool | None = None, 

1396 max_rows: int | None = None, 

1397 max_columns: int | None = None, 

1398 delimiter: str = " ", 

1399 ) -> str | None: 

1400 """ 

1401 Write Styler to a file, buffer or string in text format. 

1402 

1403 .. versionadded:: 1.5.0 

1404 

1405 Parameters 

1406 ---------- 

1407 %(buf)s 

1408 %(encoding)s 

1409 sparse_index : bool, optional 

1410 Whether to sparsify the display of a hierarchical index. Setting to False 

1411 will display each explicit level element in a hierarchical key for each row. 

1412 Defaults to ``pandas.options.styler.sparse.index`` value. 

1413 sparse_columns : bool, optional 

1414 Whether to sparsify the display of a hierarchical index. Setting to False 

1415 will display each explicit level element in a hierarchical key for each 

1416 column. Defaults to ``pandas.options.styler.sparse.columns`` value. 

1417 max_rows : int, optional 

1418 The maximum number of rows that will be rendered. Defaults to 

1419 ``pandas.options.styler.render.max_rows``, which is None. 

1420 max_columns : int, optional 

1421 The maximum number of columns that will be rendered. Defaults to 

1422 ``pandas.options.styler.render.max_columns``, which is None. 

1423 

1424 Rows and columns may be reduced if the number of total elements is 

1425 large. This value is set to ``pandas.options.styler.render.max_elements``, 

1426 which is 262144 (18 bit browser rendering). 

1427 delimiter : str, default single space 

1428 The separator between data elements. 

1429 

1430 Returns 

1431 ------- 

1432 str or None 

1433 If `buf` is None, returns the result as a string. Otherwise returns `None`. 

1434 

1435 Examples 

1436 -------- 

1437 >>> df = pd.DataFrame({'A': [1, 2], 'B': [3, 4]}) 

1438 >>> df.style.to_string() 

1439 ' A B\\n0 1 3\\n1 2 4\\n' 

1440 """ 

1441 obj = self._copy(deepcopy=True) 

1442 

1443 if sparse_index is None: 

1444 sparse_index = get_option("styler.sparse.index") 

1445 if sparse_columns is None: 

1446 sparse_columns = get_option("styler.sparse.columns") 

1447 

1448 text = obj._render_string( 

1449 sparse_columns=sparse_columns, 

1450 sparse_index=sparse_index, 

1451 max_rows=max_rows, 

1452 max_cols=max_columns, 

1453 delimiter=delimiter, 

1454 ) 

1455 return save_to_buffer( 

1456 text, buf=buf, encoding=(encoding if buf is not None else None) 

1457 ) 

1458 

1459 def set_td_classes(self, classes: DataFrame) -> Styler: 

1460 """ 

1461 Set the ``class`` attribute of ``<td>`` HTML elements. 

1462 

1463 Parameters 

1464 ---------- 

1465 classes : DataFrame 

1466 DataFrame containing strings that will be translated to CSS classes, 

1467 mapped by identical column and index key values that must exist on the 

1468 underlying Styler data. None, NaN values, and empty strings will 

1469 be ignored and not affect the rendered HTML. 

1470 

1471 Returns 

1472 ------- 

1473 Styler 

1474 

1475 See Also 

1476 -------- 

1477 Styler.set_table_styles: Set the table styles included within the ``<style>`` 

1478 HTML element. 

1479 Styler.set_table_attributes: Set the table attributes added to the ``<table>`` 

1480 HTML element. 

1481 

1482 Notes 

1483 ----- 

1484 Can be used in combination with ``Styler.set_table_styles`` to define an 

1485 internal CSS solution without reference to external CSS files. 

1486 

1487 Examples 

1488 -------- 

1489 >>> df = pd.DataFrame(data=[[1, 2, 3], [4, 5, 6]], columns=["A", "B", "C"]) 

1490 >>> classes = pd.DataFrame([ 

1491 ... ["min-val red", "", "blue"], 

1492 ... ["red", None, "blue max-val"] 

1493 ... ], index=df.index, columns=df.columns) 

1494 >>> df.style.set_td_classes(classes) # doctest: +SKIP 

1495 

1496 Using `MultiIndex` columns and a `classes` `DataFrame` as a subset of the 

1497 underlying, 

1498 

1499 >>> df = pd.DataFrame([[1,2],[3,4]], index=["a", "b"], 

1500 ... columns=[["level0", "level0"], ["level1a", "level1b"]]) 

1501 >>> classes = pd.DataFrame(["min-val"], index=["a"], 

1502 ... columns=[["level0"],["level1a"]]) 

1503 >>> df.style.set_td_classes(classes) # doctest: +SKIP 

1504 

1505 Form of the output with new additional css classes, 

1506 

1507 >>> from pandas.io.formats.style import Styler 

1508 >>> df = pd.DataFrame([[1]]) 

1509 >>> css = pd.DataFrame([["other-class"]]) 

1510 >>> s = Styler(df, uuid="_", cell_ids=False).set_td_classes(css) 

1511 >>> s.hide(axis=0).to_html() # doctest: +SKIP 

1512 '<style type="text/css"></style>' 

1513 '<table id="T__">' 

1514 ' <thead>' 

1515 ' <tr><th class="col_heading level0 col0" >0</th></tr>' 

1516 ' </thead>' 

1517 ' <tbody>' 

1518 ' <tr><td class="data row0 col0 other-class" >1</td></tr>' 

1519 ' </tbody>' 

1520 '</table>' 

1521 """ 

1522 if not classes.index.is_unique or not classes.columns.is_unique: 

1523 raise KeyError( 

1524 "Classes render only if `classes` has unique index and columns." 

1525 ) 

1526 classes = classes.reindex_like(self.data) 

1527 

1528 for r, row_tup in enumerate(classes.itertuples()): 

1529 for c, value in enumerate(row_tup[1:]): 

1530 if not (pd.isna(value) or value == ""): 

1531 self.cell_context[(r, c)] = str(value) 

1532 

1533 return self 

1534 

1535 def _update_ctx(self, attrs: DataFrame) -> None: 

1536 """ 

1537 Update the state of the ``Styler`` for data cells. 

1538 

1539 Collects a mapping of {index_label: [('<property>', '<value>'), ..]}. 

1540 

1541 Parameters 

1542 ---------- 

1543 attrs : DataFrame 

1544 should contain strings of '<property>: <value>;<prop2>: <val2>' 

1545 Whitespace shouldn't matter and the final trailing ';' shouldn't 

1546 matter. 

1547 """ 

1548 if not self.index.is_unique or not self.columns.is_unique: 

1549 raise KeyError( 

1550 "`Styler.apply` and `.map` are not compatible " 

1551 "with non-unique index or columns." 

1552 ) 

1553 

1554 for cn in attrs.columns: 

1555 j = self.columns.get_loc(cn) 

1556 ser = attrs[cn] 

1557 for rn, c in ser.items(): 

1558 if not c or pd.isna(c): 

1559 continue 

1560 css_list = maybe_convert_css_to_tuples(c) 

1561 i = self.index.get_loc(rn) 

1562 self.ctx[(i, j)].extend(css_list) 

1563 

1564 def _update_ctx_header(self, attrs: DataFrame, axis: AxisInt) -> None: 

1565 """ 

1566 Update the state of the ``Styler`` for header cells. 

1567 

1568 Collects a mapping of {index_label: [('<property>', '<value>'), ..]}. 

1569 

1570 Parameters 

1571 ---------- 

1572 attrs : Series 

1573 Should contain strings of '<property>: <value>;<prop2>: <val2>', and an 

1574 integer index. 

1575 Whitespace shouldn't matter and the final trailing ';' shouldn't 

1576 matter. 

1577 axis : int 

1578 Identifies whether the ctx object being updated is the index or columns 

1579 """ 

1580 for j in attrs.columns: 

1581 ser = attrs[j] 

1582 for i, c in ser.items(): 

1583 if not c: 

1584 continue 

1585 css_list = maybe_convert_css_to_tuples(c) 

1586 if axis == 0: 

1587 self.ctx_index[(i, j)].extend(css_list) 

1588 else: 

1589 self.ctx_columns[(j, i)].extend(css_list) 

1590 

1591 def _copy(self, deepcopy: bool = False) -> Styler: 

1592 """ 

1593 Copies a Styler, allowing for deepcopy or shallow copy 

1594 

1595 Copying a Styler aims to recreate a new Styler object which contains the same 

1596 data and styles as the original. 

1597 

1598 Data dependent attributes [copied and NOT exported]: 

1599 - formatting (._display_funcs) 

1600 - hidden index values or column values (.hidden_rows, .hidden_columns) 

1601 - tooltips 

1602 - cell_context (cell css classes) 

1603 - ctx (cell css styles) 

1604 - caption 

1605 - concatenated stylers 

1606 

1607 Non-data dependent attributes [copied and exported]: 

1608 - css 

1609 - hidden index state and hidden columns state (.hide_index_, .hide_columns_) 

1610 - table_attributes 

1611 - table_styles 

1612 - applied styles (_todo) 

1613 

1614 """ 

1615 # GH 40675, 52728 

1616 styler = type(self)( 

1617 self.data, # populates attributes 'data', 'columns', 'index' as shallow 

1618 ) 

1619 shallow = [ # simple string or boolean immutables 

1620 "hide_index_", 

1621 "hide_columns_", 

1622 "hide_column_names", 

1623 "hide_index_names", 

1624 "table_attributes", 

1625 "cell_ids", 

1626 "caption", 

1627 "uuid", 

1628 "uuid_len", 

1629 "template_latex", # also copy templates if these have been customised 

1630 "template_html_style", 

1631 "template_html_table", 

1632 "template_html", 

1633 ] 

1634 deep = [ # nested lists or dicts 

1635 "css", 

1636 "concatenated", 

1637 "_display_funcs", 

1638 "_display_funcs_index", 

1639 "_display_funcs_columns", 

1640 "hidden_rows", 

1641 "hidden_columns", 

1642 "ctx", 

1643 "ctx_index", 

1644 "ctx_columns", 

1645 "cell_context", 

1646 "_todo", 

1647 "table_styles", 

1648 "tooltips", 

1649 ] 

1650 

1651 for attr in shallow: 

1652 setattr(styler, attr, getattr(self, attr)) 

1653 

1654 for attr in deep: 

1655 val = getattr(self, attr) 

1656 setattr(styler, attr, copy.deepcopy(val) if deepcopy else val) 

1657 

1658 return styler 

1659 

1660 def __copy__(self) -> Styler: 

1661 return self._copy(deepcopy=False) 

1662 

1663 def __deepcopy__(self, memo) -> Styler: 

1664 return self._copy(deepcopy=True) 

1665 

1666 def clear(self) -> None: 

1667 """ 

1668 Reset the ``Styler``, removing any previously applied styles. 

1669 

1670 Returns None. 

1671 

1672 Examples 

1673 -------- 

1674 >>> df = pd.DataFrame({'A': [1, 2], 'B': [3, np.nan]}) 

1675 

1676 After any added style: 

1677 

1678 >>> df.style.highlight_null(color='yellow') # doctest: +SKIP 

1679 

1680 Remove it with: 

1681 

1682 >>> df.style.clear() # doctest: +SKIP 

1683 

1684 Please see: 

1685 `Table Visualization <../../user_guide/style.ipynb>`_ for more examples. 

1686 """ 

1687 # create default GH 40675 

1688 clean_copy = Styler(self.data, uuid=self.uuid) 

1689 clean_attrs = [a for a in clean_copy.__dict__ if not callable(a)] 

1690 self_attrs = [a for a in self.__dict__ if not callable(a)] # maybe more attrs 

1691 for attr in clean_attrs: 

1692 setattr(self, attr, getattr(clean_copy, attr)) 

1693 for attr in set(self_attrs).difference(clean_attrs): 

1694 delattr(self, attr) 

1695 

1696 def _apply( 

1697 self, 

1698 func: Callable, 

1699 axis: Axis | None = 0, 

1700 subset: Subset | None = None, 

1701 **kwargs, 

1702 ) -> Styler: 

1703 subset = slice(None) if subset is None else subset 

1704 subset = non_reducing_slice(subset) 

1705 data = self.data.loc[subset] 

1706 if data.empty: 

1707 result = DataFrame() 

1708 elif axis is None: 

1709 result = func(data, **kwargs) 

1710 if not isinstance(result, DataFrame): 

1711 if not isinstance(result, np.ndarray): 

1712 raise TypeError( 

1713 f"Function {repr(func)} must return a DataFrame or ndarray " 

1714 f"when passed to `Styler.apply` with axis=None" 

1715 ) 

1716 if data.shape != result.shape: 

1717 raise ValueError( 

1718 f"Function {repr(func)} returned ndarray with wrong shape.\n" 

1719 f"Result has shape: {result.shape}\n" 

1720 f"Expected shape: {data.shape}" 

1721 ) 

1722 result = DataFrame(result, index=data.index, columns=data.columns) 

1723 else: 

1724 axis = self.data._get_axis_number(axis) 

1725 if axis == 0: 

1726 result = data.apply(func, axis=0, **kwargs) 

1727 else: 

1728 result = data.T.apply(func, axis=0, **kwargs).T # see GH 42005 

1729 

1730 if isinstance(result, Series): 

1731 raise ValueError( 

1732 f"Function {repr(func)} resulted in the apply method collapsing to a " 

1733 f"Series.\nUsually, this is the result of the function returning a " 

1734 f"single value, instead of list-like." 

1735 ) 

1736 msg = ( 

1737 f"Function {repr(func)} created invalid {{0}} labels.\nUsually, this is " 

1738 f"the result of the function returning a " 

1739 f"{'Series' if axis is not None else 'DataFrame'} which contains invalid " 

1740 f"labels, or returning an incorrectly shaped, list-like object which " 

1741 f"cannot be mapped to labels, possibly due to applying the function along " 

1742 f"the wrong axis.\n" 

1743 f"Result {{0}} has shape: {{1}}\n" 

1744 f"Expected {{0}} shape: {{2}}" 

1745 ) 

1746 if not all(result.index.isin(data.index)): 

1747 raise ValueError(msg.format("index", result.index.shape, data.index.shape)) 

1748 if not all(result.columns.isin(data.columns)): 

1749 raise ValueError( 

1750 msg.format("columns", result.columns.shape, data.columns.shape) 

1751 ) 

1752 self._update_ctx(result) 

1753 return self 

1754 

1755 @Substitution(subset=subset_args) 

1756 def apply( 

1757 self, 

1758 func: Callable, 

1759 axis: Axis | None = 0, 

1760 subset: Subset | None = None, 

1761 **kwargs, 

1762 ) -> Styler: 

1763 """ 

1764 Apply a CSS-styling function column-wise, row-wise, or table-wise. 

1765 

1766 Updates the HTML representation with the result. 

1767 

1768 Parameters 

1769 ---------- 

1770 func : function 

1771 ``func`` should take a Series if ``axis`` in [0,1] and return a list-like 

1772 object of same length, or a Series, not necessarily of same length, with 

1773 valid index labels considering ``subset``. 

1774 ``func`` should take a DataFrame if ``axis`` is ``None`` and return either 

1775 an ndarray with the same shape or a DataFrame, not necessarily of the same 

1776 shape, with valid index and columns labels considering ``subset``. 

1777 

1778 .. versionchanged:: 1.3.0 

1779 

1780 .. versionchanged:: 1.4.0 

1781 

1782 axis : {0 or 'index', 1 or 'columns', None}, default 0 

1783 Apply to each column (``axis=0`` or ``'index'``), to each row 

1784 (``axis=1`` or ``'columns'``), or to the entire DataFrame at once 

1785 with ``axis=None``. 

1786 %(subset)s 

1787 **kwargs : dict 

1788 Pass along to ``func``. 

1789 

1790 Returns 

1791 ------- 

1792 Styler 

1793 

1794 See Also 

1795 -------- 

1796 Styler.map_index: Apply a CSS-styling function to headers elementwise. 

1797 Styler.apply_index: Apply a CSS-styling function to headers level-wise. 

1798 Styler.map: Apply a CSS-styling function elementwise. 

1799 

1800 Notes 

1801 ----- 

1802 The elements of the output of ``func`` should be CSS styles as strings, in the 

1803 format 'attribute: value; attribute2: value2; ...' or, 

1804 if nothing is to be applied to that element, an empty string or ``None``. 

1805 

1806 This is similar to ``DataFrame.apply``, except that ``axis=None`` 

1807 applies the function to the entire DataFrame at once, 

1808 rather than column-wise or row-wise. 

1809 

1810 Examples 

1811 -------- 

1812 >>> def highlight_max(x, color): 

1813 ... return np.where(x == np.nanmax(x.to_numpy()), f"color: {color};", None) 

1814 >>> df = pd.DataFrame(np.random.randn(5, 2), columns=["A", "B"]) 

1815 >>> df.style.apply(highlight_max, color='red') # doctest: +SKIP 

1816 >>> df.style.apply(highlight_max, color='blue', axis=1) # doctest: +SKIP 

1817 >>> df.style.apply(highlight_max, color='green', axis=None) # doctest: +SKIP 

1818 

1819 Using ``subset`` to restrict application to a single column or multiple columns 

1820 

1821 >>> df.style.apply(highlight_max, color='red', subset="A") 

1822 ... # doctest: +SKIP 

1823 >>> df.style.apply(highlight_max, color='red', subset=["A", "B"]) 

1824 ... # doctest: +SKIP 

1825 

1826 Using a 2d input to ``subset`` to select rows in addition to columns 

1827 

1828 >>> df.style.apply(highlight_max, color='red', subset=([0, 1, 2], slice(None))) 

1829 ... # doctest: +SKIP 

1830 >>> df.style.apply(highlight_max, color='red', subset=(slice(0, 5, 2), "A")) 

1831 ... # doctest: +SKIP 

1832 

1833 Using a function which returns a Series / DataFrame of unequal length but 

1834 containing valid index labels 

1835 

1836 >>> df = pd.DataFrame([[1, 2], [3, 4], [4, 6]], index=["A1", "A2", "Total"]) 

1837 >>> total_style = pd.Series("font-weight: bold;", index=["Total"]) 

1838 >>> df.style.apply(lambda s: total_style) # doctest: +SKIP 

1839 

1840 See `Table Visualization <../../user_guide/style.ipynb>`_ user guide for 

1841 more details. 

1842 """ 

1843 self._todo.append( 

1844 (lambda instance: getattr(instance, "_apply"), (func, axis, subset), kwargs) 

1845 ) 

1846 return self 

1847 

1848 def _apply_index( 

1849 self, 

1850 func: Callable, 

1851 axis: Axis = 0, 

1852 level: Level | list[Level] | None = None, 

1853 method: str = "apply", 

1854 **kwargs, 

1855 ) -> Styler: 

1856 axis = self.data._get_axis_number(axis) 

1857 obj = self.index if axis == 0 else self.columns 

1858 

1859 levels_ = refactor_levels(level, obj) 

1860 data = DataFrame(obj.to_list()).loc[:, levels_] 

1861 

1862 if method == "apply": 

1863 result = data.apply(func, axis=0, **kwargs) 

1864 elif method == "map": 

1865 result = data.map(func, **kwargs) 

1866 

1867 self._update_ctx_header(result, axis) 

1868 return self 

1869 

1870 @doc( 

1871 this="apply", 

1872 wise="level-wise", 

1873 alt="map", 

1874 altwise="elementwise", 

1875 func="take a Series and return a string array of the same length", 

1876 input_note="the index as a Series, if an Index, or a level of a MultiIndex", 

1877 output_note="an identically sized array of CSS styles as strings", 

1878 var="s", 

1879 ret='np.where(s == "B", "background-color: yellow;", "")', 

1880 ret2='["background-color: yellow;" if "x" in v else "" for v in s]', 

1881 ) 

1882 def apply_index( 

1883 self, 

1884 func: Callable, 

1885 axis: AxisInt | str = 0, 

1886 level: Level | list[Level] | None = None, 

1887 **kwargs, 

1888 ) -> Styler: 

1889 """ 

1890 Apply a CSS-styling function to the index or column headers, {wise}. 

1891 

1892 Updates the HTML representation with the result. 

1893 

1894 .. versionadded:: 1.4.0 

1895 

1896 .. versionadded:: 2.1.0 

1897 Styler.applymap_index was deprecated and renamed to Styler.map_index. 

1898 

1899 Parameters 

1900 ---------- 

1901 func : function 

1902 ``func`` should {func}. 

1903 axis : {{0, 1, "index", "columns"}} 

1904 The headers over which to apply the function. 

1905 level : int, str, list, optional 

1906 If index is MultiIndex the level(s) over which to apply the function. 

1907 **kwargs : dict 

1908 Pass along to ``func``. 

1909 

1910 Returns 

1911 ------- 

1912 Styler 

1913 

1914 See Also 

1915 -------- 

1916 Styler.{alt}_index: Apply a CSS-styling function to headers {altwise}. 

1917 Styler.apply: Apply a CSS-styling function column-wise, row-wise, or table-wise. 

1918 Styler.map: Apply a CSS-styling function elementwise. 

1919 

1920 Notes 

1921 ----- 

1922 Each input to ``func`` will be {input_note}. The output of ``func`` should be 

1923 {output_note}, in the format 'attribute: value; attribute2: value2; ...' 

1924 or, if nothing is to be applied to that element, an empty string or ``None``. 

1925 

1926 Examples 

1927 -------- 

1928 Basic usage to conditionally highlight values in the index. 

1929 

1930 >>> df = pd.DataFrame([[1,2], [3,4]], index=["A", "B"]) 

1931 >>> def color_b(s): 

1932 ... return {ret} 

1933 >>> df.style.{this}_index(color_b) # doctest: +SKIP 

1934 

1935 .. figure:: ../../_static/style/appmaphead1.png 

1936 

1937 Selectively applying to specific levels of MultiIndex columns. 

1938 

1939 >>> midx = pd.MultiIndex.from_product([['ix', 'jy'], [0, 1], ['x3', 'z4']]) 

1940 >>> df = pd.DataFrame([np.arange(8)], columns=midx) 

1941 >>> def highlight_x({var}): 

1942 ... return {ret2} 

1943 >>> df.style.{this}_index(highlight_x, axis="columns", level=[0, 2]) 

1944 ... # doctest: +SKIP 

1945 

1946 .. figure:: ../../_static/style/appmaphead2.png 

1947 """ 

1948 self._todo.append( 

1949 ( 

1950 lambda instance: getattr(instance, "_apply_index"), 

1951 (func, axis, level, "apply"), 

1952 kwargs, 

1953 ) 

1954 ) 

1955 return self 

1956 

1957 @doc( 

1958 apply_index, 

1959 this="map", 

1960 wise="elementwise", 

1961 alt="apply", 

1962 altwise="level-wise", 

1963 func="take a scalar and return a string", 

1964 input_note="an index value, if an Index, or a level value of a MultiIndex", 

1965 output_note="CSS styles as a string", 

1966 var="v", 

1967 ret='"background-color: yellow;" if v == "B" else None', 

1968 ret2='"background-color: yellow;" if "x" in v else None', 

1969 ) 

1970 def map_index( 

1971 self, 

1972 func: Callable, 

1973 axis: AxisInt | str = 0, 

1974 level: Level | list[Level] | None = None, 

1975 **kwargs, 

1976 ) -> Styler: 

1977 self._todo.append( 

1978 ( 

1979 lambda instance: getattr(instance, "_apply_index"), 

1980 (func, axis, level, "map"), 

1981 kwargs, 

1982 ) 

1983 ) 

1984 return self 

1985 

1986 def applymap_index( 

1987 self, 

1988 func: Callable, 

1989 axis: AxisInt | str = 0, 

1990 level: Level | list[Level] | None = None, 

1991 **kwargs, 

1992 ) -> Styler: 

1993 """ 

1994 Apply a CSS-styling function to the index or column headers, elementwise. 

1995 

1996 .. deprecated:: 2.1.0 

1997 

1998 Styler.applymap_index has been deprecated. Use Styler.map_index instead. 

1999 

2000 Parameters 

2001 ---------- 

2002 func : function 

2003 ``func`` should take a scalar and return a string. 

2004 axis : {{0, 1, "index", "columns"}} 

2005 The headers over which to apply the function. 

2006 level : int, str, list, optional 

2007 If index is MultiIndex the level(s) over which to apply the function. 

2008 **kwargs : dict 

2009 Pass along to ``func``. 

2010 

2011 Returns 

2012 ------- 

2013 Styler 

2014 """ 

2015 warnings.warn( 

2016 "Styler.applymap_index has been deprecated. Use Styler.map_index instead.", 

2017 FutureWarning, 

2018 stacklevel=find_stack_level(), 

2019 ) 

2020 return self.map_index(func, axis, level, **kwargs) 

2021 

2022 def _map(self, func: Callable, subset: Subset | None = None, **kwargs) -> Styler: 

2023 func = partial(func, **kwargs) # map doesn't take kwargs? 

2024 if subset is None: 

2025 subset = IndexSlice[:] 

2026 subset = non_reducing_slice(subset) 

2027 result = self.data.loc[subset].map(func) 

2028 self._update_ctx(result) 

2029 return self 

2030 

2031 @Substitution(subset=subset_args) 

2032 def map(self, func: Callable, subset: Subset | None = None, **kwargs) -> Styler: 

2033 """ 

2034 Apply a CSS-styling function elementwise. 

2035 

2036 Updates the HTML representation with the result. 

2037 

2038 Parameters 

2039 ---------- 

2040 func : function 

2041 ``func`` should take a scalar and return a string. 

2042 %(subset)s 

2043 **kwargs : dict 

2044 Pass along to ``func``. 

2045 

2046 Returns 

2047 ------- 

2048 Styler 

2049 

2050 See Also 

2051 -------- 

2052 Styler.map_index: Apply a CSS-styling function to headers elementwise. 

2053 Styler.apply_index: Apply a CSS-styling function to headers level-wise. 

2054 Styler.apply: Apply a CSS-styling function column-wise, row-wise, or table-wise. 

2055 

2056 Notes 

2057 ----- 

2058 The elements of the output of ``func`` should be CSS styles as strings, in the 

2059 format 'attribute: value; attribute2: value2; ...' or, 

2060 if nothing is to be applied to that element, an empty string or ``None``. 

2061 

2062 Examples 

2063 -------- 

2064 >>> def color_negative(v, color): 

2065 ... return f"color: {color};" if v < 0 else None 

2066 >>> df = pd.DataFrame(np.random.randn(5, 2), columns=["A", "B"]) 

2067 >>> df.style.map(color_negative, color='red') # doctest: +SKIP 

2068 

2069 Using ``subset`` to restrict application to a single column or multiple columns 

2070 

2071 >>> df.style.map(color_negative, color='red', subset="A") 

2072 ... # doctest: +SKIP 

2073 >>> df.style.map(color_negative, color='red', subset=["A", "B"]) 

2074 ... # doctest: +SKIP 

2075 

2076 Using a 2d input to ``subset`` to select rows in addition to columns 

2077 

2078 >>> df.style.map(color_negative, color='red', 

2079 ... subset=([0,1,2], slice(None))) # doctest: +SKIP 

2080 >>> df.style.map(color_negative, color='red', subset=(slice(0,5,2), "A")) 

2081 ... # doctest: +SKIP 

2082 

2083 See `Table Visualization <../../user_guide/style.ipynb>`_ user guide for 

2084 more details. 

2085 """ 

2086 self._todo.append( 

2087 (lambda instance: getattr(instance, "_map"), (func, subset), kwargs) 

2088 ) 

2089 return self 

2090 

2091 @Substitution(subset=subset_args) 

2092 def applymap( 

2093 self, func: Callable, subset: Subset | None = None, **kwargs 

2094 ) -> Styler: 

2095 """ 

2096 Apply a CSS-styling function elementwise. 

2097 

2098 .. deprecated:: 2.1.0 

2099 

2100 Styler.applymap has been deprecated. Use Styler.map instead. 

2101 

2102 Parameters 

2103 ---------- 

2104 func : function 

2105 ``func`` should take a scalar and return a string. 

2106 %(subset)s 

2107 **kwargs : dict 

2108 Pass along to ``func``. 

2109 

2110 Returns 

2111 ------- 

2112 Styler 

2113 """ 

2114 warnings.warn( 

2115 "Styler.applymap has been deprecated. Use Styler.map instead.", 

2116 FutureWarning, 

2117 stacklevel=find_stack_level(), 

2118 ) 

2119 return self.map(func, subset, **kwargs) 

2120 

2121 def set_table_attributes(self, attributes: str) -> Styler: 

2122 """ 

2123 Set the table attributes added to the ``<table>`` HTML element. 

2124 

2125 These are items in addition to automatic (by default) ``id`` attribute. 

2126 

2127 Parameters 

2128 ---------- 

2129 attributes : str 

2130 

2131 Returns 

2132 ------- 

2133 Styler 

2134 

2135 See Also 

2136 -------- 

2137 Styler.set_table_styles: Set the table styles included within the ``<style>`` 

2138 HTML element. 

2139 Styler.set_td_classes: Set the DataFrame of strings added to the ``class`` 

2140 attribute of ``<td>`` HTML elements. 

2141 

2142 Examples 

2143 -------- 

2144 >>> df = pd.DataFrame(np.random.randn(10, 4)) 

2145 >>> df.style.set_table_attributes('class="pure-table"') # doctest: +SKIP 

2146 # ... <table class="pure-table"> ... 

2147 """ 

2148 self.table_attributes = attributes 

2149 return self 

2150 

2151 def export(self) -> dict[str, Any]: 

2152 """ 

2153 Export the styles applied to the current Styler. 

2154 

2155 Can be applied to a second Styler with ``Styler.use``. 

2156 

2157 Returns 

2158 ------- 

2159 dict 

2160 

2161 See Also 

2162 -------- 

2163 Styler.use: Set the styles on the current Styler. 

2164 Styler.copy: Create a copy of the current Styler. 

2165 

2166 Notes 

2167 ----- 

2168 This method is designed to copy non-data dependent attributes of 

2169 one Styler to another. It differs from ``Styler.copy`` where data and 

2170 data dependent attributes are also copied. 

2171 

2172 The following items are exported since they are not generally data dependent: 

2173 

2174 - Styling functions added by the ``apply`` and ``map`` 

2175 - Whether axes and names are hidden from the display, if unambiguous. 

2176 - Table attributes 

2177 - Table styles 

2178 

2179 The following attributes are considered data dependent and therefore not 

2180 exported: 

2181 

2182 - Caption 

2183 - UUID 

2184 - Tooltips 

2185 - Any hidden rows or columns identified by Index labels 

2186 - Any formatting applied using ``Styler.format`` 

2187 - Any CSS classes added using ``Styler.set_td_classes`` 

2188 

2189 Examples 

2190 -------- 

2191 

2192 >>> styler = pd.DataFrame([[1, 2], [3, 4]]).style 

2193 >>> styler2 = pd.DataFrame([[9, 9, 9]]).style 

2194 >>> styler.hide(axis=0).highlight_max(axis=1) # doctest: +SKIP 

2195 >>> export = styler.export() 

2196 >>> styler2.use(export) # doctest: +SKIP 

2197 """ 

2198 return { 

2199 "apply": copy.copy(self._todo), 

2200 "table_attributes": self.table_attributes, 

2201 "table_styles": copy.copy(self.table_styles), 

2202 "hide_index": all(self.hide_index_), 

2203 "hide_columns": all(self.hide_columns_), 

2204 "hide_index_names": self.hide_index_names, 

2205 "hide_column_names": self.hide_column_names, 

2206 "css": copy.copy(self.css), 

2207 } 

2208 

2209 def use(self, styles: dict[str, Any]) -> Styler: 

2210 """ 

2211 Set the styles on the current Styler. 

2212 

2213 Possibly uses styles from ``Styler.export``. 

2214 

2215 Parameters 

2216 ---------- 

2217 styles : dict(str, Any) 

2218 List of attributes to add to Styler. Dict keys should contain only: 

2219 - "apply": list of styler functions, typically added with ``apply`` or 

2220 ``map``. 

2221 - "table_attributes": HTML attributes, typically added with 

2222 ``set_table_attributes``. 

2223 - "table_styles": CSS selectors and properties, typically added with 

2224 ``set_table_styles``. 

2225 - "hide_index": whether the index is hidden, typically added with 

2226 ``hide_index``, or a boolean list for hidden levels. 

2227 - "hide_columns": whether column headers are hidden, typically added with 

2228 ``hide_columns``, or a boolean list for hidden levels. 

2229 - "hide_index_names": whether index names are hidden. 

2230 - "hide_column_names": whether column header names are hidden. 

2231 - "css": the css class names used. 

2232 

2233 Returns 

2234 ------- 

2235 Styler 

2236 

2237 See Also 

2238 -------- 

2239 Styler.export : Export the non data dependent attributes to the current Styler. 

2240 

2241 Examples 

2242 -------- 

2243 

2244 >>> styler = pd.DataFrame([[1, 2], [3, 4]]).style 

2245 >>> styler2 = pd.DataFrame([[9, 9, 9]]).style 

2246 >>> styler.hide(axis=0).highlight_max(axis=1) # doctest: +SKIP 

2247 >>> export = styler.export() 

2248 >>> styler2.use(export) # doctest: +SKIP 

2249 """ 

2250 self._todo.extend(styles.get("apply", [])) 

2251 table_attributes: str = self.table_attributes or "" 

2252 obj_table_atts: str = ( 

2253 "" 

2254 if styles.get("table_attributes") is None 

2255 else str(styles.get("table_attributes")) 

2256 ) 

2257 self.set_table_attributes((table_attributes + " " + obj_table_atts).strip()) 

2258 if styles.get("table_styles"): 

2259 self.set_table_styles(styles.get("table_styles"), overwrite=False) 

2260 

2261 for obj in ["index", "columns"]: 

2262 hide_obj = styles.get("hide_" + obj) 

2263 if hide_obj is not None: 

2264 if isinstance(hide_obj, bool): 

2265 n = getattr(self, obj).nlevels 

2266 setattr(self, "hide_" + obj + "_", [hide_obj] * n) 

2267 else: 

2268 setattr(self, "hide_" + obj + "_", hide_obj) 

2269 

2270 self.hide_index_names = styles.get("hide_index_names", False) 

2271 self.hide_column_names = styles.get("hide_column_names", False) 

2272 if styles.get("css"): 

2273 self.css = styles.get("css") # type: ignore[assignment] 

2274 return self 

2275 

2276 def set_uuid(self, uuid: str) -> Styler: 

2277 """ 

2278 Set the uuid applied to ``id`` attributes of HTML elements. 

2279 

2280 Parameters 

2281 ---------- 

2282 uuid : str 

2283 

2284 Returns 

2285 ------- 

2286 Styler 

2287 

2288 Notes 

2289 ----- 

2290 Almost all HTML elements within the table, and including the ``<table>`` element 

2291 are assigned ``id`` attributes. The format is ``T_uuid_<extra>`` where 

2292 ``<extra>`` is typically a more specific identifier, such as ``row1_col2``. 

2293 

2294 Examples 

2295 -------- 

2296 >>> df = pd.DataFrame([[1, 2], [3, 4]], index=['A', 'B'], columns=['c1', 'c2']) 

2297 

2298 You can get the `id` attributes with the following: 

2299 

2300 >>> print((df).style.to_html()) # doctest: +SKIP 

2301 

2302 To add a title to column `c1`, its `id` is T_20a7d_level0_col0: 

2303 

2304 >>> df.style.set_uuid("T_20a7d_level0_col0") 

2305 ... .set_caption("Test") # doctest: +SKIP 

2306 

2307 Please see: 

2308 `Table visualization <../../user_guide/style.ipynb>`_ for more examples. 

2309 """ 

2310 self.uuid = uuid 

2311 return self 

2312 

2313 def set_caption(self, caption: str | tuple | list) -> Styler: 

2314 """ 

2315 Set the text added to a ``<caption>`` HTML element. 

2316 

2317 Parameters 

2318 ---------- 

2319 caption : str, tuple, list 

2320 For HTML output either the string input is used or the first element of the 

2321 tuple. For LaTeX the string input provides a caption and the additional 

2322 tuple input allows for full captions and short captions, in that order. 

2323 

2324 Returns 

2325 ------- 

2326 Styler 

2327 

2328 Examples 

2329 -------- 

2330 >>> df = pd.DataFrame({'A': [1, 2], 'B': [3, 4]}) 

2331 >>> df.style.set_caption("test") # doctest: +SKIP 

2332 

2333 Please see: 

2334 `Table Visualization <../../user_guide/style.ipynb>`_ for more examples. 

2335 """ 

2336 msg = "`caption` must be either a string or 2-tuple of strings." 

2337 if isinstance(caption, (list, tuple)): 

2338 if ( 

2339 len(caption) != 2 

2340 or not isinstance(caption[0], str) 

2341 or not isinstance(caption[1], str) 

2342 ): 

2343 raise ValueError(msg) 

2344 elif not isinstance(caption, str): 

2345 raise ValueError(msg) 

2346 self.caption = caption 

2347 return self 

2348 

2349 def set_sticky( 

2350 self, 

2351 axis: Axis = 0, 

2352 pixel_size: int | None = None, 

2353 levels: Level | list[Level] | None = None, 

2354 ) -> Styler: 

2355 """ 

2356 Add CSS to permanently display the index or column headers in a scrolling frame. 

2357 

2358 Parameters 

2359 ---------- 

2360 axis : {0 or 'index', 1 or 'columns'}, default 0 

2361 Whether to make the index or column headers sticky. 

2362 pixel_size : int, optional 

2363 Required to configure the width of index cells or the height of column 

2364 header cells when sticking a MultiIndex (or with a named Index). 

2365 Defaults to 75 and 25 respectively. 

2366 levels : int, str, list, optional 

2367 If ``axis`` is a MultiIndex the specific levels to stick. If ``None`` will 

2368 stick all levels. 

2369 

2370 Returns 

2371 ------- 

2372 Styler 

2373 

2374 Notes 

2375 ----- 

2376 This method uses the CSS 'position: sticky;' property to display. It is 

2377 designed to work with visible axes, therefore both: 

2378 

2379 - `styler.set_sticky(axis="index").hide(axis="index")` 

2380 - `styler.set_sticky(axis="columns").hide(axis="columns")` 

2381 

2382 may produce strange behaviour due to CSS controls with missing elements. 

2383 

2384 Examples 

2385 -------- 

2386 >>> df = pd.DataFrame({'A': [1, 2], 'B': [3, 4]}) 

2387 >>> df.style.set_sticky(axis="index") # doctest: +SKIP 

2388 

2389 Please see: 

2390 `Table Visualization <../../user_guide/style.ipynb>`_ for more examples. 

2391 """ 

2392 axis = self.data._get_axis_number(axis) 

2393 obj = self.data.index if axis == 0 else self.data.columns 

2394 pixel_size = (75 if axis == 0 else 25) if not pixel_size else pixel_size 

2395 

2396 props = "position:sticky; background-color:inherit;" 

2397 if not isinstance(obj, pd.MultiIndex): 

2398 # handling MultiIndexes requires different CSS 

2399 

2400 if axis == 1: 

2401 # stick the first <tr> of <head> and, if index names, the second <tr> 

2402 # if self._hide_columns then no <thead><tr> here will exist: no conflict 

2403 styles: CSSStyles = [ 

2404 { 

2405 "selector": "thead tr:nth-child(1) th", 

2406 "props": props + "top:0px; z-index:2;", 

2407 } 

2408 ] 

2409 if self.index.names[0] is not None: 

2410 styles[0]["props"] = ( 

2411 props + f"top:0px; z-index:2; height:{pixel_size}px;" 

2412 ) 

2413 styles.append( 

2414 { 

2415 "selector": "thead tr:nth-child(2) th", 

2416 "props": props 

2417 + f"top:{pixel_size}px; z-index:2; height:{pixel_size}px; ", 

2418 } 

2419 ) 

2420 else: 

2421 # stick the first <th> of each <tr> in both <thead> and <tbody> 

2422 # if self._hide_index then no <th> will exist in <tbody>: no conflict 

2423 # but <th> will exist in <thead>: conflict with initial element 

2424 styles = [ 

2425 { 

2426 "selector": "thead tr th:nth-child(1)", 

2427 "props": props + "left:0px; z-index:3 !important;", 

2428 }, 

2429 { 

2430 "selector": "tbody tr th:nth-child(1)", 

2431 "props": props + "left:0px; z-index:1;", 

2432 }, 

2433 ] 

2434 

2435 else: 

2436 # handle the MultiIndex case 

2437 range_idx = list(range(obj.nlevels)) 

2438 levels_: list[int] = refactor_levels(levels, obj) if levels else range_idx 

2439 levels_ = sorted(levels_) 

2440 

2441 if axis == 1: 

2442 styles = [] 

2443 for i, level in enumerate(levels_): 

2444 styles.append( 

2445 { 

2446 "selector": f"thead tr:nth-child({level+1}) th", 

2447 "props": props 

2448 + ( 

2449 f"top:{i * pixel_size}px; height:{pixel_size}px; " 

2450 "z-index:2;" 

2451 ), 

2452 } 

2453 ) 

2454 if not all(name is None for name in self.index.names): 

2455 styles.append( 

2456 { 

2457 "selector": f"thead tr:nth-child({obj.nlevels+1}) th", 

2458 "props": props 

2459 + ( 

2460 f"top:{(len(levels_)) * pixel_size}px; " 

2461 f"height:{pixel_size}px; z-index:2;" 

2462 ), 

2463 } 

2464 ) 

2465 

2466 else: 

2467 styles = [] 

2468 for i, level in enumerate(levels_): 

2469 props_ = props + ( 

2470 f"left:{i * pixel_size}px; " 

2471 f"min-width:{pixel_size}px; " 

2472 f"max-width:{pixel_size}px; " 

2473 ) 

2474 styles.extend( 

2475 [ 

2476 { 

2477 "selector": f"thead tr th:nth-child({level+1})", 

2478 "props": props_ + "z-index:3 !important;", 

2479 }, 

2480 { 

2481 "selector": f"tbody tr th.level{level}", 

2482 "props": props_ + "z-index:1;", 

2483 }, 

2484 ] 

2485 ) 

2486 

2487 return self.set_table_styles(styles, overwrite=False) 

2488 

2489 def set_table_styles( 

2490 self, 

2491 table_styles: dict[Any, CSSStyles] | CSSStyles | None = None, 

2492 axis: AxisInt = 0, 

2493 overwrite: bool = True, 

2494 css_class_names: dict[str, str] | None = None, 

2495 ) -> Styler: 

2496 """ 

2497 Set the table styles included within the ``<style>`` HTML element. 

2498 

2499 This function can be used to style the entire table, columns, rows or 

2500 specific HTML selectors. 

2501 

2502 Parameters 

2503 ---------- 

2504 table_styles : list or dict 

2505 If supplying a list, each individual table_style should be a 

2506 dictionary with ``selector`` and ``props`` keys. ``selector`` 

2507 should be a CSS selector that the style will be applied to 

2508 (automatically prefixed by the table's UUID) and ``props`` 

2509 should be a list of tuples with ``(attribute, value)``. 

2510 If supplying a dict, the dict keys should correspond to 

2511 column names or index values, depending upon the specified 

2512 `axis` argument. These will be mapped to row or col CSS 

2513 selectors. MultiIndex values as dict keys should be 

2514 in their respective tuple form. The dict values should be 

2515 a list as specified in the form with CSS selectors and 

2516 props that will be applied to the specified row or column. 

2517 axis : {0 or 'index', 1 or 'columns', None}, default 0 

2518 Apply to each column (``axis=0`` or ``'index'``), to each row 

2519 (``axis=1`` or ``'columns'``). Only used if `table_styles` is 

2520 dict. 

2521 overwrite : bool, default True 

2522 Styles are replaced if `True`, or extended if `False`. CSS 

2523 rules are preserved so most recent styles set will dominate 

2524 if selectors intersect. 

2525 css_class_names : dict, optional 

2526 A dict of strings used to replace the default CSS classes described below. 

2527 

2528 .. versionadded:: 1.4.0 

2529 

2530 Returns 

2531 ------- 

2532 Styler 

2533 

2534 See Also 

2535 -------- 

2536 Styler.set_td_classes: Set the DataFrame of strings added to the ``class`` 

2537 attribute of ``<td>`` HTML elements. 

2538 Styler.set_table_attributes: Set the table attributes added to the ``<table>`` 

2539 HTML element. 

2540 

2541 Notes 

2542 ----- 

2543 The default CSS classes dict, whose values can be replaced is as follows: 

2544 

2545 .. code-block:: python 

2546 

2547 css_class_names = {"row_heading": "row_heading", 

2548 "col_heading": "col_heading", 

2549 "index_name": "index_name", 

2550 "col": "col", 

2551 "row": "row", 

2552 "col_trim": "col_trim", 

2553 "row_trim": "row_trim", 

2554 "level": "level", 

2555 "data": "data", 

2556 "blank": "blank", 

2557 "foot": "foot"} 

2558 

2559 Examples 

2560 -------- 

2561 >>> df = pd.DataFrame(np.random.randn(10, 4), 

2562 ... columns=['A', 'B', 'C', 'D']) 

2563 >>> df.style.set_table_styles( 

2564 ... [{'selector': 'tr:hover', 

2565 ... 'props': [('background-color', 'yellow')]}] 

2566 ... ) # doctest: +SKIP 

2567 

2568 Or with CSS strings 

2569 

2570 >>> df.style.set_table_styles( 

2571 ... [{'selector': 'tr:hover', 

2572 ... 'props': 'background-color: yellow; font-size: 1em;'}] 

2573 ... ) # doctest: +SKIP 

2574 

2575 Adding column styling by name 

2576 

2577 >>> df.style.set_table_styles({ 

2578 ... 'A': [{'selector': '', 

2579 ... 'props': [('color', 'red')]}], 

2580 ... 'B': [{'selector': 'td', 

2581 ... 'props': 'color: blue;'}] 

2582 ... }, overwrite=False) # doctest: +SKIP 

2583 

2584 Adding row styling 

2585 

2586 >>> df.style.set_table_styles({ 

2587 ... 0: [{'selector': 'td:hover', 

2588 ... 'props': [('font-size', '25px')]}] 

2589 ... }, axis=1, overwrite=False) # doctest: +SKIP 

2590 

2591 See `Table Visualization <../../user_guide/style.ipynb>`_ user guide for 

2592 more details. 

2593 """ 

2594 if css_class_names is not None: 

2595 self.css = {**self.css, **css_class_names} 

2596 

2597 if table_styles is None: 

2598 return self 

2599 elif isinstance(table_styles, dict): 

2600 axis = self.data._get_axis_number(axis) 

2601 obj = self.data.index if axis == 1 else self.data.columns 

2602 idf = f".{self.css['row']}" if axis == 1 else f".{self.css['col']}" 

2603 

2604 table_styles = [ 

2605 { 

2606 "selector": str(s["selector"]) + idf + str(idx), 

2607 "props": maybe_convert_css_to_tuples(s["props"]), 

2608 } 

2609 for key, styles in table_styles.items() 

2610 for idx in obj.get_indexer_for([key]) 

2611 for s in format_table_styles(styles) 

2612 ] 

2613 else: 

2614 table_styles = [ 

2615 { 

2616 "selector": s["selector"], 

2617 "props": maybe_convert_css_to_tuples(s["props"]), 

2618 } 

2619 for s in table_styles 

2620 ] 

2621 

2622 if not overwrite and self.table_styles is not None: 

2623 self.table_styles.extend(table_styles) 

2624 else: 

2625 self.table_styles = table_styles 

2626 return self 

2627 

2628 def hide( 

2629 self, 

2630 subset: Subset | None = None, 

2631 axis: Axis = 0, 

2632 level: Level | list[Level] | None = None, 

2633 names: bool = False, 

2634 ) -> Styler: 

2635 """ 

2636 Hide the entire index / column headers, or specific rows / columns from display. 

2637 

2638 .. versionadded:: 1.4.0 

2639 

2640 Parameters 

2641 ---------- 

2642 subset : label, array-like, IndexSlice, optional 

2643 A valid 1d input or single key along the axis within 

2644 `DataFrame.loc[<subset>, :]` or `DataFrame.loc[:, <subset>]` depending 

2645 upon ``axis``, to limit ``data`` to select hidden rows / columns. 

2646 axis : {"index", 0, "columns", 1} 

2647 Apply to the index or columns. 

2648 level : int, str, list 

2649 The level(s) to hide in a MultiIndex if hiding the entire index / column 

2650 headers. Cannot be used simultaneously with ``subset``. 

2651 names : bool 

2652 Whether to hide the level name(s) of the index / columns headers in the case 

2653 it (or at least one the levels) remains visible. 

2654 

2655 Returns 

2656 ------- 

2657 Styler 

2658 

2659 Notes 

2660 ----- 

2661 .. warning:: 

2662 This method only works with the output methods ``to_html``, ``to_string`` 

2663 and ``to_latex``. 

2664 

2665 Other output methods, including ``to_excel``, ignore this hiding method 

2666 and will display all data. 

2667 

2668 This method has multiple functionality depending upon the combination 

2669 of the ``subset``, ``level`` and ``names`` arguments (see examples). The 

2670 ``axis`` argument is used only to control whether the method is applied to row 

2671 or column headers: 

2672 

2673 .. list-table:: Argument combinations 

2674 :widths: 10 20 10 60 

2675 :header-rows: 1 

2676 

2677 * - ``subset`` 

2678 - ``level`` 

2679 - ``names`` 

2680 - Effect 

2681 * - None 

2682 - None 

2683 - False 

2684 - The axis-Index is hidden entirely. 

2685 * - None 

2686 - None 

2687 - True 

2688 - Only the axis-Index names are hidden. 

2689 * - None 

2690 - Int, Str, List 

2691 - False 

2692 - Specified axis-MultiIndex levels are hidden entirely. 

2693 * - None 

2694 - Int, Str, List 

2695 - True 

2696 - Specified axis-MultiIndex levels are hidden entirely and the names of 

2697 remaining axis-MultiIndex levels. 

2698 * - Subset 

2699 - None 

2700 - False 

2701 - The specified data rows/columns are hidden, but the axis-Index itself, 

2702 and names, remain unchanged. 

2703 * - Subset 

2704 - None 

2705 - True 

2706 - The specified data rows/columns and axis-Index names are hidden, but 

2707 the axis-Index itself remains unchanged. 

2708 * - Subset 

2709 - Int, Str, List 

2710 - Boolean 

2711 - ValueError: cannot supply ``subset`` and ``level`` simultaneously. 

2712 

2713 Note this method only hides the identified elements so can be chained to hide 

2714 multiple elements in sequence. 

2715 

2716 Examples 

2717 -------- 

2718 Simple application hiding specific rows: 

2719 

2720 >>> df = pd.DataFrame([[1,2], [3,4], [5,6]], index=["a", "b", "c"]) 

2721 >>> df.style.hide(["a", "b"]) # doctest: +SKIP 

2722 0 1 

2723 c 5 6 

2724 

2725 Hide the index and retain the data values: 

2726 

2727 >>> midx = pd.MultiIndex.from_product([["x", "y"], ["a", "b", "c"]]) 

2728 >>> df = pd.DataFrame(np.random.randn(6,6), index=midx, columns=midx) 

2729 >>> df.style.format("{:.1f}").hide() # doctest: +SKIP 

2730 x y 

2731 a b c a b c 

2732 0.1 0.0 0.4 1.3 0.6 -1.4 

2733 0.7 1.0 1.3 1.5 -0.0 -0.2 

2734 1.4 -0.8 1.6 -0.2 -0.4 -0.3 

2735 0.4 1.0 -0.2 -0.8 -1.2 1.1 

2736 -0.6 1.2 1.8 1.9 0.3 0.3 

2737 0.8 0.5 -0.3 1.2 2.2 -0.8 

2738 

2739 Hide specific rows in a MultiIndex but retain the index: 

2740 

2741 >>> df.style.format("{:.1f}").hide(subset=(slice(None), ["a", "c"])) 

2742 ... # doctest: +SKIP 

2743 x y 

2744 a b c a b c 

2745 x b 0.7 1.0 1.3 1.5 -0.0 -0.2 

2746 y b -0.6 1.2 1.8 1.9 0.3 0.3 

2747 

2748 Hide specific rows and the index through chaining: 

2749 

2750 >>> df.style.format("{:.1f}").hide(subset=(slice(None), ["a", "c"])).hide() 

2751 ... # doctest: +SKIP 

2752 x y 

2753 a b c a b c 

2754 0.7 1.0 1.3 1.5 -0.0 -0.2 

2755 -0.6 1.2 1.8 1.9 0.3 0.3 

2756 

2757 Hide a specific level: 

2758 

2759 >>> df.style.format("{:,.1f}").hide(level=1) # doctest: +SKIP 

2760 x y 

2761 a b c a b c 

2762 x 0.1 0.0 0.4 1.3 0.6 -1.4 

2763 0.7 1.0 1.3 1.5 -0.0 -0.2 

2764 1.4 -0.8 1.6 -0.2 -0.4 -0.3 

2765 y 0.4 1.0 -0.2 -0.8 -1.2 1.1 

2766 -0.6 1.2 1.8 1.9 0.3 0.3 

2767 0.8 0.5 -0.3 1.2 2.2 -0.8 

2768 

2769 Hiding just the index level names: 

2770 

2771 >>> df.index.names = ["lev0", "lev1"] 

2772 >>> df.style.format("{:,.1f}").hide(names=True) # doctest: +SKIP 

2773 x y 

2774 a b c a b c 

2775 x a 0.1 0.0 0.4 1.3 0.6 -1.4 

2776 b 0.7 1.0 1.3 1.5 -0.0 -0.2 

2777 c 1.4 -0.8 1.6 -0.2 -0.4 -0.3 

2778 y a 0.4 1.0 -0.2 -0.8 -1.2 1.1 

2779 b -0.6 1.2 1.8 1.9 0.3 0.3 

2780 c 0.8 0.5 -0.3 1.2 2.2 -0.8 

2781 

2782 Examples all produce equivalently transposed effects with ``axis="columns"``. 

2783 """ 

2784 axis = self.data._get_axis_number(axis) 

2785 if axis == 0: 

2786 obj, objs, alt = "index", "index", "rows" 

2787 else: 

2788 obj, objs, alt = "column", "columns", "columns" 

2789 

2790 if level is not None and subset is not None: 

2791 raise ValueError("`subset` and `level` cannot be passed simultaneously") 

2792 

2793 if subset is None: 

2794 if level is None and names: 

2795 # this combination implies user shows the index and hides just names 

2796 setattr(self, f"hide_{obj}_names", True) 

2797 return self 

2798 

2799 levels_ = refactor_levels(level, getattr(self, objs)) 

2800 setattr( 

2801 self, 

2802 f"hide_{objs}_", 

2803 [lev in levels_ for lev in range(getattr(self, objs).nlevels)], 

2804 ) 

2805 else: 

2806 if axis == 0: 

2807 subset_ = IndexSlice[subset, :] # new var so mypy reads not Optional 

2808 else: 

2809 subset_ = IndexSlice[:, subset] # new var so mypy reads not Optional 

2810 subset = non_reducing_slice(subset_) 

2811 hide = self.data.loc[subset] 

2812 h_els = getattr(self, objs).get_indexer_for(getattr(hide, objs)) 

2813 setattr(self, f"hidden_{alt}", h_els) 

2814 

2815 if names: 

2816 setattr(self, f"hide_{obj}_names", True) 

2817 return self 

2818 

2819 # ----------------------------------------------------------------------- 

2820 # A collection of "builtin" styles 

2821 # ----------------------------------------------------------------------- 

2822 

2823 def _get_numeric_subset_default(self): 

2824 # Returns a boolean mask indicating where `self.data` has numerical columns. 

2825 # Choosing a mask as opposed to the column names also works for 

2826 # boolean column labels (GH47838). 

2827 return self.data.columns.isin(self.data.select_dtypes(include=np.number)) 

2828 

2829 @doc( 

2830 name="background", 

2831 alt="text", 

2832 image_prefix="bg", 

2833 text_threshold="""text_color_threshold : float or int\n 

2834 Luminance threshold for determining text color in [0, 1]. Facilitates text\n 

2835 visibility across varying background colors. All text is dark if 0, and\n 

2836 light if 1, defaults to 0.408.""", 

2837 ) 

2838 @Substitution(subset=subset_args) 

2839 def background_gradient( 

2840 self, 

2841 cmap: str | Colormap = "PuBu", 

2842 low: float = 0, 

2843 high: float = 0, 

2844 axis: Axis | None = 0, 

2845 subset: Subset | None = None, 

2846 text_color_threshold: float = 0.408, 

2847 vmin: float | None = None, 

2848 vmax: float | None = None, 

2849 gmap: Sequence | None = None, 

2850 ) -> Styler: 

2851 """ 

2852 Color the {name} in a gradient style. 

2853 

2854 The {name} color is determined according 

2855 to the data in each column, row or frame, or by a given 

2856 gradient map. Requires matplotlib. 

2857 

2858 Parameters 

2859 ---------- 

2860 cmap : str or colormap 

2861 Matplotlib colormap. 

2862 low : float 

2863 Compress the color range at the low end. This is a multiple of the data 

2864 range to extend below the minimum; good values usually in [0, 1], 

2865 defaults to 0. 

2866 high : float 

2867 Compress the color range at the high end. This is a multiple of the data 

2868 range to extend above the maximum; good values usually in [0, 1], 

2869 defaults to 0. 

2870 axis : {{0, 1, "index", "columns", None}}, default 0 

2871 Apply to each column (``axis=0`` or ``'index'``), to each row 

2872 (``axis=1`` or ``'columns'``), or to the entire DataFrame at once 

2873 with ``axis=None``. 

2874 %(subset)s 

2875 {text_threshold} 

2876 vmin : float, optional 

2877 Minimum data value that corresponds to colormap minimum value. 

2878 If not specified the minimum value of the data (or gmap) will be used. 

2879 vmax : float, optional 

2880 Maximum data value that corresponds to colormap maximum value. 

2881 If not specified the maximum value of the data (or gmap) will be used. 

2882 gmap : array-like, optional 

2883 Gradient map for determining the {name} colors. If not supplied 

2884 will use the underlying data from rows, columns or frame. If given as an 

2885 ndarray or list-like must be an identical shape to the underlying data 

2886 considering ``axis`` and ``subset``. If given as DataFrame or Series must 

2887 have same index and column labels considering ``axis`` and ``subset``. 

2888 If supplied, ``vmin`` and ``vmax`` should be given relative to this 

2889 gradient map. 

2890 

2891 .. versionadded:: 1.3.0 

2892 

2893 Returns 

2894 ------- 

2895 Styler 

2896 

2897 See Also 

2898 -------- 

2899 Styler.{alt}_gradient: Color the {alt} in a gradient style. 

2900 

2901 Notes 

2902 ----- 

2903 When using ``low`` and ``high`` the range 

2904 of the gradient, given by the data if ``gmap`` is not given or by ``gmap``, 

2905 is extended at the low end effectively by 

2906 `map.min - low * map.range` and at the high end by 

2907 `map.max + high * map.range` before the colors are normalized and determined. 

2908 

2909 If combining with ``vmin`` and ``vmax`` the `map.min`, `map.max` and 

2910 `map.range` are replaced by values according to the values derived from 

2911 ``vmin`` and ``vmax``. 

2912 

2913 This method will preselect numeric columns and ignore non-numeric columns 

2914 unless a ``gmap`` is supplied in which case no preselection occurs. 

2915 

2916 Examples 

2917 -------- 

2918 >>> df = pd.DataFrame(columns=["City", "Temp (c)", "Rain (mm)", "Wind (m/s)"], 

2919 ... data=[["Stockholm", 21.6, 5.0, 3.2], 

2920 ... ["Oslo", 22.4, 13.3, 3.1], 

2921 ... ["Copenhagen", 24.5, 0.0, 6.7]]) 

2922 

2923 Shading the values column-wise, with ``axis=0``, preselecting numeric columns 

2924 

2925 >>> df.style.{name}_gradient(axis=0) # doctest: +SKIP 

2926 

2927 .. figure:: ../../_static/style/{image_prefix}_ax0.png 

2928 

2929 Shading all values collectively using ``axis=None`` 

2930 

2931 >>> df.style.{name}_gradient(axis=None) # doctest: +SKIP 

2932 

2933 .. figure:: ../../_static/style/{image_prefix}_axNone.png 

2934 

2935 Compress the color map from the both ``low`` and ``high`` ends 

2936 

2937 >>> df.style.{name}_gradient(axis=None, low=0.75, high=1.0) # doctest: +SKIP 

2938 

2939 .. figure:: ../../_static/style/{image_prefix}_axNone_lowhigh.png 

2940 

2941 Manually setting ``vmin`` and ``vmax`` gradient thresholds 

2942 

2943 >>> df.style.{name}_gradient(axis=None, vmin=6.7, vmax=21.6) # doctest: +SKIP 

2944 

2945 .. figure:: ../../_static/style/{image_prefix}_axNone_vminvmax.png 

2946 

2947 Setting a ``gmap`` and applying to all columns with another ``cmap`` 

2948 

2949 >>> df.style.{name}_gradient(axis=0, gmap=df['Temp (c)'], cmap='YlOrRd') 

2950 ... # doctest: +SKIP 

2951 

2952 .. figure:: ../../_static/style/{image_prefix}_gmap.png 

2953 

2954 Setting the gradient map for a dataframe (i.e. ``axis=None``), we need to 

2955 explicitly state ``subset`` to match the ``gmap`` shape 

2956 

2957 >>> gmap = np.array([[1,2,3], [2,3,4], [3,4,5]]) 

2958 >>> df.style.{name}_gradient(axis=None, gmap=gmap, 

2959 ... cmap='YlOrRd', subset=['Temp (c)', 'Rain (mm)', 'Wind (m/s)'] 

2960 ... ) # doctest: +SKIP 

2961 

2962 .. figure:: ../../_static/style/{image_prefix}_axNone_gmap.png 

2963 """ 

2964 if subset is None and gmap is None: 

2965 subset = self._get_numeric_subset_default() 

2966 

2967 self.apply( 

2968 _background_gradient, 

2969 cmap=cmap, 

2970 subset=subset, 

2971 axis=axis, 

2972 low=low, 

2973 high=high, 

2974 text_color_threshold=text_color_threshold, 

2975 vmin=vmin, 

2976 vmax=vmax, 

2977 gmap=gmap, 

2978 ) 

2979 return self 

2980 

2981 @doc( 

2982 background_gradient, 

2983 name="text", 

2984 alt="background", 

2985 image_prefix="tg", 

2986 text_threshold="", 

2987 ) 

2988 def text_gradient( 

2989 self, 

2990 cmap: str | Colormap = "PuBu", 

2991 low: float = 0, 

2992 high: float = 0, 

2993 axis: Axis | None = 0, 

2994 subset: Subset | None = None, 

2995 vmin: float | None = None, 

2996 vmax: float | None = None, 

2997 gmap: Sequence | None = None, 

2998 ) -> Styler: 

2999 if subset is None and gmap is None: 

3000 subset = self._get_numeric_subset_default() 

3001 

3002 return self.apply( 

3003 _background_gradient, 

3004 cmap=cmap, 

3005 subset=subset, 

3006 axis=axis, 

3007 low=low, 

3008 high=high, 

3009 vmin=vmin, 

3010 vmax=vmax, 

3011 gmap=gmap, 

3012 text_only=True, 

3013 ) 

3014 

3015 @Substitution(subset=subset_args) 

3016 def set_properties(self, subset: Subset | None = None, **kwargs) -> Styler: 

3017 """ 

3018 Set defined CSS-properties to each ``<td>`` HTML element for the given subset. 

3019 

3020 Parameters 

3021 ---------- 

3022 %(subset)s 

3023 **kwargs : dict 

3024 A dictionary of property, value pairs to be set for each cell. 

3025 

3026 Returns 

3027 ------- 

3028 Styler 

3029 

3030 Notes 

3031 ----- 

3032 This is a convenience methods which wraps the :meth:`Styler.map` calling a 

3033 function returning the CSS-properties independently of the data. 

3034 

3035 Examples 

3036 -------- 

3037 >>> df = pd.DataFrame(np.random.randn(10, 4)) 

3038 >>> df.style.set_properties(color="white", align="right") # doctest: +SKIP 

3039 >>> df.style.set_properties(**{'background-color': 'yellow'}) # doctest: +SKIP 

3040 

3041 See `Table Visualization <../../user_guide/style.ipynb>`_ user guide for 

3042 more details. 

3043 """ 

3044 values = "".join([f"{p}: {v};" for p, v in kwargs.items()]) 

3045 return self.map(lambda x: values, subset=subset) 

3046 

3047 @Substitution(subset=subset_args) 

3048 def bar( # pylint: disable=disallowed-name 

3049 self, 

3050 subset: Subset | None = None, 

3051 axis: Axis | None = 0, 

3052 *, 

3053 color: str | list | tuple | None = None, 

3054 cmap: Any | None = None, 

3055 width: float = 100, 

3056 height: float = 100, 

3057 align: str | float | Callable = "mid", 

3058 vmin: float | None = None, 

3059 vmax: float | None = None, 

3060 props: str = "width: 10em;", 

3061 ) -> Styler: 

3062 """ 

3063 Draw bar chart in the cell backgrounds. 

3064 

3065 .. versionchanged:: 1.4.0 

3066 

3067 Parameters 

3068 ---------- 

3069 %(subset)s 

3070 axis : {0 or 'index', 1 or 'columns', None}, default 0 

3071 Apply to each column (``axis=0`` or ``'index'``), to each row 

3072 (``axis=1`` or ``'columns'``), or to the entire DataFrame at once 

3073 with ``axis=None``. 

3074 color : str or 2-tuple/list 

3075 If a str is passed, the color is the same for both 

3076 negative and positive numbers. If 2-tuple/list is used, the 

3077 first element is the color_negative and the second is the 

3078 color_positive (eg: ['#d65f5f', '#5fba7d']). 

3079 cmap : str, matplotlib.cm.ColorMap 

3080 A string name of a matplotlib Colormap, or a Colormap object. Cannot be 

3081 used together with ``color``. 

3082 

3083 .. versionadded:: 1.4.0 

3084 width : float, default 100 

3085 The percentage of the cell, measured from the left, in which to draw the 

3086 bars, in [0, 100]. 

3087 height : float, default 100 

3088 The percentage height of the bar in the cell, centrally aligned, in [0,100]. 

3089 

3090 .. versionadded:: 1.4.0 

3091 align : str, int, float, callable, default 'mid' 

3092 How to align the bars within the cells relative to a width adjusted center. 

3093 If string must be one of: 

3094 

3095 - 'left' : bars are drawn rightwards from the minimum data value. 

3096 - 'right' : bars are drawn leftwards from the maximum data value. 

3097 - 'zero' : a value of zero is located at the center of the cell. 

3098 - 'mid' : a value of (max-min)/2 is located at the center of the cell, 

3099 or if all values are negative (positive) the zero is 

3100 aligned at the right (left) of the cell. 

3101 - 'mean' : the mean value of the data is located at the center of the cell. 

3102 

3103 If a float or integer is given this will indicate the center of the cell. 

3104 

3105 If a callable should take a 1d or 2d array and return a scalar. 

3106 

3107 .. versionchanged:: 1.4.0 

3108 

3109 vmin : float, optional 

3110 Minimum bar value, defining the left hand limit 

3111 of the bar drawing range, lower values are clipped to `vmin`. 

3112 When None (default): the minimum value of the data will be used. 

3113 vmax : float, optional 

3114 Maximum bar value, defining the right hand limit 

3115 of the bar drawing range, higher values are clipped to `vmax`. 

3116 When None (default): the maximum value of the data will be used. 

3117 props : str, optional 

3118 The base CSS of the cell that is extended to add the bar chart. Defaults to 

3119 `"width: 10em;"`. 

3120 

3121 .. versionadded:: 1.4.0 

3122 

3123 Returns 

3124 ------- 

3125 Styler 

3126 

3127 Notes 

3128 ----- 

3129 This section of the user guide: 

3130 `Table Visualization <../../user_guide/style.ipynb>`_ gives 

3131 a number of examples for different settings and color coordination. 

3132 

3133 Examples 

3134 -------- 

3135 >>> df = pd.DataFrame({'A': [1, 2, 3, 4], 'B': [3, 4, 5, 6]}) 

3136 >>> df.style.bar(subset=['A'], color='gray') # doctest: +SKIP 

3137 """ 

3138 if color is None and cmap is None: 

3139 color = "#d65f5f" 

3140 elif color is not None and cmap is not None: 

3141 raise ValueError("`color` and `cmap` cannot both be given") 

3142 elif color is not None: 

3143 if (isinstance(color, (list, tuple)) and len(color) > 2) or not isinstance( 

3144 color, (str, list, tuple) 

3145 ): 

3146 raise ValueError( 

3147 "`color` must be string or list or tuple of 2 strings," 

3148 "(eg: color=['#d65f5f', '#5fba7d'])" 

3149 ) 

3150 

3151 if not 0 <= width <= 100: 

3152 raise ValueError(f"`width` must be a value in [0, 100], got {width}") 

3153 if not 0 <= height <= 100: 

3154 raise ValueError(f"`height` must be a value in [0, 100], got {height}") 

3155 

3156 if subset is None: 

3157 subset = self._get_numeric_subset_default() 

3158 

3159 self.apply( 

3160 _bar, 

3161 subset=subset, 

3162 axis=axis, 

3163 align=align, 

3164 colors=color, 

3165 cmap=cmap, 

3166 width=width / 100, 

3167 height=height / 100, 

3168 vmin=vmin, 

3169 vmax=vmax, 

3170 base_css=props, 

3171 ) 

3172 

3173 return self 

3174 

3175 @Substitution( 

3176 subset=subset_args, 

3177 props=properties_args, 

3178 color=coloring_args.format(default="red"), 

3179 ) 

3180 def highlight_null( 

3181 self, 

3182 color: str = "red", 

3183 subset: Subset | None = None, 

3184 props: str | None = None, 

3185 ) -> Styler: 

3186 """ 

3187 Highlight missing values with a style. 

3188 

3189 Parameters 

3190 ---------- 

3191 %(color)s 

3192 

3193 .. versionadded:: 1.5.0 

3194 

3195 %(subset)s 

3196 

3197 %(props)s 

3198 

3199 .. versionadded:: 1.3.0 

3200 

3201 Returns 

3202 ------- 

3203 Styler 

3204 

3205 See Also 

3206 -------- 

3207 Styler.highlight_max: Highlight the maximum with a style. 

3208 Styler.highlight_min: Highlight the minimum with a style. 

3209 Styler.highlight_between: Highlight a defined range with a style. 

3210 Styler.highlight_quantile: Highlight values defined by a quantile with a style. 

3211 

3212 Examples 

3213 -------- 

3214 >>> df = pd.DataFrame({'A': [1, 2], 'B': [3, np.nan]}) 

3215 >>> df.style.highlight_null(color='yellow') # doctest: +SKIP 

3216 

3217 Please see: 

3218 `Table Visualization <../../user_guide/style.ipynb>`_ for more examples. 

3219 """ 

3220 

3221 def f(data: DataFrame, props: str) -> np.ndarray: 

3222 return np.where(pd.isna(data).to_numpy(), props, "") 

3223 

3224 if props is None: 

3225 props = f"background-color: {color};" 

3226 return self.apply(f, axis=None, subset=subset, props=props) 

3227 

3228 @Substitution( 

3229 subset=subset_args, 

3230 color=coloring_args.format(default="yellow"), 

3231 props=properties_args, 

3232 ) 

3233 def highlight_max( 

3234 self, 

3235 subset: Subset | None = None, 

3236 color: str = "yellow", 

3237 axis: Axis | None = 0, 

3238 props: str | None = None, 

3239 ) -> Styler: 

3240 """ 

3241 Highlight the maximum with a style. 

3242 

3243 Parameters 

3244 ---------- 

3245 %(subset)s 

3246 %(color)s 

3247 axis : {0 or 'index', 1 or 'columns', None}, default 0 

3248 Apply to each column (``axis=0`` or ``'index'``), to each row 

3249 (``axis=1`` or ``'columns'``), or to the entire DataFrame at once 

3250 with ``axis=None``. 

3251 %(props)s 

3252 

3253 .. versionadded:: 1.3.0 

3254 

3255 Returns 

3256 ------- 

3257 Styler 

3258 

3259 See Also 

3260 -------- 

3261 Styler.highlight_null: Highlight missing values with a style. 

3262 Styler.highlight_min: Highlight the minimum with a style. 

3263 Styler.highlight_between: Highlight a defined range with a style. 

3264 Styler.highlight_quantile: Highlight values defined by a quantile with a style. 

3265 

3266 Examples 

3267 -------- 

3268 >>> df = pd.DataFrame({'A': [2, 1], 'B': [3, 4]}) 

3269 >>> df.style.highlight_max(color='yellow') # doctest: +SKIP 

3270 

3271 Please see: 

3272 `Table Visualization <../../user_guide/style.ipynb>`_ for more examples. 

3273 """ 

3274 

3275 if props is None: 

3276 props = f"background-color: {color};" 

3277 return self.apply( 

3278 partial(_highlight_value, op="max"), 

3279 axis=axis, 

3280 subset=subset, 

3281 props=props, 

3282 ) 

3283 

3284 @Substitution( 

3285 subset=subset_args, 

3286 color=coloring_args.format(default="yellow"), 

3287 props=properties_args, 

3288 ) 

3289 def highlight_min( 

3290 self, 

3291 subset: Subset | None = None, 

3292 color: str = "yellow", 

3293 axis: Axis | None = 0, 

3294 props: str | None = None, 

3295 ) -> Styler: 

3296 """ 

3297 Highlight the minimum with a style. 

3298 

3299 Parameters 

3300 ---------- 

3301 %(subset)s 

3302 %(color)s 

3303 axis : {0 or 'index', 1 or 'columns', None}, default 0 

3304 Apply to each column (``axis=0`` or ``'index'``), to each row 

3305 (``axis=1`` or ``'columns'``), or to the entire DataFrame at once 

3306 with ``axis=None``. 

3307 %(props)s 

3308 

3309 .. versionadded:: 1.3.0 

3310 

3311 Returns 

3312 ------- 

3313 Styler 

3314 

3315 See Also 

3316 -------- 

3317 Styler.highlight_null: Highlight missing values with a style. 

3318 Styler.highlight_max: Highlight the maximum with a style. 

3319 Styler.highlight_between: Highlight a defined range with a style. 

3320 Styler.highlight_quantile: Highlight values defined by a quantile with a style. 

3321 

3322 Examples 

3323 -------- 

3324 >>> df = pd.DataFrame({'A': [2, 1], 'B': [3, 4]}) 

3325 >>> df.style.highlight_min(color='yellow') # doctest: +SKIP 

3326 

3327 Please see: 

3328 `Table Visualization <../../user_guide/style.ipynb>`_ for more examples. 

3329 """ 

3330 

3331 if props is None: 

3332 props = f"background-color: {color};" 

3333 return self.apply( 

3334 partial(_highlight_value, op="min"), 

3335 axis=axis, 

3336 subset=subset, 

3337 props=props, 

3338 ) 

3339 

3340 @Substitution( 

3341 subset=subset_args, 

3342 color=coloring_args.format(default="yellow"), 

3343 props=properties_args, 

3344 ) 

3345 def highlight_between( 

3346 self, 

3347 subset: Subset | None = None, 

3348 color: str = "yellow", 

3349 axis: Axis | None = 0, 

3350 left: Scalar | Sequence | None = None, 

3351 right: Scalar | Sequence | None = None, 

3352 inclusive: IntervalClosedType = "both", 

3353 props: str | None = None, 

3354 ) -> Styler: 

3355 """ 

3356 Highlight a defined range with a style. 

3357 

3358 .. versionadded:: 1.3.0 

3359 

3360 Parameters 

3361 ---------- 

3362 %(subset)s 

3363 %(color)s 

3364 axis : {0 or 'index', 1 or 'columns', None}, default 0 

3365 If ``left`` or ``right`` given as sequence, axis along which to apply those 

3366 boundaries. See examples. 

3367 left : scalar or datetime-like, or sequence or array-like, default None 

3368 Left bound for defining the range. 

3369 right : scalar or datetime-like, or sequence or array-like, default None 

3370 Right bound for defining the range. 

3371 inclusive : {'both', 'neither', 'left', 'right'} 

3372 Identify whether bounds are closed or open. 

3373 %(props)s 

3374 

3375 Returns 

3376 ------- 

3377 Styler 

3378 

3379 See Also 

3380 -------- 

3381 Styler.highlight_null: Highlight missing values with a style. 

3382 Styler.highlight_max: Highlight the maximum with a style. 

3383 Styler.highlight_min: Highlight the minimum with a style. 

3384 Styler.highlight_quantile: Highlight values defined by a quantile with a style. 

3385 

3386 Notes 

3387 ----- 

3388 If ``left`` is ``None`` only the right bound is applied. 

3389 If ``right`` is ``None`` only the left bound is applied. If both are ``None`` 

3390 all values are highlighted. 

3391 

3392 ``axis`` is only needed if ``left`` or ``right`` are provided as a sequence or 

3393 an array-like object for aligning the shapes. If ``left`` and ``right`` are 

3394 both scalars then all ``axis`` inputs will give the same result. 

3395 

3396 This function only works with compatible ``dtypes``. For example a datetime-like 

3397 region can only use equivalent datetime-like ``left`` and ``right`` arguments. 

3398 Use ``subset`` to control regions which have multiple ``dtypes``. 

3399 

3400 Examples 

3401 -------- 

3402 Basic usage 

3403 

3404 >>> df = pd.DataFrame({ 

3405 ... 'One': [1.2, 1.6, 1.5], 

3406 ... 'Two': [2.9, 2.1, 2.5], 

3407 ... 'Three': [3.1, 3.2, 3.8], 

3408 ... }) 

3409 >>> df.style.highlight_between(left=2.1, right=2.9) # doctest: +SKIP 

3410 

3411 .. figure:: ../../_static/style/hbetw_basic.png 

3412 

3413 Using a range input sequence along an ``axis``, in this case setting a ``left`` 

3414 and ``right`` for each column individually 

3415 

3416 >>> df.style.highlight_between(left=[1.4, 2.4, 3.4], right=[1.6, 2.6, 3.6], 

3417 ... axis=1, color="#fffd75") # doctest: +SKIP 

3418 

3419 .. figure:: ../../_static/style/hbetw_seq.png 

3420 

3421 Using ``axis=None`` and providing the ``left`` argument as an array that 

3422 matches the input DataFrame, with a constant ``right`` 

3423 

3424 >>> df.style.highlight_between(left=[[2,2,3],[2,2,3],[3,3,3]], right=3.5, 

3425 ... axis=None, color="#fffd75") # doctest: +SKIP 

3426 

3427 .. figure:: ../../_static/style/hbetw_axNone.png 

3428 

3429 Using ``props`` instead of default background coloring 

3430 

3431 >>> df.style.highlight_between(left=1.5, right=3.5, 

3432 ... props='font-weight:bold;color:#e83e8c') # doctest: +SKIP 

3433 

3434 .. figure:: ../../_static/style/hbetw_props.png 

3435 """ 

3436 if props is None: 

3437 props = f"background-color: {color};" 

3438 return self.apply( 

3439 _highlight_between, 

3440 axis=axis, 

3441 subset=subset, 

3442 props=props, 

3443 left=left, 

3444 right=right, 

3445 inclusive=inclusive, 

3446 ) 

3447 

3448 @Substitution( 

3449 subset=subset_args, 

3450 color=coloring_args.format(default="yellow"), 

3451 props=properties_args, 

3452 ) 

3453 def highlight_quantile( 

3454 self, 

3455 subset: Subset | None = None, 

3456 color: str = "yellow", 

3457 axis: Axis | None = 0, 

3458 q_left: float = 0.0, 

3459 q_right: float = 1.0, 

3460 interpolation: QuantileInterpolation = "linear", 

3461 inclusive: IntervalClosedType = "both", 

3462 props: str | None = None, 

3463 ) -> Styler: 

3464 """ 

3465 Highlight values defined by a quantile with a style. 

3466 

3467 .. versionadded:: 1.3.0 

3468 

3469 Parameters 

3470 ---------- 

3471 %(subset)s 

3472 %(color)s 

3473 axis : {0 or 'index', 1 or 'columns', None}, default 0 

3474 Axis along which to determine and highlight quantiles. If ``None`` quantiles 

3475 are measured over the entire DataFrame. See examples. 

3476 q_left : float, default 0 

3477 Left bound, in [0, q_right), for the target quantile range. 

3478 q_right : float, default 1 

3479 Right bound, in (q_left, 1], for the target quantile range. 

3480 interpolation : {'linear', 'lower', 'higher', 'midpoint', 'nearest'} 

3481 Argument passed to ``Series.quantile`` or ``DataFrame.quantile`` for 

3482 quantile estimation. 

3483 inclusive : {'both', 'neither', 'left', 'right'} 

3484 Identify whether quantile bounds are closed or open. 

3485 %(props)s 

3486 

3487 Returns 

3488 ------- 

3489 Styler 

3490 

3491 See Also 

3492 -------- 

3493 Styler.highlight_null: Highlight missing values with a style. 

3494 Styler.highlight_max: Highlight the maximum with a style. 

3495 Styler.highlight_min: Highlight the minimum with a style. 

3496 Styler.highlight_between: Highlight a defined range with a style. 

3497 

3498 Notes 

3499 ----- 

3500 This function does not work with ``str`` dtypes. 

3501 

3502 Examples 

3503 -------- 

3504 Using ``axis=None`` and apply a quantile to all collective data 

3505 

3506 >>> df = pd.DataFrame(np.arange(10).reshape(2,5) + 1) 

3507 >>> df.style.highlight_quantile(axis=None, q_left=0.8, color="#fffd75") 

3508 ... # doctest: +SKIP 

3509 

3510 .. figure:: ../../_static/style/hq_axNone.png 

3511 

3512 Or highlight quantiles row-wise or column-wise, in this case by row-wise 

3513 

3514 >>> df.style.highlight_quantile(axis=1, q_left=0.8, color="#fffd75") 

3515 ... # doctest: +SKIP 

3516 

3517 .. figure:: ../../_static/style/hq_ax1.png 

3518 

3519 Use ``props`` instead of default background coloring 

3520 

3521 >>> df.style.highlight_quantile(axis=None, q_left=0.2, q_right=0.8, 

3522 ... props='font-weight:bold;color:#e83e8c') # doctest: +SKIP 

3523 

3524 .. figure:: ../../_static/style/hq_props.png 

3525 """ 

3526 subset_ = slice(None) if subset is None else subset 

3527 subset_ = non_reducing_slice(subset_) 

3528 data = self.data.loc[subset_] 

3529 

3530 # after quantile is found along axis, e.g. along rows, 

3531 # applying the calculated quantile to alternate axis, e.g. to each column 

3532 quantiles = [q_left, q_right] 

3533 if axis is None: 

3534 q = Series(data.to_numpy().ravel()).quantile( 

3535 q=quantiles, interpolation=interpolation 

3536 ) 

3537 axis_apply: int | None = None 

3538 else: 

3539 axis = self.data._get_axis_number(axis) 

3540 q = data.quantile( 

3541 axis=axis, numeric_only=False, q=quantiles, interpolation=interpolation 

3542 ) 

3543 axis_apply = 1 - axis 

3544 

3545 if props is None: 

3546 props = f"background-color: {color};" 

3547 return self.apply( 

3548 _highlight_between, 

3549 axis=axis_apply, 

3550 subset=subset, 

3551 props=props, 

3552 left=q.iloc[0], 

3553 right=q.iloc[1], 

3554 inclusive=inclusive, 

3555 ) 

3556 

3557 @classmethod 

3558 def from_custom_template( 

3559 cls, 

3560 searchpath: Sequence[str], 

3561 html_table: str | None = None, 

3562 html_style: str | None = None, 

3563 ) -> type[Styler]: 

3564 """ 

3565 Factory function for creating a subclass of ``Styler``. 

3566 

3567 Uses custom templates and Jinja environment. 

3568 

3569 .. versionchanged:: 1.3.0 

3570 

3571 Parameters 

3572 ---------- 

3573 searchpath : str or list 

3574 Path or paths of directories containing the templates. 

3575 html_table : str 

3576 Name of your custom template to replace the html_table template. 

3577 

3578 .. versionadded:: 1.3.0 

3579 

3580 html_style : str 

3581 Name of your custom template to replace the html_style template. 

3582 

3583 .. versionadded:: 1.3.0 

3584 

3585 Returns 

3586 ------- 

3587 MyStyler : subclass of Styler 

3588 Has the correct ``env``,``template_html``, ``template_html_table`` and 

3589 ``template_html_style`` class attributes set. 

3590 

3591 Examples 

3592 -------- 

3593 >>> from pandas.io.formats.style import Styler 

3594 >>> EasyStyler = Styler.from_custom_template("path/to/template", 

3595 ... "template.tpl", 

3596 ... ) # doctest: +SKIP 

3597 >>> df = pd.DataFrame({"A": [1, 2]}) 

3598 >>> EasyStyler(df) # doctest: +SKIP 

3599 

3600 Please see: 

3601 `Table Visualization <../../user_guide/style.ipynb>`_ for more examples. 

3602 """ 

3603 loader = jinja2.ChoiceLoader([jinja2.FileSystemLoader(searchpath), cls.loader]) 

3604 

3605 # mypy doesn't like dynamically-defined classes 

3606 # error: Variable "cls" is not valid as a type 

3607 # error: Invalid base class "cls" 

3608 class MyStyler(cls): # type: ignore[valid-type,misc] 

3609 env = jinja2.Environment(loader=loader) 

3610 if html_table: 

3611 template_html_table = env.get_template(html_table) 

3612 if html_style: 

3613 template_html_style = env.get_template(html_style) 

3614 

3615 return MyStyler 

3616 

3617 def pipe(self, func: Callable, *args, **kwargs): 

3618 """ 

3619 Apply ``func(self, *args, **kwargs)``, and return the result. 

3620 

3621 Parameters 

3622 ---------- 

3623 func : function 

3624 Function to apply to the Styler. Alternatively, a 

3625 ``(callable, keyword)`` tuple where ``keyword`` is a string 

3626 indicating the keyword of ``callable`` that expects the Styler. 

3627 *args : optional 

3628 Arguments passed to `func`. 

3629 **kwargs : optional 

3630 A dictionary of keyword arguments passed into ``func``. 

3631 

3632 Returns 

3633 ------- 

3634 object : 

3635 The value returned by ``func``. 

3636 

3637 See Also 

3638 -------- 

3639 DataFrame.pipe : Analogous method for DataFrame. 

3640 Styler.apply : Apply a CSS-styling function column-wise, row-wise, or 

3641 table-wise. 

3642 

3643 Notes 

3644 ----- 

3645 Like :meth:`DataFrame.pipe`, this method can simplify the 

3646 application of several user-defined functions to a styler. Instead 

3647 of writing: 

3648 

3649 .. code-block:: python 

3650 

3651 f(g(df.style.format(precision=3), arg1=a), arg2=b, arg3=c) 

3652 

3653 users can write: 

3654 

3655 .. code-block:: python 

3656 

3657 (df.style.format(precision=3) 

3658 .pipe(g, arg1=a) 

3659 .pipe(f, arg2=b, arg3=c)) 

3660 

3661 In particular, this allows users to define functions that take a 

3662 styler object, along with other parameters, and return the styler after 

3663 making styling changes (such as calling :meth:`Styler.apply` or 

3664 :meth:`Styler.set_properties`). 

3665 

3666 Examples 

3667 -------- 

3668 

3669 **Common Use** 

3670 

3671 A common usage pattern is to pre-define styling operations which 

3672 can be easily applied to a generic styler in a single ``pipe`` call. 

3673 

3674 >>> def some_highlights(styler, min_color="red", max_color="blue"): 

3675 ... styler.highlight_min(color=min_color, axis=None) 

3676 ... styler.highlight_max(color=max_color, axis=None) 

3677 ... styler.highlight_null() 

3678 ... return styler 

3679 >>> df = pd.DataFrame([[1, 2, 3, pd.NA], [pd.NA, 4, 5, 6]], dtype="Int64") 

3680 >>> df.style.pipe(some_highlights, min_color="green") # doctest: +SKIP 

3681 

3682 .. figure:: ../../_static/style/df_pipe_hl.png 

3683 

3684 Since the method returns a ``Styler`` object it can be chained with other 

3685 methods as if applying the underlying highlighters directly. 

3686 

3687 >>> (df.style.format("{:.1f}") 

3688 ... .pipe(some_highlights, min_color="green") 

3689 ... .highlight_between(left=2, right=5)) # doctest: +SKIP 

3690 

3691 .. figure:: ../../_static/style/df_pipe_hl2.png 

3692 

3693 **Advanced Use** 

3694 

3695 Sometimes it may be necessary to pre-define styling functions, but in the case 

3696 where those functions rely on the styler, data or context. Since 

3697 ``Styler.use`` and ``Styler.export`` are designed to be non-data dependent, 

3698 they cannot be used for this purpose. Additionally the ``Styler.apply`` 

3699 and ``Styler.format`` type methods are not context aware, so a solution 

3700 is to use ``pipe`` to dynamically wrap this functionality. 

3701 

3702 Suppose we want to code a generic styling function that highlights the final 

3703 level of a MultiIndex. The number of levels in the Index is dynamic so we 

3704 need the ``Styler`` context to define the level. 

3705 

3706 >>> def highlight_last_level(styler): 

3707 ... return styler.apply_index( 

3708 ... lambda v: "background-color: pink; color: yellow", axis="columns", 

3709 ... level=styler.columns.nlevels-1 

3710 ... ) # doctest: +SKIP 

3711 >>> df.columns = pd.MultiIndex.from_product([["A", "B"], ["X", "Y"]]) 

3712 >>> df.style.pipe(highlight_last_level) # doctest: +SKIP 

3713 

3714 .. figure:: ../../_static/style/df_pipe_applymap.png 

3715 

3716 Additionally suppose we want to highlight a column header if there is any 

3717 missing data in that column. 

3718 In this case we need the data object itself to determine the effect on the 

3719 column headers. 

3720 

3721 >>> def highlight_header_missing(styler, level): 

3722 ... def dynamic_highlight(s): 

3723 ... return np.where( 

3724 ... styler.data.isna().any(), "background-color: red;", "" 

3725 ... ) 

3726 ... return styler.apply_index(dynamic_highlight, axis=1, level=level) 

3727 >>> df.style.pipe(highlight_header_missing, level=1) # doctest: +SKIP 

3728 

3729 .. figure:: ../../_static/style/df_pipe_applydata.png 

3730 """ 

3731 return com.pipe(self, func, *args, **kwargs) 

3732 

3733 

3734def _validate_apply_axis_arg( 

3735 arg: NDFrame | Sequence | np.ndarray, 

3736 arg_name: str, 

3737 dtype: Any | None, 

3738 data: NDFrame, 

3739) -> np.ndarray: 

3740 """ 

3741 For the apply-type methods, ``axis=None`` creates ``data`` as DataFrame, and for 

3742 ``axis=[1,0]`` it creates a Series. Where ``arg`` is expected as an element 

3743 of some operator with ``data`` we must make sure that the two are compatible shapes, 

3744 or raise. 

3745 

3746 Parameters 

3747 ---------- 

3748 arg : sequence, Series or DataFrame 

3749 the user input arg 

3750 arg_name : string 

3751 name of the arg for use in error messages 

3752 dtype : numpy dtype, optional 

3753 forced numpy dtype if given 

3754 data : Series or DataFrame 

3755 underling subset of Styler data on which operations are performed 

3756 

3757 Returns 

3758 ------- 

3759 ndarray 

3760 """ 

3761 dtype = {"dtype": dtype} if dtype else {} 

3762 # raise if input is wrong for axis: 

3763 if isinstance(arg, Series) and isinstance(data, DataFrame): 

3764 raise ValueError( 

3765 f"'{arg_name}' is a Series but underlying data for operations " 

3766 f"is a DataFrame since 'axis=None'" 

3767 ) 

3768 if isinstance(arg, DataFrame) and isinstance(data, Series): 

3769 raise ValueError( 

3770 f"'{arg_name}' is a DataFrame but underlying data for " 

3771 f"operations is a Series with 'axis in [0,1]'" 

3772 ) 

3773 if isinstance(arg, (Series, DataFrame)): # align indx / cols to data 

3774 arg = arg.reindex_like(data, method=None).to_numpy(**dtype) 

3775 else: 

3776 arg = np.asarray(arg, **dtype) 

3777 assert isinstance(arg, np.ndarray) # mypy requirement 

3778 if arg.shape != data.shape: # check valid input 

3779 raise ValueError( 

3780 f"supplied '{arg_name}' is not correct shape for data over " 

3781 f"selected 'axis': got {arg.shape}, " 

3782 f"expected {data.shape}" 

3783 ) 

3784 return arg 

3785 

3786 

3787def _background_gradient( 

3788 data, 

3789 cmap: str | Colormap = "PuBu", 

3790 low: float = 0, 

3791 high: float = 0, 

3792 text_color_threshold: float = 0.408, 

3793 vmin: float | None = None, 

3794 vmax: float | None = None, 

3795 gmap: Sequence | np.ndarray | DataFrame | Series | None = None, 

3796 text_only: bool = False, 

3797): 

3798 """ 

3799 Color background in a range according to the data or a gradient map 

3800 """ 

3801 if gmap is None: # the data is used the gmap 

3802 gmap = data.to_numpy(dtype=float, na_value=np.nan) 

3803 else: # else validate gmap against the underlying data 

3804 gmap = _validate_apply_axis_arg(gmap, "gmap", float, data) 

3805 

3806 with _mpl(Styler.background_gradient) as (_, _matplotlib): 

3807 smin = np.nanmin(gmap) if vmin is None else vmin 

3808 smax = np.nanmax(gmap) if vmax is None else vmax 

3809 rng = smax - smin 

3810 # extend lower / upper bounds, compresses color range 

3811 norm = _matplotlib.colors.Normalize(smin - (rng * low), smax + (rng * high)) 

3812 

3813 if cmap is None: 

3814 rgbas = _matplotlib.colormaps[_matplotlib.rcParams["image.cmap"]]( 

3815 norm(gmap) 

3816 ) 

3817 else: 

3818 rgbas = _matplotlib.colormaps.get_cmap(cmap)(norm(gmap)) 

3819 

3820 def relative_luminance(rgba) -> float: 

3821 """ 

3822 Calculate relative luminance of a color. 

3823 

3824 The calculation adheres to the W3C standards 

3825 (https://www.w3.org/WAI/GL/wiki/Relative_luminance) 

3826 

3827 Parameters 

3828 ---------- 

3829 color : rgb or rgba tuple 

3830 

3831 Returns 

3832 ------- 

3833 float 

3834 The relative luminance as a value from 0 to 1 

3835 """ 

3836 r, g, b = ( 

3837 x / 12.92 if x <= 0.04045 else ((x + 0.055) / 1.055) ** 2.4 

3838 for x in rgba[:3] 

3839 ) 

3840 return 0.2126 * r + 0.7152 * g + 0.0722 * b 

3841 

3842 def css(rgba, text_only) -> str: 

3843 if not text_only: 

3844 dark = relative_luminance(rgba) < text_color_threshold 

3845 text_color = "#f1f1f1" if dark else "#000000" 

3846 return ( 

3847 f"background-color: {_matplotlib.colors.rgb2hex(rgba)};" 

3848 f"color: {text_color};" 

3849 ) 

3850 else: 

3851 return f"color: {_matplotlib.colors.rgb2hex(rgba)};" 

3852 

3853 if data.ndim == 1: 

3854 return [css(rgba, text_only) for rgba in rgbas] 

3855 else: 

3856 return DataFrame( 

3857 [[css(rgba, text_only) for rgba in row] for row in rgbas], 

3858 index=data.index, 

3859 columns=data.columns, 

3860 ) 

3861 

3862 

3863def _highlight_between( 

3864 data: NDFrame, 

3865 props: str, 

3866 left: Scalar | Sequence | np.ndarray | NDFrame | None = None, 

3867 right: Scalar | Sequence | np.ndarray | NDFrame | None = None, 

3868 inclusive: bool | str = True, 

3869) -> np.ndarray: 

3870 """ 

3871 Return an array of css props based on condition of data values within given range. 

3872 """ 

3873 if np.iterable(left) and not isinstance(left, str): 

3874 left = _validate_apply_axis_arg(left, "left", None, data) 

3875 

3876 if np.iterable(right) and not isinstance(right, str): 

3877 right = _validate_apply_axis_arg(right, "right", None, data) 

3878 

3879 # get ops with correct boundary attribution 

3880 if inclusive == "both": 

3881 ops = (operator.ge, operator.le) 

3882 elif inclusive == "neither": 

3883 ops = (operator.gt, operator.lt) 

3884 elif inclusive == "left": 

3885 ops = (operator.ge, operator.lt) 

3886 elif inclusive == "right": 

3887 ops = (operator.gt, operator.le) 

3888 else: 

3889 raise ValueError( 

3890 f"'inclusive' values can be 'both', 'left', 'right', or 'neither' " 

3891 f"got {inclusive}" 

3892 ) 

3893 

3894 g_left = ( 

3895 # error: Argument 2 to "ge" has incompatible type "Union[str, float, 

3896 # Period, Timedelta, Interval[Any], datetime64, timedelta64, datetime, 

3897 # Sequence[Any], ndarray[Any, Any], NDFrame]"; expected "Union 

3898 # [SupportsDunderLE, SupportsDunderGE, SupportsDunderGT, SupportsDunderLT]" 

3899 ops[0](data, left) # type: ignore[arg-type] 

3900 if left is not None 

3901 else np.full(data.shape, True, dtype=bool) 

3902 ) 

3903 if isinstance(g_left, (DataFrame, Series)): 

3904 g_left = g_left.where(pd.notna(g_left), False) 

3905 l_right = ( 

3906 # error: Argument 2 to "le" has incompatible type "Union[str, float, 

3907 # Period, Timedelta, Interval[Any], datetime64, timedelta64, datetime, 

3908 # Sequence[Any], ndarray[Any, Any], NDFrame]"; expected "Union 

3909 # [SupportsDunderLE, SupportsDunderGE, SupportsDunderGT, SupportsDunderLT]" 

3910 ops[1](data, right) # type: ignore[arg-type] 

3911 if right is not None 

3912 else np.full(data.shape, True, dtype=bool) 

3913 ) 

3914 if isinstance(l_right, (DataFrame, Series)): 

3915 l_right = l_right.where(pd.notna(l_right), False) 

3916 return np.where(g_left & l_right, props, "") 

3917 

3918 

3919def _highlight_value(data: DataFrame | Series, op: str, props: str) -> np.ndarray: 

3920 """ 

3921 Return an array of css strings based on the condition of values matching an op. 

3922 """ 

3923 value = getattr(data, op)(skipna=True) 

3924 if isinstance(data, DataFrame): # min/max must be done twice to return scalar 

3925 value = getattr(value, op)(skipna=True) 

3926 cond = data == value 

3927 cond = cond.where(pd.notna(cond), False) 

3928 return np.where(cond, props, "") 

3929 

3930 

3931def _bar( 

3932 data: NDFrame, 

3933 align: str | float | Callable, 

3934 colors: str | list | tuple, 

3935 cmap: Any, 

3936 width: float, 

3937 height: float, 

3938 vmin: float | None, 

3939 vmax: float | None, 

3940 base_css: str, 

3941): 

3942 """ 

3943 Draw bar chart in data cells using HTML CSS linear gradient. 

3944 

3945 Parameters 

3946 ---------- 

3947 data : Series or DataFrame 

3948 Underling subset of Styler data on which operations are performed. 

3949 align : str in {"left", "right", "mid", "zero", "mean"}, int, float, callable 

3950 Method for how bars are structured or scalar value of centre point. 

3951 colors : list-like of str 

3952 Two listed colors as string in valid CSS. 

3953 width : float in [0,1] 

3954 The percentage of the cell, measured from left, where drawn bars will reside. 

3955 height : float in [0,1] 

3956 The percentage of the cell's height where drawn bars will reside, centrally 

3957 aligned. 

3958 vmin : float, optional 

3959 Overwrite the minimum value of the window. 

3960 vmax : float, optional 

3961 Overwrite the maximum value of the window. 

3962 base_css : str 

3963 Additional CSS that is included in the cell before bars are drawn. 

3964 """ 

3965 

3966 def css_bar(start: float, end: float, color: str) -> str: 

3967 """ 

3968 Generate CSS code to draw a bar from start to end in a table cell. 

3969 

3970 Uses linear-gradient. 

3971 

3972 Parameters 

3973 ---------- 

3974 start : float 

3975 Relative positional start of bar coloring in [0,1] 

3976 end : float 

3977 Relative positional end of the bar coloring in [0,1] 

3978 color : str 

3979 CSS valid color to apply. 

3980 

3981 Returns 

3982 ------- 

3983 str : The CSS applicable to the cell. 

3984 

3985 Notes 

3986 ----- 

3987 Uses ``base_css`` from outer scope. 

3988 """ 

3989 cell_css = base_css 

3990 if end > start: 

3991 cell_css += "background: linear-gradient(90deg," 

3992 if start > 0: 

3993 cell_css += f" transparent {start*100:.1f}%, {color} {start*100:.1f}%," 

3994 cell_css += f" {color} {end*100:.1f}%, transparent {end*100:.1f}%)" 

3995 return cell_css 

3996 

3997 def css_calc(x, left: float, right: float, align: str, color: str | list | tuple): 

3998 """ 

3999 Return the correct CSS for bar placement based on calculated values. 

4000 

4001 Parameters 

4002 ---------- 

4003 x : float 

4004 Value which determines the bar placement. 

4005 left : float 

4006 Value marking the left side of calculation, usually minimum of data. 

4007 right : float 

4008 Value marking the right side of the calculation, usually maximum of data 

4009 (left < right). 

4010 align : {"left", "right", "zero", "mid"} 

4011 How the bars will be positioned. 

4012 "left", "right", "zero" can be used with any values for ``left``, ``right``. 

4013 "mid" can only be used where ``left <= 0`` and ``right >= 0``. 

4014 "zero" is used to specify a center when all values ``x``, ``left``, 

4015 ``right`` are translated, e.g. by say a mean or median. 

4016 

4017 Returns 

4018 ------- 

4019 str : Resultant CSS with linear gradient. 

4020 

4021 Notes 

4022 ----- 

4023 Uses ``colors``, ``width`` and ``height`` from outer scope. 

4024 """ 

4025 if pd.isna(x): 

4026 return base_css 

4027 

4028 if isinstance(color, (list, tuple)): 

4029 color = color[0] if x < 0 else color[1] 

4030 assert isinstance(color, str) # mypy redefinition 

4031 

4032 x = left if x < left else x 

4033 x = right if x > right else x # trim data if outside of the window 

4034 

4035 start: float = 0 

4036 end: float = 1 

4037 

4038 if align == "left": 

4039 # all proportions are measured from the left side between left and right 

4040 end = (x - left) / (right - left) 

4041 

4042 elif align == "right": 

4043 # all proportions are measured from the right side between left and right 

4044 start = (x - left) / (right - left) 

4045 

4046 else: 

4047 z_frac: float = 0.5 # location of zero based on the left-right range 

4048 if align == "zero": 

4049 # all proportions are measured from the center at zero 

4050 limit: float = max(abs(left), abs(right)) 

4051 left, right = -limit, limit 

4052 elif align == "mid": 

4053 # bars drawn from zero either leftwards or rightwards with center at mid 

4054 mid: float = (left + right) / 2 

4055 z_frac = ( 

4056 -mid / (right - left) + 0.5 if mid < 0 else -left / (right - left) 

4057 ) 

4058 

4059 if x < 0: 

4060 start, end = (x - left) / (right - left), z_frac 

4061 else: 

4062 start, end = z_frac, (x - left) / (right - left) 

4063 

4064 ret = css_bar(start * width, end * width, color) 

4065 if height < 1 and "background: linear-gradient(" in ret: 

4066 return ( 

4067 ret + f" no-repeat center; background-size: 100% {height * 100:.1f}%;" 

4068 ) 

4069 else: 

4070 return ret 

4071 

4072 values = data.to_numpy() 

4073 # A tricky way to address the issue where np.nanmin/np.nanmax fail to handle pd.NA. 

4074 left = np.nanmin(data.min(skipna=True)) if vmin is None else vmin 

4075 right = np.nanmax(data.max(skipna=True)) if vmax is None else vmax 

4076 z: float = 0 # adjustment to translate data 

4077 

4078 if align == "mid": 

4079 if left >= 0: # "mid" is documented to act as "left" if all values positive 

4080 align, left = "left", 0 if vmin is None else vmin 

4081 elif right <= 0: # "mid" is documented to act as "right" if all values negative 

4082 align, right = "right", 0 if vmax is None else vmax 

4083 elif align == "mean": 

4084 z, align = np.nanmean(values), "zero" 

4085 elif callable(align): 

4086 z, align = align(values), "zero" 

4087 elif isinstance(align, (float, int)): 

4088 z, align = float(align), "zero" 

4089 elif align not in ("left", "right", "zero"): 

4090 raise ValueError( 

4091 "`align` should be in {'left', 'right', 'mid', 'mean', 'zero'} or be a " 

4092 "value defining the center line or a callable that returns a float" 

4093 ) 

4094 

4095 rgbas = None 

4096 if cmap is not None: 

4097 # use the matplotlib colormap input 

4098 with _mpl(Styler.bar) as (_, _matplotlib): 

4099 cmap = ( 

4100 _matplotlib.colormaps[cmap] 

4101 if isinstance(cmap, str) 

4102 else cmap # assumed to be a Colormap instance as documented 

4103 ) 

4104 norm = _matplotlib.colors.Normalize(left, right) 

4105 rgbas = cmap(norm(values)) 

4106 if data.ndim == 1: 

4107 rgbas = [_matplotlib.colors.rgb2hex(rgba) for rgba in rgbas] 

4108 else: 

4109 rgbas = [ 

4110 [_matplotlib.colors.rgb2hex(rgba) for rgba in row] for row in rgbas 

4111 ] 

4112 

4113 assert isinstance(align, str) # mypy: should now be in [left, right, mid, zero] 

4114 if data.ndim == 1: 

4115 return [ 

4116 css_calc( 

4117 x - z, left - z, right - z, align, colors if rgbas is None else rgbas[i] 

4118 ) 

4119 for i, x in enumerate(values) 

4120 ] 

4121 else: 

4122 return np.array( 

4123 [ 

4124 [ 

4125 css_calc( 

4126 x - z, 

4127 left - z, 

4128 right - z, 

4129 align, 

4130 colors if rgbas is None else rgbas[i][j], 

4131 ) 

4132 for j, x in enumerate(row) 

4133 ] 

4134 for i, row in enumerate(values) 

4135 ] 

4136 )