|
1 | | -""" pyplots.ai |
| 1 | +""" anyplot.ai |
2 | 2 | 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 |
5 | 5 | """ |
6 | 6 |
|
| 7 | +import os |
| 8 | + |
7 | 9 | import numpy as np |
8 | 10 | import plotly.graph_objects as go |
9 | 11 |
|
10 | 12 |
|
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 |
12 | 23 | np.random.seed(42) |
13 | | -n_students = 120 |
| 24 | +n_students = 160 |
14 | 25 | 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 |
17 | 27 | exam_scores = np.clip(exam_scores, 0, 100) |
18 | 28 |
|
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( |
50 | 31 | go.Scatter( |
51 | 32 | x=study_hours, |
52 | 33 | y=exam_scores, |
53 | 34 | 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>", |
55 | 37 | showlegend=False, |
56 | | - hovertemplate="<b>Student</b><br>Study: %{x:.1f} h<br>Score: %{y:.1f}%<extra></extra>", |
57 | 38 | ) |
58 | 39 | ) |
59 | 40 |
|
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 |
108 | 42 | fig.update_layout( |
109 | 43 | 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}, |
112 | 46 | "x": 0.5, |
113 | 47 | "xanchor": "center", |
114 | 48 | "y": 0.95, |
115 | 49 | }, |
116 | 50 | 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, |
120 | 54 | "gridwidth": 1, |
121 | | - "gridcolor": "rgba(0,0,0,0.06)", |
122 | | - "range": [0, 11], |
| 55 | + "linecolor": INK_SOFT, |
123 | 56 | "zeroline": False, |
| 57 | + "showline": True, |
| 58 | + "mirror": False, |
| 59 | + "range": [0, 11], |
124 | 60 | "dtick": 2, |
125 | 61 | }, |
126 | 62 | 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, |
130 | 66 | "gridwidth": 1, |
131 | | - "gridcolor": "rgba(0,0,0,0.06)", |
132 | | - "range": [35, 105], |
| 67 | + "linecolor": INK_SOFT, |
133 | 68 | "zeroline": False, |
| 69 | + "showline": True, |
| 70 | + "mirror": False, |
| 71 | + "range": [35, 105], |
134 | 72 | "dtick": 10, |
135 | 73 | }, |
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}}, |
143 | 79 | ) |
144 | 80 |
|
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