"""
Module for applying conditional formatting to DataFrames and Series.
"""
from __future__ import annotations
from contextlib import contextmanager
import copy
from functools import partial
import operator
from typing import (
Any,
Callable,
Hashable,
Sequence,
)
import warnings
import numpy as np
from pandas._config import get_option
from pandas._typing import (
Axis,
FilePathOrBuffer,
FrameOrSeries,
FrameOrSeriesUnion,
IndexLabel,
Scalar,
)
from pandas.compat._optional import import_optional_dependency
from pandas.util._decorators import doc
import pandas as pd
from pandas import (
IndexSlice,
RangeIndex,
)
from pandas.api.types import is_list_like
from pandas.core import generic
import pandas.core.common as com
from pandas.core.frame import (
DataFrame,
Series,
)
from pandas.core.generic import NDFrame
from pandas.io.formats.format import save_to_buffer
jinja2 = import_optional_dependency("jinja2", extra="DataFrame.style requires jinja2.")
from pandas.io.formats.style_render import (
CSSProperties,
CSSStyles,
StylerRenderer,
Subset,
Tooltips,
maybe_convert_css_to_tuples,
non_reducing_slice,
)
try:
from matplotlib import colors
import matplotlib.pyplot as plt
has_mpl = True
except ImportError:
has_mpl = False
no_mpl_message = "{0} requires matplotlib."
@contextmanager
def _mpl(func: Callable):
if has_mpl:
yield plt, colors
else:
raise ImportError(no_mpl_message.format(func.__name__))
class Styler(StylerRenderer):
r"""
Helps style a DataFrame or Series according to the data with HTML and CSS.
Parameters
----------
data : Series or DataFrame
Data to be styled - either a Series or DataFrame.
precision : int
Precision to round floats to, defaults to pd.options.display.precision.
table_styles : list-like, default None
List of {selector: (attr, value)} dicts; see Notes.
uuid : str, default None
A unique identifier to avoid CSS collisions; generated automatically.
caption : str, tuple, default None
String caption to attach to the table. Tuple only used for LaTeX dual captions.
table_attributes : str, default None
Items that show up in the opening ``
`` tag
in addition to automatic (by default) id.
cell_ids : bool, default True
If True, each cell will have an ``id`` attribute in their HTML tag.
The ``id`` takes the form ``T__row_col``
where ```` is the unique identifier, ```` is the row
number and ```` is the column number.
na_rep : str, optional
Representation for missing values.
If ``na_rep`` is None, no special formatting is applied.
.. versionadded:: 1.0.0
uuid_len : int, default 5
If ``uuid`` is not specified, the length of the ``uuid`` to randomly generate
expressed in hex characters, in range [0, 32].
.. versionadded:: 1.2.0
decimal : str, default "."
Character used as decimal separator for floats, complex and integers
.. versionadded:: 1.3.0
thousands : str, optional, default None
Character used as thousands separator for floats, complex and integers
.. versionadded:: 1.3.0
escape : str, optional
Use 'html' to replace the characters ``&``, ``<``, ``>``, ``'``, and ``"``
in cell display string with HTML-safe sequences.
Use 'latex' to replace the characters ``&``, ``%``, ``$``, ``#``, ``_``,
``{``, ``}``, ``~``, ``^``, and ``\`` in the cell display string with
LaTeX-safe sequences.
.. versionadded:: 1.3.0
Attributes
----------
env : Jinja2 jinja2.Environment
template : Jinja2 Template
loader : Jinja2 Loader
See Also
--------
DataFrame.style : Return a Styler object containing methods for building
a styled HTML representation for the DataFrame.
Notes
-----
Most styling will be done by passing style functions into
``Styler.apply`` or ``Styler.applymap``. Style functions should
return values with strings containing CSS ``'attr: value'`` that will
be applied to the indicated cells.
If using in the Jupyter notebook, Styler has defined a ``_repr_html_``
to automatically render itself. Otherwise call Styler.render to get
the generated HTML.
CSS classes are attached to the generated HTML
* Index and Column names include ``index_name`` and ``level``
where `k` is its level in a MultiIndex
* Index label cells include
* ``row_heading``
* ``row`` where `n` is the numeric position of the row
* ``level`` where `k` is the level in a MultiIndex
* Column label cells include
* ``col_heading``
* ``col`` where `n` is the numeric position of the column
* ``level`` where `k` is the level in a MultiIndex
* Blank cells include ``blank``
* Data cells include ``data``
"""
def __init__(
self,
data: FrameOrSeriesUnion,
precision: int | None = None,
table_styles: CSSStyles | None = None,
uuid: str | None = None,
caption: str | tuple | None = None,
table_attributes: str | None = None,
cell_ids: bool = True,
na_rep: str | None = None,
uuid_len: int = 5,
decimal: str = ".",
thousands: str | None = None,
escape: str | None = None,
):
super().__init__(
data=data,
uuid=uuid,
uuid_len=uuid_len,
table_styles=table_styles,
table_attributes=table_attributes,
caption=caption,
cell_ids=cell_ids,
)
# validate ordered args
self.precision = precision # can be removed on set_precision depr cycle
self.na_rep = na_rep # can be removed on set_na_rep depr cycle
self.format(
formatter=None,
precision=precision,
na_rep=na_rep,
escape=escape,
decimal=decimal,
thousands=thousands,
)
def _repr_html_(self) -> str:
"""
Hooks into Jupyter notebook rich display system.
"""
return self.render()
def render(
self,
sparse_index: bool | None = None,
sparse_columns: bool | None = None,
**kwargs,
) -> str:
"""
Render the ``Styler`` including all applied styles to HTML.
Parameters
----------
sparse_index : bool, optional
Whether to sparsify the display of a hierarchical index. Setting to False
will display each explicit level element in a hierarchical key for each row.
Defaults to ``pandas.options.styler.sparse.index`` value.
sparse_columns : bool, optional
Whether to sparsify the display of a hierarchical index. Setting to False
will display each explicit level element in a hierarchical key for each row.
Defaults to ``pandas.options.styler.sparse.columns`` value.
**kwargs
Any additional keyword arguments are passed
through to ``self.template.render``.
This is useful when you need to provide
additional variables for a custom template.
Returns
-------
rendered : str
The rendered HTML.
Notes
-----
Styler objects have defined the ``_repr_html_`` method
which automatically calls ``self.render()`` when it's the
last item in a Notebook cell. When calling ``Styler.render()``
directly, wrap the result in ``IPython.display.HTML`` to view
the rendered HTML in the notebook.
Pandas uses the following keys in render. Arguments passed
in ``**kwargs`` take precedence, so think carefully if you want
to override them:
* head
* cellstyle
* body
* uuid
* table_styles
* caption
* table_attributes
"""
if sparse_index is None:
sparse_index = get_option("styler.sparse.index")
if sparse_columns is None:
sparse_columns = get_option("styler.sparse.columns")
return self._render_html(sparse_index, sparse_columns, **kwargs)
def set_tooltips(
self,
ttips: DataFrame,
props: CSSProperties | None = None,
css_class: str | None = None,
) -> Styler:
"""
Set the DataFrame of strings on ``Styler`` generating ``:hover`` tooltips.
These string based tooltips are only applicable to ``
`` HTML elements,
and cannot be used for column or index headers.
.. versionadded:: 1.3.0
Parameters
----------
ttips : DataFrame
DataFrame containing strings that will be translated to tooltips, mapped
by identical column and index values that must exist on the underlying
Styler data. None, NaN values, and empty strings will be ignored and
not affect the rendered HTML.
props : list-like or str, optional
List of (attr, value) tuples or a valid CSS string. If ``None`` adopts
the internal default values described in notes.
css_class : str, optional
Name of the tooltip class used in CSS, should conform to HTML standards.
Only useful if integrating tooltips with external CSS. If ``None`` uses the
internal default value 'pd-t'.
Returns
-------
self : Styler
Notes
-----
Tooltips are created by adding `` to each data cell
and then manipulating the table level CSS to attach pseudo hover and pseudo
after selectors to produce the required the results.
The default properties for the tooltip CSS class are:
- visibility: hidden
- position: absolute
- z-index: 1
- background-color: black
- color: white
- transform: translate(-20px, -20px)
The property 'visibility: hidden;' is a key prerequisite to the hover
functionality, and should always be included in any manual properties
specification, using the ``props`` argument.
Tooltips are not designed to be efficient, and can add large amounts of
additional HTML for larger tables, since they also require that ``cell_ids``
is forced to `True`.
Examples
--------
Basic application
>>> df = pd.DataFrame(data=[[0, 1], [2, 3]])
>>> ttips = pd.DataFrame(
... data=[["Min", ""], [np.nan, "Max"]], columns=df.columns, index=df.index
... )
>>> s = df.style.set_tooltips(ttips).render()
Optionally controlling the tooltip visual display
>>> df.style.set_tooltips(ttips, css_class='tt-add', props=[
... ('visibility', 'hidden'),
... ('position', 'absolute'),
... ('z-index', 1)])
>>> df.style.set_tooltips(ttips, css_class='tt-add',
... props='visibility:hidden; position:absolute; z-index:1;')
"""
if not self.cell_ids:
# tooltips not optimised for individual cell check. requires reasonable
# redesign and more extensive code for a feature that might be rarely used.
raise NotImplementedError(
"Tooltips can only render with 'cell_ids' is True."
)
if not ttips.index.is_unique or not ttips.columns.is_unique:
raise KeyError(
"Tooltips render only if `ttips` has unique index and columns."
)
if self.tooltips is None: # create a default instance if necessary
self.tooltips = Tooltips()
self.tooltips.tt_data = ttips
if props:
self.tooltips.class_properties = props
if css_class:
self.tooltips.class_name = css_class
return self
@doc(
NDFrame.to_excel,
klass="Styler",
storage_options=generic._shared_docs["storage_options"],
)
def to_excel(
self,
excel_writer,
sheet_name: str = "Sheet1",
na_rep: str = "",
float_format: str | None = None,
columns: Sequence[Hashable] | None = None,
header: Sequence[Hashable] | bool = True,
index: bool = True,
index_label: IndexLabel | None = None,
startrow: int = 0,
startcol: int = 0,
engine: str | None = None,
merge_cells: bool = True,
encoding: str | None = None,
inf_rep: str = "inf",
verbose: bool = True,
freeze_panes: tuple[int, int] | None = None,
) -> None:
from pandas.io.formats.excel import ExcelFormatter
formatter = ExcelFormatter(
self,
na_rep=na_rep,
cols=columns,
header=header,
float_format=float_format,
index=index,
index_label=index_label,
merge_cells=merge_cells,
inf_rep=inf_rep,
)
formatter.write(
excel_writer,
sheet_name=sheet_name,
startrow=startrow,
startcol=startcol,
freeze_panes=freeze_panes,
engine=engine,
)
def to_latex(
self,
buf: FilePathOrBuffer[str] | None = None,
*,
column_format: str | None = None,
position: str | None = None,
position_float: str | None = None,
hrules: bool = False,
label: str | None = None,
caption: str | tuple | None = None,
sparse_index: bool | None = None,
sparse_columns: bool | None = None,
multirow_align: str = "c",
multicol_align: str = "r",
siunitx: bool = False,
encoding: str | None = None,
convert_css: bool = False,
):
r"""
Write Styler to a file, buffer or string in LaTeX format.
.. versionadded:: 1.3.0
Parameters
----------
buf : str, Path, or StringIO-like, optional, default None
Buffer to write to. If ``None``, the output is returned as a string.
column_format : str, optional
The LaTeX column specification placed in location:
\\begin{tabular}{}
Defaults to 'l' for index and
non-numeric data columns, and, for numeric data columns,
to 'r' by default, or 'S' if ``siunitx`` is ``True``.
position : str, optional
The LaTeX positional argument (e.g. 'h!') for tables, placed in location:
\\begin{table}[]
position_float : {"centering", "raggedleft", "raggedright"}, optional
The LaTeX float command placed in location:
\\begin{table}[]
\\
hrules : bool, default False
Set to `True` to add \\toprule, \\midrule and \\bottomrule from the
{booktabs} LaTeX package.
label : str, optional
The LaTeX label included as: \\label{