Skip to content

Commit 9da8d5b

Browse files
update(bullet-basic): plotly — comprehensive quality review
Comprehensive quality review improving visual design, indicator usage, and layout.
1 parent 1e072e3 commit 9da8d5b

2 files changed

Lines changed: 37 additions & 86 deletions

File tree

Lines changed: 32 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
""" pyplots.ai
1+
"""pyplots.ai
22
bullet-basic: Basic Bullet Chart
3-
Library: plotly 6.5.0 | Python 3.13.11
4-
Quality: 92/100 | Created: 2025-12-23
3+
Library: plotly 6.5.2 | Python 3.14.3
4+
Quality: /100 | Updated: 2026-02-22
55
"""
66

77
import plotly.graph_objects as go
8-
from plotly.subplots import make_subplots
98

109

1110
# Data - Multiple KPIs with different performance levels
@@ -17,97 +16,49 @@
1716
]
1817

1918
# Grayscale colors for qualitative ranges (poor -> satisfactory -> good)
20-
range_colors = ["#D9D9D9", "#BFBFBF", "#A6A6A6"]
19+
range_colors = ["#A6A6A6", "#C0C0C0", "#DCDCDC"]
2120

22-
# Create subplots - one row per metric for proper scaling
23-
fig = make_subplots(
24-
rows=len(metrics), cols=1, shared_xaxes=False, vertical_spacing=0.12, subplot_titles=[m["label"] for m in metrics]
25-
)
21+
# Create figure with native Indicator traces (bullet mode)
22+
fig = go.Figure()
23+
n = len(metrics)
24+
spacing = 0.04
25+
row_height = (1.0 - spacing * (n - 1)) / n
2626

27-
# Create each bullet chart in its own subplot
2827
for i, m in enumerate(metrics):
29-
row = i + 1
30-
31-
# Add qualitative range bands (background, plotted in reverse order)
32-
for j, r in enumerate(reversed(m["ranges"])):
33-
fig.add_trace(
34-
go.Bar(
35-
x=[r],
36-
y=[""],
37-
orientation="h",
38-
marker=dict(color=range_colors[len(m["ranges"]) - 1 - j]),
39-
width=0.6,
40-
showlegend=False,
41-
hoverinfo="skip",
42-
),
43-
row=row,
44-
col=1,
45-
)
28+
y_start = 1.0 - (i + 1) * row_height - i * spacing
29+
y_end = y_start + row_height
4630

47-
# Add actual value bar (primary measure) using Python Blue
4831
fig.add_trace(
49-
go.Bar(
50-
x=[m["actual"]],
51-
y=[""],
52-
orientation="h",
53-
marker=dict(color="#306998"),
54-
width=0.25,
55-
showlegend=False,
56-
name=m["label"],
57-
hovertemplate=f"{m['label']}: {m['actual']}<extra></extra>",
58-
),
59-
row=row,
60-
col=1,
61-
)
62-
63-
# Add target marker line (thin black vertical line)
64-
fig.add_shape(
65-
type="line",
66-
x0=m["target"],
67-
x1=m["target"],
68-
y0=-0.4,
69-
y1=0.4,
70-
line=dict(color="#1A1A1A", width=5),
71-
row=row,
72-
col=1,
73-
)
74-
75-
# Add actual value annotation for precise reading
76-
max_range = m["ranges"][-1]
77-
fig.add_annotation(
78-
x=max_range * 1.02,
79-
y=0,
80-
text=f"<b>{m['actual']}</b>",
81-
showarrow=False,
82-
font=dict(size=20, color="#306998"),
83-
xanchor="left",
84-
row=row,
85-
col=1,
86-
)
87-
88-
# Update x-axis range for each subplot
89-
fig.update_xaxes(
90-
range=[0, max_range * 1.15], tickfont=dict(size=16), showgrid=True, gridcolor="rgba(0,0,0,0.1)", row=row, col=1
32+
go.Indicator(
33+
mode="number+gauge",
34+
value=m["actual"],
35+
number={"font": {"size": 26, "color": "#306998"}},
36+
domain={"x": [0.18, 0.95], "y": [y_start, y_end]},
37+
title={"text": m["label"], "font": {"size": 22}, "align": "left"},
38+
gauge={
39+
"shape": "bullet",
40+
"axis": {"range": [0, m["ranges"][-1]], "tickfont": {"size": 16}},
41+
"bar": {"color": "#306998"},
42+
"bgcolor": "white",
43+
"threshold": {"line": {"color": "#1A1A1A", "width": 4}, "thickness": 0.75, "value": m["target"]},
44+
"steps": [
45+
{"range": [0, m["ranges"][0]], "color": range_colors[0]},
46+
{"range": [m["ranges"][0], m["ranges"][1]], "color": range_colors[1]},
47+
{"range": [m["ranges"][1], m["ranges"][2]], "color": range_colors[2]},
48+
],
49+
},
50+
)
9151
)
9252

93-
fig.update_yaxes(showticklabels=False, row=row, col=1)
94-
9553
# Layout
9654
fig.update_layout(
97-
title=dict(text="bullet-basic · plotly · pyplots.ai", font=dict(size=32), x=0.5, xanchor="center"),
98-
barmode="overlay",
55+
title={"text": "bullet-basic · plotly · pyplots.ai", "font": {"size": 32}, "x": 0.5, "xanchor": "center"},
9956
template="plotly_white",
100-
margin=dict(l=80, r=100, t=120, b=60),
101-
showlegend=False,
57+
margin={"l": 40, "r": 40, "t": 100, "b": 40},
10258
height=900,
10359
width=1600,
10460
)
10561

106-
# Update subplot titles font size
107-
for annotation in fig["layout"]["annotations"]:
108-
if "text" in annotation and annotation["text"] in [m["label"] for m in metrics]:
109-
annotation["font"] = dict(size=22)
110-
11162
# Save
11263
fig.write_image("plot.png", width=1600, height=900, scale=3)
11364
fig.write_html("plot.html", include_plotlyjs="cdn")

plots/bullet-basic/metadata/plotly.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
library: plotly
22
specification_id: bullet-basic
33
created: '2025-12-23T09:18:22Z'
4-
updated: '2025-12-23T09:22:28Z'
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: 20456603137
77
issue: 0
8-
python_version: 3.13.11
9-
library_version: 6.5.0
8+
python_version: 3.14.3
9+
library_version: 6.5.2
1010
preview_url: https://storage.googleapis.com/pyplots-images/plots/bullet-basic/plotly/plot.png
1111
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/bullet-basic/plotly/plot_thumb.png
1212
preview_html: https://storage.googleapis.com/pyplots-images/plots/bullet-basic/plotly/plot.html
13-
quality_score: 92
13+
quality_score: null
1414
impl_tags:
1515
dependencies: []
1616
techniques:

0 commit comments

Comments
 (0)