|
21 | 21 | import re |
22 | 22 | import warnings |
23 | 23 | from datetime import date, datetime |
| 24 | +import math |
24 | 25 | from math import prod |
25 | 26 | from typing import TYPE_CHECKING, Any, Literal, TypedDict, cast |
26 | 27 |
|
@@ -2325,6 +2326,16 @@ def st_pivot_table( |
2325 | 2326 | Each rule is a dict with ``"type"`` (``"color_scale"``, |
2326 | 2327 | ``"data_bars"``, or ``"threshold"``), ``"apply_to"`` (list of |
2327 | 2328 | field names, empty = all), and type-specific keys. |
| 2329 | + ``color_scale`` rules accept optional ``"mid_color"`` and |
| 2330 | + ``"mid_value"`` keys. When ``"mid_value"`` is provided (a finite |
| 2331 | + number, and only valid alongside ``"mid_color"``), the gradient |
| 2332 | + is anchored at that midpoint for a smooth Excel-like diverging |
| 2333 | + scale (for example, ``mid_value=0`` for PnL columns). ``mid_value`` |
| 2334 | + is interpreted in the same numeric space as the underlying |
| 2335 | + aggregated cell values (the raw ``agg.value()`` that feeds all |
| 2336 | + conditional formatting rules), not as a post-``show_values_as`` |
| 2337 | + display value. Values outside the observed column range clamp |
| 2338 | + to the endpoint colors. |
2328 | 2339 | number_format : str or dict[str, str] or None |
2329 | 2340 | Number format pattern(s). A single string applies to all |
2330 | 2341 | value fields; a dict maps field names to patterns. Use |
@@ -2754,6 +2765,26 @@ def st_pivot_table( |
2754 | 2765 | raise ValueError( |
2755 | 2766 | f"conditional_formatting[{i}]: color_scale requires 'min_color' and 'max_color'" |
2756 | 2767 | ) |
| 2768 | + if "mid_color" in rule and not isinstance( |
| 2769 | + rule.get("mid_color", ""), str |
| 2770 | + ): |
| 2771 | + raise TypeError( |
| 2772 | + f"conditional_formatting[{i}]['mid_color'] must be a string" |
| 2773 | + ) |
| 2774 | + if "mid_value" in rule and rule["mid_value"] is not None: |
| 2775 | + mv = rule["mid_value"] |
| 2776 | + if not rule.get("mid_color"): |
| 2777 | + raise ValueError( |
| 2778 | + f"conditional_formatting[{i}]: 'mid_value' requires 'mid_color'" |
| 2779 | + ) |
| 2780 | + if ( |
| 2781 | + isinstance(mv, bool) |
| 2782 | + or not isinstance(mv, (int, float)) |
| 2783 | + or not math.isfinite(mv) |
| 2784 | + ): |
| 2785 | + raise TypeError( |
| 2786 | + f"conditional_formatting[{i}]['mid_value'] must be a finite number" |
| 2787 | + ) |
2757 | 2788 | elif rtype == "threshold": |
2758 | 2789 | conditions = rule.get("conditions") |
2759 | 2790 | if not isinstance(conditions, list) or len(conditions) == 0: |
|
0 commit comments