diff --git a/ci/code_checks.sh b/ci/code_checks.sh index f481ecf7a97ed..d04d0eaee6ec4 100755 --- a/ci/code_checks.sh +++ b/ci/code_checks.sh @@ -122,6 +122,7 @@ if [[ -z "$CHECK" || "$CHECK" == "doctests" ]]; then pandas/io/sas/ \ pandas/io/sql.py \ pandas/io/formats/format.py \ + pandas/io/formats/style.py \ pandas/tseries/ RET=$(($RET + $?)) ; echo $MSG "DONE" diff --git a/doc/source/_static/style/df_pipe.png b/doc/source/_static/style/df_pipe.png new file mode 100644 index 0000000000000..071a481ad5acc Binary files /dev/null and b/doc/source/_static/style/df_pipe.png differ diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index af1c0ca34ec0f..3d6705ed593d2 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -341,9 +341,10 @@ def set_tooltips( >>> df.style.set_tooltips(ttips, css_class='tt-add', props=[ ... ('visibility', 'hidden'), ... ('position', 'absolute'), - ... ('z-index', 1)]) + ... ('z-index', 1)]) # doctest: +SKIP >>> df.style.set_tooltips(ttips, css_class='tt-add', ... props='visibility:hidden; position:absolute; z-index:1;') + ... # doctest: +SKIP """ if not self.cell_ids: # tooltips not optimised for individual cell check. requires reasonable @@ -553,7 +554,7 @@ def to_latex( >>> s = df.style.highlight_max(axis=None, ... props='cellcolor:{red}; bfseries: ;') - >>> s.to_latex() + >>> s.to_latex() # doctest: +SKIP Internally these structured LaTeX ``(, )`` pairs are translated to the @@ -592,7 +593,7 @@ def to_latex( ... props='cellcolor:[HTML]{FFFF00}; color:{red};' ... 'textit:--rwrap; textbf:--rwrap;' ... ) - >>> s.to_latex() + >>> s.to_latex() # doctest: +SKIP .. figure:: ../../_static/style/latex_1.png @@ -653,7 +654,7 @@ def to_latex( ... column_format="rrrrr", position="h", position_float="centering", ... hrules=True, label="table:5", caption="Styled LaTeX Table", ... multirow_align="t", multicol_align="r" - ... ) + ... ) # doctest: +SKIP .. figure:: ../../_static/style/latex_2.png @@ -670,8 +671,14 @@ def to_latex( ... ("Numeric", "Integers"): '\${}', ... ("Numeric", "Floats"): '{:.3f}', ... ("Non-Numeric", "Strings"): str.upper - ... }) - >>> s.to_latex() + ... }) # doctest: +SKIP + Numeric Non-Numeric + Integers Floats Strings + L0 ix1 $1 2.200 DOGS + ix2 $3 4.400 CATS + L1 ix3 $2 6.600 COWS + + >>> s.to_latex() # doctest: +SKIP \begin{tabular}{llrrl} {} & {} & \multicolumn{2}{r}{Numeric} & {Non-Numeric} \\ {} & {} & {Integers} & {Floats} & {Strings} \\ @@ -713,7 +720,7 @@ def to_latex( >>> df = pd.DataFrame([[1]]) >>> df.style.set_properties( ... **{"font-weight": "bold /* --dwrap */", "Huge": "--latex--rwrap"} - ... ).to_latex(convert_css=True) + ... ).to_latex(convert_css=True) # doctest: +SKIP \begin{tabular}{lr} {} & {0} \\ 0 & {\bfseries}{\Huge{1}} \\ @@ -934,7 +941,7 @@ def set_td_classes(self, classes: DataFrame) -> Styler: ... ["min-val red", "", "blue"], ... ["red", None, "blue max-val"] ... ], index=df.index, columns=df.columns) - >>> df.style.set_td_classes(classes) + >>> df.style.set_td_classes(classes) # doctest: +SKIP Using `MultiIndex` columns and a `classes` `DataFrame` as a subset of the underlying, @@ -943,14 +950,14 @@ def set_td_classes(self, classes: DataFrame) -> Styler: ... columns=[["level0", "level0"], ["level1a", "level1b"]]) >>> classes = pd.DataFrame(["min-val"], index=["a"], ... columns=[["level0"],["level1a"]]) - >>> df.style.set_td_classes(classes) + >>> df.style.set_td_classes(classes) # doctest: +SKIP Form of the output with new additional css classes, >>> df = pd.DataFrame([[1]]) >>> css = pd.DataFrame([["other-class"]]) >>> s = Styler(df, uuid="_", cell_ids=False).set_td_classes(css) - >>> s.hide_index().render() + >>> s.hide_index().render() # doctest: +SKIP '' '' ' ' @@ -1178,19 +1185,26 @@ def apply( >>> def highlight_max(x, color): ... return np.where(x == np.nanmax(x.to_numpy()), f"color: {color};", None) >>> df = pd.DataFrame(np.random.randn(5, 2), columns=["A", "B"]) - >>> df.style.apply(highlight_max, color='red') - >>> df.style.apply(highlight_max, color='blue', axis=1) - >>> df.style.apply(highlight_max, color='green', axis=None) + >>> df.style.apply(highlight_max, color='red') # doctest: +SKIP + >>> df.style.apply(highlight_max, color='blue', axis=1) # doctest: +SKIP + >>> df.style.apply(highlight_max, color='green', axis=None) # doctest: +SKIP Using ``subset`` to restrict application to a single column or multiple columns >>> df.style.apply(highlight_max, color='red', subset="A") + ... # doctest: +SKIP >>> df.style.apply(highlight_max, color='red', subset=["A", "B"]) + ... # doctest: +SKIP Using a 2d input to ``subset`` to select rows in addition to columns - >>> df.style.apply(highlight_max, color='red', subset=([0,1,2], slice(None)) - >>> df.style.apply(highlight_max, color='red', subset=(slice(0,5,2), "A") + >>> df.style.apply(highlight_max, color='red', subset=([0,1,2], slice(None))) + ... # doctest: +SKIP + >>> df.style.apply(highlight_max, color='red', subset=(slice(0,5,2), "A")) + ... # doctest: +SKIP + + See `Table Visualization <../../user_guide/style.ipynb>`_ user guide for + more details. """ self._todo.append( (lambda instance: getattr(instance, "_apply"), (func, axis, subset), kwargs) @@ -1246,17 +1260,24 @@ def applymap( >>> def color_negative(v, color): ... return f"color: {color};" if v < 0 else None >>> df = pd.DataFrame(np.random.randn(5, 2), columns=["A", "B"]) - >>> df.style.applymap(color_negative, color='red') + >>> df.style.applymap(color_negative, color='red') # doctest: +SKIP Using ``subset`` to restrict application to a single column or multiple columns >>> df.style.applymap(color_negative, color='red', subset="A") + ... # doctest: +SKIP >>> df.style.applymap(color_negative, color='red', subset=["A", "B"]) + ... # doctest: +SKIP Using a 2d input to ``subset`` to select rows in addition to columns - >>> df.style.applymap(color_negative, color='red', subset=([0,1,2], slice(None)) - >>> df.style.applymap(color_negative, color='red', subset=(slice(0,5,2), "A") + >>> df.style.applymap(color_negative, color='red', + ... subset=([0,1,2], slice(None))) # doctest: +SKIP + >>> df.style.applymap(color_negative, color='red', subset=(slice(0,5,2), "A")) + ... # doctest: +SKIP + + See `Table Visualization <../../user_guide/style.ipynb>`_ user guide for + more details. """ self._todo.append( (lambda instance: getattr(instance, "_applymap"), (func, subset), kwargs) @@ -1317,6 +1338,7 @@ def where( >>> def cond(v, limit=4): ... return v > 1 and v != limit >>> df.style.where(cond, value='color:green;', other='color:red;') + ... # doctest: +SKIP should be refactored to: @@ -1324,6 +1346,7 @@ def where( ... cond = v > 1 and v != limit ... return value if cond else other >>> df.style.applymap(style_func, value='color:green;', other='color:red;') + ... # doctest: +SKIP """ warnings.warn( "this method is deprecated in favour of `Styler.applymap()`", @@ -1389,7 +1412,7 @@ def set_table_attributes(self, attributes: str) -> Styler: Examples -------- >>> df = pd.DataFrame(np.random.randn(10, 4)) - >>> df.style.set_table_attributes('class="pure-table"') + >>> df.style.set_table_attributes('class="pure-table"') # doctest: +SKIP # ...
... """ self.table_attributes = attributes @@ -1637,14 +1660,14 @@ def set_table_styles( >>> df.style.set_table_styles( ... [{'selector': 'tr:hover', ... 'props': [('background-color', 'yellow')]}] - ... ) + ... ) # doctest: +SKIP Or with CSS strings >>> df.style.set_table_styles( ... [{'selector': 'tr:hover', - ... 'props': 'background-color: yellow; font-size: 1em;']}] - ... ) + ... 'props': 'background-color: yellow; font-size: 1em;'}] + ... ) # doctest: +SKIP Adding column styling by name @@ -1652,15 +1675,18 @@ def set_table_styles( ... 'A': [{'selector': '', ... 'props': [('color', 'red')]}], ... 'B': [{'selector': 'td', - ... 'props': 'color: blue;']}] - ... }, overwrite=False) + ... 'props': 'color: blue;'}] + ... }, overwrite=False) # doctest: +SKIP Adding row styling >>> df.style.set_table_styles({ ... 0: [{'selector': 'td:hover', ... 'props': [('font-size', '25px')]}] - ... }, axis=1, overwrite=False) + ... }, axis=1, overwrite=False) # doctest: +SKIP + + See `Table Visualization <../../user_guide/style.ipynb>`_ user guide for + more details. """ if isinstance(table_styles, dict): if axis in [0, "index"]: @@ -1753,7 +1779,7 @@ def hide_index(self, subset: Subset | None = None) -> Styler: Simple application hiding specific rows: >>> df = pd.DataFrame([[1,2], [3,4], [5,6]], index=["a", "b", "c"]) - >>> df.style.hide_index(["a", "b"]) + >>> df.style.hide_index(["a", "b"]) # doctest: +SKIP 0 1 c 5 6 @@ -1761,7 +1787,7 @@ def hide_index(self, subset: Subset | None = None) -> Styler: >>> midx = pd.MultiIndex.from_product([["x", "y"], ["a", "b", "c"]]) >>> df = pd.DataFrame(np.random.randn(6,6), index=midx, columns=midx) - >>> df.style.format("{:.1f}").hide_index() + >>> df.style.format("{:.1f}").hide_index() # doctest: +SKIP x y a b c a b c 0.1 0.0 0.4 1.3 0.6 -1.4 @@ -1774,6 +1800,7 @@ def hide_index(self, subset: Subset | None = None) -> Styler: Hide specific rows but retain the index: >>> df.style.format("{:.1f}").hide_index(subset=(slice(None), ["a", "c"])) + ... # doctest: +SKIP x y a b c a b c x b 0.7 1.0 1.3 1.5 -0.0 -0.2 @@ -1781,8 +1808,8 @@ def hide_index(self, subset: Subset | None = None) -> Styler: Hide specific rows and the index: - >>> df.style.format("{:.1f}").hide_index(subset=(slice(None), ["a", "c"])) - ... .hide_index() + >>> df.style.format("{:.1f}").hide_index( + ... subset=(slice(None), ["a", "c"])).hide_index() # doctest: +SKIP x y a b c a b c 0.7 1.0 1.3 1.5 -0.0 -0.2 @@ -1833,7 +1860,7 @@ def hide_columns(self, subset: Subset | None = None) -> Styler: Simple application hiding specific columns: >>> df = pd.DataFrame([[1, 2, 3], [4, 5, 6]], columns=["a", "b", "c"]) - >>> df.style.hide_columns(["a", "b"]) + >>> df.style.hide_columns(["a", "b"]) # doctest: +SKIP c 0 3 1 6 @@ -1842,17 +1869,18 @@ def hide_columns(self, subset: Subset | None = None) -> Styler: >>> midx = pd.MultiIndex.from_product([["x", "y"], ["a", "b", "c"]]) >>> df = pd.DataFrame(np.random.randn(6,6), index=midx, columns=midx) - >>> df.style.format("{:.1f}").hide_columns() - x d 0.1 0.0 0.4 1.3 0.6 -1.4 - e 0.7 1.0 1.3 1.5 -0.0 -0.2 - f 1.4 -0.8 1.6 -0.2 -0.4 -0.3 - y d 0.4 1.0 -0.2 -0.8 -1.2 1.1 - e -0.6 1.2 1.8 1.9 0.3 0.3 - f 0.8 0.5 -0.3 1.2 2.2 -0.8 + >>> df.style.format("{:.1f}").hide_columns() # doctest: +SKIP + x a 0.1 0.0 0.4 1.3 0.6 -1.4 + b 0.7 1.0 1.3 1.5 -0.0 -0.2 + c 1.4 -0.8 1.6 -0.2 -0.4 -0.3 + y a 0.4 1.0 -0.2 -0.8 -1.2 1.1 + b -0.6 1.2 1.8 1.9 0.3 0.3 + c 0.8 0.5 -0.3 1.2 2.2 -0.8 Hide specific columns but retain the column headers: >>> df.style.format("{:.1f}").hide_columns(subset=(slice(None), ["a", "c"])) + ... # doctest: +SKIP x y b b x a 0.0 0.6 @@ -1864,8 +1892,8 @@ def hide_columns(self, subset: Subset | None = None) -> Styler: Hide specific columns and the column headers: - >>> df.style.format("{:.1f}").hide_columns(subset=(slice(None), ["a", "c"])) - ... .hide_columns() + >>> df.style.format("{:.1f}").hide_columns( + ... subset=(slice(None), ["a", "c"])).hide_columns() # doctest: +SKIP x a 0.0 0.6 b 1.0 -0.0 c -0.8 -0.4 @@ -1995,31 +2023,32 @@ def background_gradient( Shading the values column-wise, with ``axis=0``, preselecting numeric columns - >>> df.style.{name}_gradient(axis=0) + >>> df.style.{name}_gradient(axis=0) # doctest: +SKIP .. figure:: ../../_static/style/{image_prefix}_ax0.png Shading all values collectively using ``axis=None`` - >>> df.style.{name}_gradient(axis=None) + >>> df.style.{name}_gradient(axis=None) # doctest: +SKIP .. figure:: ../../_static/style/{image_prefix}_axNone.png Compress the color map from the both ``low`` and ``high`` ends - >>> df.style.{name}_gradient(axis=None, low=0.75, high=1.0) + >>> df.style.{name}_gradient(axis=None, low=0.75, high=1.0) # doctest: +SKIP .. figure:: ../../_static/style/{image_prefix}_axNone_lowhigh.png Manually setting ``vmin`` and ``vmax`` gradient thresholds - >>> df.style.{name}_gradient(axis=None, vmin=6.7, vmax=21.6) + >>> df.style.{name}_gradient(axis=None, vmin=6.7, vmax=21.6) # doctest: +SKIP .. figure:: ../../_static/style/{image_prefix}_axNone_vminvmax.png Setting a ``gmap`` and applying to all columns with another ``cmap`` >>> df.style.{name}_gradient(axis=0, gmap=df['Temp (c)'], cmap='YlOrRd') + ... # doctest: +SKIP .. figure:: ../../_static/style/{image_prefix}_gmap.png @@ -2029,7 +2058,7 @@ def background_gradient( >>> gmap = np.array([[1,2,3], [2,3,4], [3,4,5]]) >>> df.style.{name}_gradient(axis=None, gmap=gmap, ... cmap='YlOrRd', subset=['Temp (c)', 'Rain (mm)', 'Wind (m/s)'] - ... ) + ... ) # doctest: +SKIP .. figure:: ../../_static/style/{image_prefix}_axNone_gmap.png """ @@ -2111,8 +2140,11 @@ def set_properties(self, subset: Subset | None = None, **kwargs) -> Styler: Examples -------- >>> df = pd.DataFrame(np.random.randn(10, 4)) - >>> df.style.set_properties(color="white", align="right") - >>> df.style.set_properties(**{'background-color': 'yellow'}) + >>> df.style.set_properties(color="white", align="right") # doctest: +SKIP + >>> df.style.set_properties(**{'background-color': 'yellow'}) # doctest: +SKIP + + See `Table Visualization <../../user_guide/style.ipynb>`_ user guide for + more details. """ values = "".join([f"{p}: {v};" for p, v in kwargs.items()]) return self.applymap(lambda x: values, subset=subset) @@ -2447,7 +2479,7 @@ def highlight_between( ... 'Two': [2.9, 2.1, 2.5], ... 'Three': [3.1, 3.2, 3.8], ... }) - >>> df.style.highlight_between(left=2.1, right=2.9) + >>> df.style.highlight_between(left=2.1, right=2.9) # doctest: +SKIP .. figure:: ../../_static/style/hbetw_basic.png @@ -2455,7 +2487,7 @@ def highlight_between( and ``right`` for each column individually >>> df.style.highlight_between(left=[1.4, 2.4, 3.4], right=[1.6, 2.6, 3.6], - ... axis=1, color="#fffd75") + ... axis=1, color="#fffd75") # doctest: +SKIP .. figure:: ../../_static/style/hbetw_seq.png @@ -2463,14 +2495,14 @@ def highlight_between( matches the input DataFrame, with a constant ``right`` >>> df.style.highlight_between(left=[[2,2,3],[2,2,3],[3,3,3]], right=3.5, - ... axis=None, color="#fffd75") + ... axis=None, color="#fffd75") # doctest: +SKIP .. figure:: ../../_static/style/hbetw_axNone.png Using ``props`` instead of default background coloring >>> df.style.highlight_between(left=1.5, right=3.5, - ... props='font-weight:bold;color:#e83e8c') + ... props='font-weight:bold;color:#e83e8c') # doctest: +SKIP .. figure:: ../../_static/style/hbetw_props.png """ @@ -2547,19 +2579,21 @@ def highlight_quantile( >>> df = pd.DataFrame(np.arange(10).reshape(2,5) + 1) >>> df.style.highlight_quantile(axis=None, q_left=0.8, color="#fffd75") + ... # doctest: +SKIP .. figure:: ../../_static/style/hq_axNone.png Or highlight quantiles row-wise or column-wise, in this case by row-wise >>> df.style.highlight_quantile(axis=1, q_left=0.8, color="#fffd75") + ... # doctest: +SKIP .. figure:: ../../_static/style/hq_ax1.png Use ``props`` instead of default background coloring >>> df.style.highlight_quantile(axis=None, q_left=0.2, q_right=0.8, - ... props='font-weight:bold;color:#e83e8c') + ... props='font-weight:bold;color:#e83e8c') # doctest: +SKIP .. figure:: ../../_static/style/hq_props.png """ @@ -2703,6 +2737,9 @@ def pipe(self, func: Callable, *args, **kwargs): ... .highlight_min(subset=['conversion'], color='yellow') ... .pipe(format_conversion) ... .set_caption("Results with minimum conversion highlighted.")) + ... # doctest: +SKIP + + .. figure:: ../../_static/style/df_pipe.png """ return com.pipe(self, func, *args, **kwargs)