Skip to content

Commit 85eea4f

Browse files
feat(plotly): implement waffle-basic (#9950)
## Implementation: `waffle-basic` - python/plotly Implements the **python/plotly** version of `waffle-basic`. **File:** `plots/waffle-basic/implementations/python/plotly.py` **Parent Issue:** #998 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/30198132759)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 3017f58 commit 85eea4f

2 files changed

Lines changed: 148 additions & 148 deletions

File tree

Lines changed: 57 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
""" anyplot.ai
22
waffle-basic: Basic Waffle Chart
3-
Library: plotly 6.7.0 | Python 3.13.13
4-
Quality: 90/100 | Updated: 2026-05-05
3+
Library: plotly 6.9.0 | Python 3.13.14
4+
Quality: 91/100 | Updated: 2026-07-26
55
"""
66

77
import os
@@ -10,113 +10,87 @@
1010
import plotly.graph_objects as go
1111

1212

13-
# Theme tokens
13+
# Theme tokens (see prompts/default-style-guide.md "Background" + "Theme-adaptive Chrome")
1414
THEME = os.getenv("ANYPLOT_THEME", "light")
1515
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
1616
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
1717
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
1818
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
19-
GRID = "rgba(26,26,23,0.10)" if THEME == "light" else "rgba(240,239,232,0.10)"
2019

21-
# Okabe-Ito palette - first series ALWAYS #009E73
22-
IMPRINT = [
23-
"#009E73", # bluish green (brand)
24-
"#C475FD", # vermillion
25-
"#4467A3", # blue
26-
"#BD8233", # reddish purple
27-
"#AE3030", # orange
28-
]
20+
# Imprint palette - first series ALWAYS #009E73
21+
IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030"]
2922

30-
# Data - Budget allocation across spending categories
23+
# Data - budget allocation across spending categories
3124
categories = ["Operations", "Marketing", "R&D", "HR", "Other"]
3225
values = [35, 25, 22, 12, 6] # Percentages (sum to 100)
26+
n_categories = len(categories)
3327

34-
# Create 10x10 waffle grid (100 squares, each = 1%)
28+
# Build a 10x10 waffle grid (100 squares, each = 1%), filled bottom-up, left-to-right
3529
grid_size = 10
3630
total_squares = grid_size * grid_size
37-
waffle_data = np.zeros(total_squares, dtype=int)
31+
category_idx = np.repeat(np.arange(n_categories), values)
32+
waffle_matrix = category_idx.reshape(grid_size, grid_size)[::-1]
3833

39-
# Fill grid with category indices (row by row from bottom-left)
40-
square_idx = 0
41-
for cat_idx, count in enumerate(values):
42-
for _ in range(count):
43-
if square_idx < total_squares:
44-
waffle_data[square_idx] = cat_idx
45-
square_idx += 1
34+
# Hover metadata mirrors the grid so each square reports its own category and share
35+
category_names = np.array(categories)[waffle_matrix]
36+
category_values = np.array(values)[waffle_matrix]
4637

47-
# Reshape to 10x10 grid
48-
waffle_matrix = waffle_data.reshape(grid_size, grid_size)
38+
# Single Heatmap trace (not 100+ scatter traces) with a hand-built discrete
39+
# colorscale - each category occupies an equal band so cell colors stay flat,
40+
# and the vertical colorbar doubles as a labeled legend (unrotated tick text,
41+
# one row per category, so nothing crowds or clips at the canvas edge).
42+
discrete_colorscale = []
43+
for idx, color in enumerate(IMPRINT[:n_categories]):
44+
discrete_colorscale.append([idx / n_categories, color])
45+
discrete_colorscale.append([(idx + 1) / n_categories, color])
4946

50-
# Create figure
51-
fig = go.Figure()
52-
53-
# Add squares using scatter plot with larger markers for visibility
54-
for row in range(grid_size):
55-
for col in range(grid_size):
56-
cat_idx = waffle_matrix[row, col]
57-
fig.add_trace(
58-
go.Scatter(
59-
x=[col],
60-
y=[row],
61-
mode="markers",
62-
marker={
63-
"size": 55,
64-
"symbol": "square",
65-
"color": IMPRINT[cat_idx],
66-
"line": {"color": PAGE_BG, "width": 2},
67-
},
68-
showlegend=False,
69-
hoverinfo="skip",
70-
)
71-
)
72-
73-
# Add legend traces (one per category)
74-
for cat_idx, (cat, val) in enumerate(zip(categories, values, strict=True)):
75-
fig.add_trace(
76-
go.Scatter(
77-
x=[None],
78-
y=[None],
79-
mode="markers",
80-
marker={"size": 22, "symbol": "square", "color": IMPRINT[cat_idx]},
81-
name=f"{cat}: {val}%",
82-
showlegend=True,
83-
)
47+
fig = go.Figure(
48+
go.Heatmap(
49+
z=waffle_matrix,
50+
zmin=-0.5,
51+
zmax=n_categories - 0.5,
52+
colorscale=discrete_colorscale,
53+
xgap=6,
54+
ygap=6,
55+
text=category_names,
56+
customdata=category_values,
57+
hovertemplate="<b>%{text}</b><br>%{customdata}% of total<extra></extra>",
58+
showscale=True,
59+
colorbar={
60+
"thickness": 26,
61+
"len": 0.7,
62+
"x": 1.06,
63+
"xanchor": "left",
64+
"y": 0.5,
65+
"yanchor": "middle",
66+
"tickmode": "array",
67+
"tickvals": list(range(n_categories)),
68+
"ticktext": [f"{cat}{val}%" for cat, val in zip(categories, values, strict=True)],
69+
"ticks": "",
70+
"outlinewidth": 1,
71+
"outlinecolor": INK_SOFT,
72+
"tickfont": {"size": 13, "color": INK_SOFT},
73+
"bgcolor": ELEVATED_BG,
74+
},
8475
)
76+
)
8577

8678
# Layout
8779
fig.update_layout(
80+
autosize=False,
8881
title={
89-
"text": "waffle-basic · plotly · anyplot.ai",
90-
"font": {"size": 28, "color": INK},
82+
"text": "waffle-basic · python · plotly · anyplot.ai",
83+
"font": {"size": 16, "color": INK},
9184
"x": 0.5,
9285
"xanchor": "center",
9386
},
94-
xaxis={
95-
"showgrid": False,
96-
"zeroline": False,
97-
"showticklabels": False,
98-
"range": [-0.8, 9.8],
99-
"scaleanchor": "y",
100-
"scaleratio": 1,
101-
},
102-
yaxis={"showgrid": False, "zeroline": False, "showticklabels": False, "range": [-0.8, 9.8]},
103-
legend={
104-
"font": {"size": 18, "color": INK_SOFT},
105-
"bgcolor": ELEVATED_BG,
106-
"bordercolor": INK_SOFT,
107-
"borderwidth": 1,
108-
"orientation": "h",
109-
"yanchor": "top",
110-
"y": -0.08,
111-
"xanchor": "center",
112-
"x": 0.5,
113-
"itemsizing": "constant",
114-
},
87+
xaxis={"visible": False, "range": [-0.5, grid_size - 0.5], "scaleanchor": "y", "scaleratio": 1},
88+
yaxis={"visible": False, "range": [-0.5, grid_size - 0.5]},
11589
paper_bgcolor=PAGE_BG,
11690
plot_bgcolor=PAGE_BG,
117-
margin={"l": 60, "r": 60, "t": 120, "b": 120},
91+
margin={"l": 60, "r": 220, "t": 90, "b": 60},
11892
)
11993

12094
# Save outputs
121-
fig.write_image(f"plot-{THEME}.png", width=1600, height=900, scale=3)
95+
fig.write_image(f"plot-{THEME}.png", width=600, height=600, scale=4)
12296
fig.write_html(f"plot-{THEME}.html", include_plotlyjs="cdn")

0 commit comments

Comments
 (0)