Skip to content

Commit be8066b

Browse files
committed
update table colors and styling
1 parent ddb008c commit be8066b

File tree

4 files changed

+231
-16
lines changed

4 files changed

+231
-16
lines changed

doc/index.qmd

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,26 @@ $$
2828
for a nominal coverage level is $1-\alpha$.
2929
The corresponding coverage results are highlighted according to the following color scheme:
3030

31-
* <span style="background-color: #00FF00; color: black; padding: 2px 5px; border-radius: 3px;">Green</span> if the deviation to the nominal level is below $5\%$
32-
* <span style="background-color: #FFFF00; color: black; padding: 2px 5px; border-radius: 3px;">Yellow</span> if the deviation to the nominal level is above $5\%$ and below $10\%$
33-
* <span style="background-color: #FF0000; color: black; padding: 2px 5px; border-radius: 3px;">Red</span> if the deviation to the nominal level is above $10\%$
31+
```{python}
32+
#| echo: false
33+
#| output: asis
34+
from utils.styling import get_html_color_span
35+
36+
# Generate color legend using centralized configuration
37+
good_span = get_html_color_span("good")
38+
medium_span = get_html_color_span("medium")
39+
poor_span = get_html_color_span("poor")
40+
41+
from IPython.display import Markdown, display
42+
43+
markdown_output = f"""
44+
* {good_span} if the deviation to the nominal level is below 5%
45+
* {medium_span} if the deviation to the nominal level is above 5% and below 10%
46+
* {poor_span} if the deviation to the nominal level is above 10%
47+
"""
48+
49+
display(Markdown(markdown_output))
50+
```
3451

3552
For simulations with multiple parameters of interest, usually pointwise and uniform coverage is assessed.
3653

