Skip to content

Commit b39d469

Browse files
update(bullet-basic): altair — comprehensive quality review
Comprehensive quality review improving Vega-Lite layered approach and visual design.
1 parent 1e072e3 commit b39d469

2 files changed

Lines changed: 59 additions & 37 deletions

File tree

Lines changed: 55 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,109 @@
1-
""" pyplots.ai
1+
"""pyplots.ai
22
bullet-basic: Basic Bullet Chart
3-
Library: altair 6.0.0 | Python 3.13.11
4-
Quality: 92/100 | Created: 2025-12-23
3+
Library: altair 6.0.0 | Python 3.14.3
4+
Quality: /100 | Updated: 2026-02-22
55
"""
66

77
import altair as alt
88
import pandas as pd
99

1010

1111
# Data - KPI metrics with actual values, targets, and qualitative range thresholds
12-
# Each metric shows: current performance, target goal, and bands for poor/satisfactory/good
1312
metrics = [
1413
{"metric": "Revenue ($K)", "actual": 275, "target": 250, "poor": 150, "satisfactory": 200, "good": 300},
1514
{"metric": "Profit ($K)", "actual": 85, "target": 100, "poor": 50, "satisfactory": 75, "good": 125},
1615
{"metric": "New Customers", "actual": 320, "target": 300, "poor": 200, "satisfactory": 275, "good": 350},
17-
{"metric": "Satisfaction (1-5)", "actual": 4.2, "target": 4.5, "poor": 3.0, "satisfactory": 4.0, "good": 5.0},
16+
{"metric": "Satisfaction", "actual": 4.2, "target": 4.5, "poor": 3.0, "satisfactory": 4.0, "good": 5.0},
1817
]
18+
metric_order = [m["metric"] for m in metrics]
1919

20-
# Normalize all values to percentage of maximum (good value) for common scale
20+
# Normalize to percentage of maximum and build dataframes
2121
range_data = []
2222
for m in metrics:
2323
max_val = m["good"]
2424
poor_pct = (m["poor"] / max_val) * 100
2525
sat_pct = (m["satisfactory"] / max_val) * 100
26-
# Build qualitative bands: Poor (0 to poor), Satisfactory (poor to sat), Good (sat to 100%)
27-
range_data.append({"metric": m["metric"], "range_start": 0, "range_end": poor_pct, "band": "Poor"})
28-
range_data.append({"metric": m["metric"], "range_start": poor_pct, "range_end": sat_pct, "band": "Satisfactory"})
29-
range_data.append({"metric": m["metric"], "range_start": sat_pct, "range_end": 100, "band": "Good"})
26+
range_data.append({"metric": m["metric"], "start": 0, "end": poor_pct, "band": "Poor"})
27+
range_data.append({"metric": m["metric"], "start": poor_pct, "end": sat_pct, "band": "Satisfactory"})
28+
range_data.append({"metric": m["metric"], "start": sat_pct, "end": 100, "band": "Good"})
3029

3130
df_ranges = pd.DataFrame(range_data)
3231

33-
# Build dataframe for actual values (normalized to percentage)
34-
df_actual = pd.DataFrame([{"metric": m["metric"], "actual": (m["actual"] / m["good"]) * 100} for m in metrics])
32+
df_actual = pd.DataFrame(
33+
[{"metric": m["metric"], "actual_pct": (m["actual"] / m["good"]) * 100, "actual_raw": m["actual"]} for m in metrics]
34+
)
3535

36-
# Build dataframe for target markers (normalized to percentage)
37-
df_target = pd.DataFrame([{"metric": m["metric"], "target": (m["target"] / m["good"]) * 100} for m in metrics])
36+
df_target = pd.DataFrame(
37+
[{"metric": m["metric"], "target_pct": (m["target"] / m["good"]) * 100, "target_raw": m["target"]} for m in metrics]
38+
)
3839

39-
# Background qualitative ranges (grayscale bands as per specification)
40+
# Background qualitative ranges (grayscale bands)
4041
ranges_chart = (
4142
alt.Chart(df_ranges)
42-
.mark_bar(height=55)
43+
.mark_bar(height=50)
4344
.encode(
44-
y=alt.Y("metric:N", title=None, sort=[m["metric"] for m in metrics]),
45-
x=alt.X("range_start:Q", title="% of Maximum", scale=alt.Scale(domain=[0, 110])),
46-
x2=alt.X2("range_end:Q"),
45+
y=alt.Y("metric:N", title=None, sort=metric_order, axis=alt.Axis(labelFontSize=18)),
46+
x=alt.X(
47+
"start:Q",
48+
title="Performance (% of Goal)",
49+
scale=alt.Scale(domain=[0, 105]),
50+
axis=alt.Axis(titleFontSize=22, labelFontSize=16, tickCount=6),
51+
),
52+
x2="end:Q",
4753
color=alt.Color(
4854
"band:N",
49-
scale=alt.Scale(domain=["Poor", "Satisfactory", "Good"], range=["#d9d9d9", "#bdbdbd", "#969696"]),
50-
legend=alt.Legend(title="Performance Band", orient="bottom", titleFontSize=18, labelFontSize=16),
55+
scale=alt.Scale(domain=["Poor", "Satisfactory", "Good"], range=["#e0e0e0", "#c0c0c0", "#9e9e9e"]),
56+
legend=alt.Legend(
57+
title="Performance Band", orient="bottom", titleFontSize=18, labelFontSize=16, direction="horizontal"
58+
),
5159
),
60+
tooltip=[alt.Tooltip("metric:N", title="Metric"), alt.Tooltip("band:N", title="Band")],
5261
)
5362
)
5463

55-
# Actual value bar (Python Blue - primary measure)
64+
# Actual value bar (Python Blue)
5665
actual_chart = (
5766
alt.Chart(df_actual)
58-
.mark_bar(color="#306998", height=22)
67+
.mark_bar(color="#306998", height=20)
5968
.encode(
60-
y=alt.Y("metric:N", title=None, sort=[m["metric"] for m in metrics]),
61-
x=alt.X("actual:Q", title="% of Maximum", scale=alt.Scale(domain=[0, 110])),
69+
y=alt.Y("metric:N", sort=metric_order),
70+
x=alt.X("actual_pct:Q"),
71+
tooltip=[
72+
alt.Tooltip("metric:N", title="Metric"),
73+
alt.Tooltip("actual_raw:Q", title="Actual"),
74+
alt.Tooltip("actual_pct:Q", title="% of Goal", format=".1f"),
75+
],
6276
)
6377
)
6478

65-
# Target marker (thin black vertical line perpendicular to bar)
79+
# Target marker (thin black vertical tick)
6680
target_chart = (
6781
alt.Chart(df_target)
68-
.mark_tick(color="black", thickness=4, size=55)
82+
.mark_tick(color="#222222", thickness=4, size=50)
6983
.encode(
70-
y=alt.Y("metric:N", title=None, sort=[m["metric"] for m in metrics]),
71-
x=alt.X("target:Q", title="% of Maximum", scale=alt.Scale(domain=[0, 110])),
84+
y=alt.Y("metric:N", sort=metric_order),
85+
x=alt.X("target_pct:Q"),
86+
tooltip=[alt.Tooltip("metric:N", title="Metric"), alt.Tooltip("target_raw:Q", title="Target")],
7287
)
7388
)
7489

75-
# Layer all components: ranges (background), actual bar, target marker
90+
# Actual value text labels on bars
91+
value_labels = (
92+
alt.Chart(df_actual)
93+
.mark_text(align="left", dx=6, fontSize=16, fontWeight="bold", color="#1a3a5c")
94+
.encode(y=alt.Y("metric:N", sort=metric_order), x=alt.X("actual_pct:Q"), text=alt.Text("actual_raw:Q"))
95+
)
96+
97+
# Layer all components and configure
7698
chart = (
77-
alt.layer(ranges_chart, actual_chart, target_chart)
99+
alt.layer(ranges_chart, actual_chart, target_chart, value_labels)
78100
.properties(
79-
width=1400, height=700, title=alt.Title("bullet-basic · altair · pyplots.ai", fontSize=28, anchor="middle")
101+
width=1400, height=400, title=alt.Title("bullet-basic · altair · pyplots.ai", fontSize=28, anchor="middle")
80102
)
81103
.configure_axis(labelFontSize=18, titleFontSize=22)
82104
.configure_view(strokeWidth=0)
83105
)
84106

85-
# Save outputs
107+
# Save
86108
chart.save("plot.png", scale_factor=3.0)
87109
chart.save("plot.html")

plots/bullet-basic/metadata/altair.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
library: altair
22
specification_id: bullet-basic
33
created: '2025-12-23T09:18:21Z'
4-
updated: '2025-12-23T09:22:59Z'
5-
generated_by: claude-opus-4-5-20251101
4+
updated: '2026-02-22T12:00:00+00:00'
5+
generated_by: claude-opus-4-6
66
workflow_run: 20456606505
77
issue: 0
8-
python_version: 3.13.11
8+
python_version: 3.14.3
99
library_version: 6.0.0
1010
preview_url: https://storage.googleapis.com/pyplots-images/plots/bullet-basic/altair/plot.png
1111
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/bullet-basic/altair/plot_thumb.png
1212
preview_html: https://storage.googleapis.com/pyplots-images/plots/bullet-basic/altair/plot.html
13-
quality_score: 92
13+
quality_score: null
1414
impl_tags:
1515
dependencies: []
1616
techniques:

0 commit comments

Comments
 (0)