Skip to content

Commit 31dfd18

Browse files
chore(bokeh): add metadata for scatter-marginal
1 parent 6018975 commit 31dfd18

2 files changed

Lines changed: 105 additions & 264 deletions

File tree

Lines changed: 88 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
1-
""" pyplots.ai
1+
"""anyplot.ai
22
scatter-marginal: Scatter Plot with Marginal Distributions
3-
Library: bokeh 3.8.1 | Python 3.13.11
4-
Quality: 91/100 | Created: 2025-12-26
3+
Library: bokeh | Python 3.13
4+
Quality: pending | Created: 2025-12-26
55
"""
66

7+
import os
8+
import time
9+
from pathlib import Path
10+
711
import numpy as np
8-
from bokeh.io import export_png
12+
from bokeh.io import output_file, save
913
from bokeh.layouts import column, row
1014
from bokeh.models import ColumnDataSource
1115
from bokeh.plotting import figure
16+
from selenium import webdriver
17+
from selenium.webdriver.chrome.options import Options
18+
1219

20+
# Theme tokens
21+
THEME = os.getenv("ANYPLOT_THEME", "light")
22+
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
23+
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
24+
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
25+
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
26+
BRAND = "#009E73" # Okabe-Ito position 1
1327

1428
# Data - bivariate normal with correlation
1529
np.random.seed(42)
@@ -19,15 +33,10 @@
1933

2034
source = ColumnDataSource(data={"x": x, "y": y})
2135

22-
# Colors
23-
python_blue = "#306998"
24-
python_yellow = "#FFD43B"
25-
2636
# Calculate dimensions for 4800x2700 total with marginal plots
27-
# Main scatter: ~80% of each dimension, marginals: ~20%
2837
main_width = 3800
2938
main_height = 2100
30-
marginal_width = 3800 # Same as main for alignment
39+
marginal_width = 3800
3140
marginal_height = 550
3241
side_marginal_width = 950
3342
side_marginal_height = 2100
@@ -41,84 +50,107 @@
4150
p_scatter = figure(
4251
width=main_width,
4352
height=main_height,
44-
x_axis_label="X Value",
45-
y_axis_label="Y Value",
46-
title="scatter-marginal · bokeh · pyplots.ai",
53+
x_axis_label="Height (cm)",
54+
y_axis_label="Weight (kg)",
55+
title="scatter-marginal · bokeh · anyplot.ai",
4756
)
4857

49-
p_scatter.scatter(x="x", y="y", source=source, size=18, color=python_blue, alpha=0.65, line_color=None)
58+
p_scatter.scatter(x="x", y="y", source=source, size=20, color=BRAND, alpha=0.65, line_color=None)
5059

5160
# Style main scatter
5261
p_scatter.title.text_font_size = "28pt"
62+
p_scatter.title.text_color = INK
5363
p_scatter.xaxis.axis_label_text_font_size = "22pt"
5464
p_scatter.yaxis.axis_label_text_font_size = "22pt"
65+
p_scatter.xaxis.axis_label_text_color = INK
66+
p_scatter.yaxis.axis_label_text_color = INK
5567
p_scatter.xaxis.major_label_text_font_size = "18pt"
5668
p_scatter.yaxis.major_label_text_font_size = "18pt"
57-
p_scatter.grid.grid_line_alpha = 0.3
58-
p_scatter.grid.grid_line_dash = [6, 4]
69+
p_scatter.xaxis.major_label_text_color = INK_SOFT
70+
p_scatter.yaxis.major_label_text_color = INK_SOFT
71+
p_scatter.xaxis.axis_line_color = INK_SOFT
72+
p_scatter.yaxis.axis_line_color = INK_SOFT
73+
p_scatter.xaxis.major_tick_line_color = INK_SOFT
74+
p_scatter.yaxis.major_tick_line_color = INK_SOFT
75+
p_scatter.background_fill_color = PAGE_BG
76+
p_scatter.border_fill_color = PAGE_BG
77+
p_scatter.outline_line_color = INK_SOFT
78+
p_scatter.grid.grid_line_color = INK
79+
p_scatter.grid.grid_line_alpha = 0.10
80+
p_scatter.toolbar_location = None
5981

6082
# Top marginal histogram (X distribution)
61-
p_top = figure(
62-
width=main_width,
63-
height=marginal_height,
64-
x_range=p_scatter.x_range, # Align with scatter
65-
title=None,
66-
)
67-
p_top.quad(
68-
top=x_hist,
69-
bottom=0,
70-
left=x_edges[:-1],
71-
right=x_edges[1:],
72-
fill_color=python_yellow,
73-
line_color=python_blue,
74-
alpha=0.7,
75-
)
83+
p_top = figure(width=marginal_width, height=marginal_height, x_range=p_scatter.x_range, title=None)
84+
p_top.quad(top=x_hist, bottom=0, left=x_edges[:-1], right=x_edges[1:], fill_color=BRAND, line_color=None, alpha=0.6)
7685
p_top.xaxis.visible = False
7786
p_top.yaxis.axis_label = "Count"
7887
p_top.yaxis.axis_label_text_font_size = "18pt"
88+
p_top.yaxis.axis_label_text_color = INK
7989
p_top.yaxis.major_label_text_font_size = "14pt"
80-
p_top.grid.grid_line_alpha = 0.3
90+
p_top.yaxis.major_label_text_color = INK_SOFT
91+
p_top.yaxis.axis_line_color = INK_SOFT
92+
p_top.yaxis.major_tick_line_color = INK_SOFT
93+
p_top.background_fill_color = PAGE_BG
94+
p_top.border_fill_color = PAGE_BG
95+
p_top.outline_line_color = INK_SOFT
96+
p_top.grid.grid_line_color = INK
97+
p_top.grid.grid_line_alpha = 0.10
8198
p_top.min_border_bottom = 0
8299
p_top.min_border_left = p_scatter.min_border_left
100+
p_top.toolbar_location = None
83101

84102
# Right marginal histogram (Y distribution)
85-
p_right = figure(
86-
width=side_marginal_width,
87-
height=main_height,
88-
y_range=p_scatter.y_range, # Align with scatter
89-
title=None,
90-
)
91-
p_right.quad(
92-
top=y_edges[1:],
93-
bottom=y_edges[:-1],
94-
left=0,
95-
right=y_hist,
96-
fill_color=python_yellow,
97-
line_color=python_blue,
98-
alpha=0.7,
99-
)
103+
p_right = figure(width=side_marginal_width, height=main_height, y_range=p_scatter.y_range, title=None)
104+
p_right.quad(top=y_edges[1:], bottom=y_edges[:-1], left=0, right=y_hist, fill_color=BRAND, line_color=None, alpha=0.6)
100105
p_right.yaxis.visible = False
101106
p_right.xaxis.axis_label = "Count"
102107
p_right.xaxis.axis_label_text_font_size = "18pt"
108+
p_right.xaxis.axis_label_text_color = INK
103109
p_right.xaxis.major_label_text_font_size = "14pt"
104-
p_right.grid.grid_line_alpha = 0.3
110+
p_right.xaxis.major_label_text_color = INK_SOFT
111+
p_right.xaxis.axis_line_color = INK_SOFT
112+
p_right.xaxis.major_tick_line_color = INK_SOFT
113+
p_right.background_fill_color = PAGE_BG
114+
p_right.border_fill_color = PAGE_BG
115+
p_right.outline_line_color = INK_SOFT
116+
p_right.grid.grid_line_color = INK
117+
p_right.grid.grid_line_alpha = 0.10
105118
p_right.min_border_left = 0
106119
p_right.min_border_bottom = p_scatter.min_border_bottom
120+
p_right.toolbar_location = None
107121

108122
# Empty corner placeholder
109123
p_corner = figure(width=side_marginal_width, height=marginal_height, toolbar_location=None)
110124
p_corner.outline_line_color = None
111125
p_corner.xaxis.visible = False
112126
p_corner.yaxis.visible = False
113127
p_corner.grid.visible = False
128+
p_corner.background_fill_color = PAGE_BG
129+
p_corner.border_fill_color = PAGE_BG
114130

115-
# Remove toolbars for clean look
116-
p_scatter.toolbar_location = None
117-
p_top.toolbar_location = None
118-
p_right.toolbar_location = None
119-
120-
# Layout: top marginal + corner on top row, scatter + right marginal on bottom row
131+
# Layout
121132
layout = column(row(p_top, p_corner), row(p_scatter, p_right))
122133

123-
# Save
124-
export_png(layout, filename="plot.png")
134+
# Save HTML
135+
output_file(f"plot-{THEME}.html")
136+
save(layout)
137+
138+
# Screenshot with Selenium
139+
W, H = 4800, 2700
140+
opts = Options()
141+
for arg in (
142+
"--headless=new",
143+
"--no-sandbox",
144+
"--disable-dev-shm-usage",
145+
"--disable-gpu",
146+
f"--window-size={W},{H}",
147+
"--hide-scrollbars",
148+
):
149+
opts.add_argument(arg)
150+
151+
driver = webdriver.Chrome(options=opts)
152+
driver.set_window_size(W, H)
153+
driver.get(f"file://{Path(f'plot-{THEME}.html').resolve()}")
154+
time.sleep(3)
155+
driver.save_screenshot(f"plot-{THEME}.png")
156+
driver.quit()

0 commit comments

Comments
 (0)