@@ -247,5 +264,3 @@ fig.show()
247264
```
248265

249266
:::
250-
251-
:::

doc/utils/style_tables.py

Lines changed: 90 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,21 @@
33
from pandas.io.formats.style import Styler
44
from typing import Union, Optional, List, Any
55
from itables import show
6+
from .styling import (
7+
TABLE_STYLING,
8+
COVERAGE_THRESHOLDS,
9+
get_coverage_tier_props,
10+
)
611

712

8-
# Define highlighting tiers as a list of dictionaries or tuples
9-
# Each element defines: dist, props. Applied in order (later rules can override).
10-
# Order: from least specific (largest dist) to most specific (smallest dist)
11-
# or ensure the _apply_highlight_range logic correctly handles overlaps if props are different.
12-
# Current logic: more specific (smaller dist) rules are applied last and override.
13+
# Define highlighting tiers using centralized color configuration
1314
HIGHLIGHT_TIERS = [
14-
{"dist": 1.0, "props": "color:black;background-color:red;"},
15-
{"dist": 0.1, "props": "color:black;background-color:yellow;"},
16-
{"dist": 0.05, "props": "color:white;background-color:darkgreen;"},
15+
{"dist": COVERAGE_THRESHOLDS["poor"], "props": get_coverage_tier_props("poor")},
16+
{
17+
"dist": COVERAGE_THRESHOLDS["medium"],
18+
"props": get_coverage_tier_props("medium", "500"),
19+
},
20+
{"dist": COVERAGE_THRESHOLDS["good"], "props": get_coverage_tier_props("good")},
1721
]
1822

1923

@@ -34,6 +38,77 @@ def _apply_highlight_range(
3438
return np.where(condition, props, "")
3539

3640

41+
def _apply_base_table_styling(styler: Styler) -> Styler:
42+
"""
43+
Apply base styling to the table including headers, borders, and overall appearance.
44+
"""
45+
# Define CSS styles for clean table appearance using centralized colors
46+
styles = [
47+
# Table-wide styling
48+
{
49+
"selector": "table",
50+
"props": [
51+
("border-collapse", "separate"),
52+
("border-spacing", "0"),
53+
("width", "100%"),
54+
(
55+
"font-family",
56+
'"Segoe UI", -apple-system, BlinkMacSystemFont, "Roboto", sans-serif',
57+
),
58+
("font-size", "14px"),
59+
("line-height", "1.5"),
60+
("box-shadow", "0 2px 8px rgba(0,0,0,0.1)"),
61+
("border-radius", "8px"),
62+
("overflow", "hidden"),
63+
],
64+
},
65+
# Header styling
66+
{
67+
"selector": "thead th",
68+
"props": [
69+
("background-color", TABLE_STYLING["header_bg"]),
70+
("color", TABLE_STYLING["header_text"]),
71+
("font-weight", "600"),
72+
("text-align", "center"),
73+
("padding", "12px 16px"),
74+
("border-bottom", f'2px solid {TABLE_STYLING["border"]}'),
75+
("position", "sticky"),
76+
("top", "0"),
77+
("z-index", "10"),
78+
],
79+
},
80+
# Cell styling
81+
{
82+
"selector": "tbody td",
83+
"props": [
84+
("padding", "10px 16px"),
85+
("text-align", "center"),
86+
("border-bottom", f'1px solid {TABLE_STYLING["border"]}'),
87+
("transition", "background-color 0.2s ease"),
88+
],
89+
},
90+
# Row hover effect
91+
{
92+
"selector": "tbody tr:hover td",
93+
"props": [("background-color", TABLE_STYLING["hover_bg"])],
94+
},
95+
# Caption styling
96+
{
97+
"selector": "caption",
98+
"props": [
99+
("color", TABLE_STYLING["caption_color"]),
100+
("font-size", "16px"),
101+
("font-weight", "600"),
102+
("margin-bottom", "16px"),
103+
("text-align", "left"),
104+
("caption-side", "top"),
105+
],
106+
},
107+
]
108+
109+
return styler.set_table_styles(styles)
110+
111+
37112
def color_coverage_columns(
38113
styler: Styler, level: float, coverage_cols: list[str] = ["Coverage"]
39114
) -> Styler:
@@ -54,13 +129,15 @@ def color_coverage_columns(
54129
if not valid_coverage_cols:
55130
return styler # No valid columns to style
56131

132+
# Apply base styling first
133+
current_styler = _apply_base_table_styling(styler)
134+
57135
# Apply highlighting rules from the defined tiers
58136
# The order in HIGHLIGHT_TIERS is important if props are meant to override.
59137
# Pandas Styler.apply applies styles sequentially. If a cell matches multiple
60138
# conditions from different .apply calls, the styles from later calls typically override
61139
# or merge with earlier ones, depending on the CSS properties.
62140
# For background-color, later calls will override.
63-
current_styler = styler
64141
for tier in HIGHLIGHT_TIERS:
65142
current_styler = current_styler.apply(
66143
_apply_highlight_range,
@@ -70,10 +147,12 @@ def color_coverage_columns(
70147
subset=valid_coverage_cols,
71148
)
72149

73-
# Set font to bold for the coverage columns
150+
# Apply additional styling to coverage columns for emphasis
74151
current_styler = current_styler.set_properties(
75-
**{"font-weight": "bold"}, subset=valid_coverage_cols
152+
**{"text-align": "center", "font-family": "monospace", "font-size": "13px"},
153+
subset=valid_coverage_cols,
76154
)
155+
77156
return current_styler
78157

79158

doc/utils/styling.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
"""
2+
Styling utilities for DoubleML Coverage tables and documentation.
3+
4+
This module provides helper functions for applying consistent styling
5+
based on the centralized theme configuration.
6+
"""
7+
8+
import yaml
9+
from pathlib import Path
10+
from typing import Dict, Any
11+
12+
13+
def _load_theme_config() -> Dict[str, Any]:
14+
"""Load theme configuration from YAML file."""
15+
config_path = Path(__file__).parent / "theme.yml"
16+
with open(config_path, "r") as f:
17+
return yaml.safe_load(f)
18+
19+
20+
# Load configuration once at module import
21+
_THEME = _load_theme_config()
22+
23+
# Expose configuration for backward compatibility and direct access
24+
COVERAGE_COLORS = _THEME["coverage_colors"]
25+
TABLE_STYLING = _THEME["table_styling"]
26+
COVERAGE_THRESHOLDS = _THEME["coverage_thresholds"]
27+
28+
29+
def get_coverage_tier_props(tier: str, font_weight: str = "600") -> str:
30+
"""
31+
Generate CSS properties string for a coverage performance tier.
32+
33+
Args:
34+
tier: One of 'good', 'medium', 'poor'
35+
font_weight: CSS font-weight value
36+
37+
Returns:
38+
CSS properties string for use with pandas Styler
39+
"""
40+
if tier not in COVERAGE_COLORS:
41+
raise ValueError(
42+
f"Unknown tier '{tier}'. Must be one of: {list(COVERAGE_COLORS.keys())}"
43+
)
44+
45+
colors = COVERAGE_COLORS[tier]
46+
return (
47+
f"color:{colors['text']};"
48+
f"background-color:{colors['background']};"
49+
f"border-left:4px solid {colors['border']};"
50+
f"font-weight:{font_weight};"
51+
)
52+
53+
54+
def get_html_color_span(tier: str, text: str = None) -> str:
55+
"""
56+
Generate HTML span element with coverage tier styling for documentation.
57+
58+
Args:
59+
tier: One of 'good', 'medium', 'poor'
60+
text: Text to display (defaults to tier description)
61+
62+
Returns:
63+
HTML span element with inline styling
64+
"""
65+
if tier not in COVERAGE_COLORS:
66+
raise ValueError(
67+
f"Unknown tier '{tier}'. Must be one of: {list(COVERAGE_COLORS.keys())}"
68+
)
69+
70+
colors = COVERAGE_COLORS[tier]
71+
display_text = text or colors["description"]
72+
73+
return (
74+
f'<span style="background-color: {colors["background"]}; '
75+
f'color: {colors["text"]}; '
76+
f"padding: 2px 5px; "
77+
f"border-radius: 3px; "
78+
f'border-left: 3px solid {colors["border"]};">'
79+
f"{display_text}</span>"
80+
)
81+
82+
83+
def get_theme_config() -> Dict[str, Any]:
84+
"""
85+
Get the complete theme configuration.
86+
87+
Returns:
88+
Dictionary containing all theme settings
89+
"""
90+
return _THEME.copy()

doc/utils/theme.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# DoubleML Coverage Theme Configuration
2+
# Central color palette and styling settings
3+
4+
coverage_colors:
5+
good:
6+
background: "#d1e7dd"
7+
text: "#0f5132"
8+
border: "#198754"
9+
description: "Green"
10+
medium:
11+
background: "#fff3cd"
12+
text: "#856404"
13+
border: "#ffc107"
14+
description: "Amber"
15+
poor:
16+
background: "#f8d7da"
17+
text: "#721c24"
18+
border: "#dc3545"
19+
description: "Coral"
20+
21+
table_styling:
22+
header_bg: "#f8f9fa"
23+
header_text: "#495057"
24+
border: "#dee2e6"
25+
caption_color: "#6c757d"
26+
hover_bg: "#f5f5f5"
27+
28+
coverage_thresholds:
29+
good: 0.05 # Within 5% of nominal level
30+
medium: 0.1 # Within 10% of nominal level
31+
poor: 1.0 # Beyond 10% of nominal level

0 commit comments

Comments
 (0)