Skip to content

Commit 2ebe024

Browse files
feat(plotly): implement scatter-basic (#5310)
## Implementation: `scatter-basic` - python/plotly Implements the **python/plotly** version of `scatter-basic`. **File:** `plots/scatter-basic/implementations/python/plotly.py` **Parent Issue:** #611 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/24853790187)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent a5566dc commit 2ebe024

2 files changed

Lines changed: 164 additions & 207 deletions

File tree

Lines changed: 46 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,146 +1,83 @@
1-
""" pyplots.ai
1+
""" anyplot.ai
22
scatter-basic: Basic Scatter Plot
3-
Library: plotly 6.5.2 | Python 3.14
4-
Quality: 92/100 | Created: 2025-12-22
3+
Library: plotly 6.7.0 | Python 3.14.4
4+
Quality: 86/100 | Created: 2026-04-23
55
"""
66

7+
import os
8+
79
import numpy as np
810
import plotly.graph_objects as go
911

1012

11-
# Data: Study hours vs exam scores (realistic educational context)
13+
# Theme tokens (see prompts/default-style-guide.md "Theme-adaptive Chrome")
14+
THEME = os.getenv("ANYPLOT_THEME", "light")
15+
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
16+
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
17+
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
18+
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)"
20+
BRAND = "#009E73" # Okabe-Ito position 1 — always first series
21+
22+
# Data: study hours vs exam scores, moderate positive correlation
1223
np.random.seed(42)
13-
n_students = 120
24+
n_students = 160
1425
study_hours = np.random.uniform(1, 10, n_students)
15-
base_score = 45 + study_hours * 5
16-
exam_scores = base_score + np.random.randn(n_students) * 8
26+
exam_scores = 45 + study_hours * 5 + np.random.randn(n_students) * 8
1727
exam_scores = np.clip(exam_scores, 0, 100)
1828

19-
# Inject outliers to show scatter plot's outlier-detection value
20-
study_hours[0], exam_scores[0] = 8.5, 52.0 # High effort, low result
21-
study_hours[1], exam_scores[1] = 2.0, 78.0 # Low effort, high result
22-
study_hours[2], exam_scores[2] = 9.2, 55.0 # Another underperformer
23-
24-
# Linear regression for trend line
25-
coeffs = np.polyfit(study_hours, exam_scores, 1)
26-
trend_x = np.array([0.5, 10.5])
27-
trend_y = np.polyval(coeffs, trend_x)
28-
r_value = np.corrcoef(study_hours, exam_scores)[0, 1]
29-
30-
# Color palette
31-
python_blue = "#306998"
32-
accent_orange = "#D4782F"
33-
34-
fig = go.Figure()
35-
36-
# Trend line (behind markers)
37-
fig.add_trace(
38-
go.Scatter(
39-
x=trend_x,
40-
y=trend_y,
41-
mode="lines",
42-
line={"color": "rgba(48, 105, 152, 0.4)", "width": 2.5, "dash": "dash"},
43-
showlegend=False,
44-
hoverinfo="skip",
45-
)
46-
)
47-
48-
# Main scatter — size 11 avoids congestion in dense regions
49-
fig.add_trace(
29+
# Plot
30+
fig = go.Figure(
5031
go.Scatter(
5132
x=study_hours,
5233
y=exam_scores,
5334
mode="markers",
54-
marker={"size": 11, "color": python_blue, "opacity": 0.6, "line": {"width": 1.2, "color": "white"}},
35+
marker={"size": 13, "color": BRAND, "opacity": 0.7, "line": {"width": 1.2, "color": PAGE_BG}},
36+
hovertemplate="Study: %{x:.1f} h<br>Score: %{y:.1f}%<extra></extra>",
5537
showlegend=False,
56-
hovertemplate="<b>Student</b><br>Study: %{x:.1f} h<br>Score: %{y:.1f}%<extra></extra>",
5738
)
5839
)
5940

60-
# Outlier diamonds
61-
fig.add_trace(
62-
go.Scatter(
63-
x=[8.5, 2.0, 9.2],
64-
y=[52.0, 78.0, 55.0],
65-
mode="markers",
66-
marker={
67-
"size": 15,
68-
"color": accent_orange,
69-
"opacity": 0.9,
70-
"line": {"width": 2, "color": "white"},
71-
"symbol": "diamond",
72-
},
73-
showlegend=False,
74-
hoverinfo="skip",
75-
)
76-
)
77-
78-
# Annotations — each outlier gets its own label for clarity
79-
ann_base = {
80-
"showarrow": True,
81-
"arrowhead": 2,
82-
"arrowsize": 1.2,
83-
"arrowwidth": 2,
84-
"arrowcolor": accent_orange,
85-
"align": "center",
86-
"font": {"size": 16, "color": accent_orange, "family": "Arial, sans-serif"},
87-
"bgcolor": "rgba(255,255,255,0.85)",
88-
"bordercolor": accent_orange,
89-
"borderwidth": 1.5,
90-
"borderpad": 5,
91-
}
92-
annotations = [
93-
{**ann_base, "x": 2.0, "y": 78.0, "text": "Low effort,<br>high score", "ax": -75, "ay": -45},
94-
{**ann_base, "x": 8.5, "y": 52.0, "text": "High effort,<br>low score", "ax": 80, "ay": 40},
95-
{**ann_base, "x": 9.2, "y": 55.0, "text": "Underperformer", "ax": 75, "ay": -35},
96-
# Correlation coefficient near trend line
97-
{
98-
"x": 8.5,
99-
"y": np.polyval(coeffs, 8.5) + 4,
100-
"text": f"<b>r = {r_value:.2f}</b>",
101-
"showarrow": False,
102-
"bgcolor": "rgba(255,255,255,0.8)",
103-
"borderpad": 4,
104-
"font": {"size": 17, "color": python_blue, "family": "Arial, sans-serif"},
105-
},
106-
]
107-
41+
# Style
10842
fig.update_layout(
10943
title={
110-
"text": "scatter-basic · plotly · pyplots.ai",
111-
"font": {"size": 28, "color": "#2C3E50", "family": "Arial Black, Arial, sans-serif"},
44+
"text": "scatter-basic · plotly · anyplot.ai",
45+
"font": {"size": 28, "color": INK},
11246
"x": 0.5,
11347
"xanchor": "center",
11448
"y": 0.95,
11549
},
11650
xaxis={
117-
"title": {"text": "Study Hours (h)", "font": {"size": 22, "family": "Arial, sans-serif"}, "standoff": 12},
118-
"tickfont": {"size": 18},
119-
"showgrid": True,
51+
"title": {"text": "Study Hours per Day", "font": {"size": 22, "color": INK}, "standoff": 12},
52+
"tickfont": {"size": 18, "color": INK_SOFT},
53+
"gridcolor": GRID,
12054
"gridwidth": 1,
121-
"gridcolor": "rgba(0,0,0,0.06)",
122-
"range": [0, 11],
55+
"linecolor": INK_SOFT,
12356
"zeroline": False,
57+
"showline": True,
58+
"mirror": False,
59+
"range": [0, 11],
12460
"dtick": 2,
12561
},
12662
yaxis={
127-
"title": {"text": "Exam Score (%)", "font": {"size": 22, "family": "Arial, sans-serif"}, "standoff": 12},
128-
"tickfont": {"size": 18},
129-
"showgrid": True,
63+
"title": {"text": "Exam Score (%)", "font": {"size": 22, "color": INK}, "standoff": 12},
64+
"tickfont": {"size": 18, "color": INK_SOFT},
65+
"gridcolor": GRID,
13066
"gridwidth": 1,
131-
"gridcolor": "rgba(0,0,0,0.06)",
132-
"range": [35, 105],
67+
"linecolor": INK_SOFT,
13368
"zeroline": False,
69+
"showline": True,
70+
"mirror": False,
71+
"range": [35, 105],
13472
"dtick": 10,
13573
},
136-
template="plotly_white",
137-
showlegend=False,
138-
margin={"l": 80, "r": 40, "t": 90, "b": 70},
139-
annotations=annotations,
140-
plot_bgcolor="white",
141-
paper_bgcolor="#FAFBFC",
142-
hoverlabel={"bgcolor": "white", "font_size": 14, "font_color": python_blue},
74+
paper_bgcolor=PAGE_BG,
75+
plot_bgcolor=PAGE_BG,
76+
font={"color": INK},
77+
margin={"l": 90, "r": 60, "t": 100, "b": 80},
78+
hoverlabel={"bgcolor": ELEVATED_BG, "bordercolor": INK_SOFT, "font": {"color": INK}},
14379
)
14480

145-
fig.write_image("plot.png", width=1600, height=900, scale=3)
146-
fig.write_html("plot.html")
81+
# Save
82+
fig.write_image(f"plot-{THEME}.png", width=1600, height=900, scale=3)
83+
fig.write_html(f"plot-{THEME}.html", include_plotlyjs="cdn")

0 commit comments

Comments
 (0)