Skip to content

Commit 5f5c2d7

Browse files
feat(bokeh): implement ridgeline-basic (#547)
## Summary Implements `ridgeline-basic` for **bokeh**. **Parent Issue:** #539 **Base Branch:** `plot/ridgeline-basic` ## Files - `plots/ridgeline-basic/implementations/bokeh.py` ## Preview Preview will be uploaded to GCS staging after this workflow completes. Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent ba078e4 commit 5f5c2d7

File tree

1 file changed

+117
-0
lines changed
  • plots/ridgeline-basic/implementations

1 file changed

+117
-0
lines changed
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
"""
2+
ridgeline-basic: Ridgeline Plot
3+
Library: bokeh
4+
"""
5+
6+
import numpy as np
7+
from bokeh.io import export_png, output_file, save
8+
from bokeh.models import Range1d
9+
from bokeh.plotting import figure
10+
from scipy import stats
11+
12+
13+
# Data - Monthly temperature readings
14+
np.random.seed(42)
15+
16+
months = [
17+
"January",
18+
"February",
19+
"March",
20+
"April",
21+
"May",
22+
"June",
23+
"July",
24+
"August",
25+
"September",
26+
"October",
27+
"November",
28+
"December",
29+
]
30+
31+
# Generate realistic temperature distributions for each month (Northern Hemisphere)
32+
base_temps = [2, 4, 8, 12, 17, 21, 24, 23, 19, 13, 7, 3]
33+
data = {}
34+
for i, month in enumerate(months):
35+
temps = np.random.normal(base_temps[i], 3, 200)
36+
data[month] = temps
37+
38+
# Colors for gradient effect (from cool to warm and back)
39+
colors = [
40+
"#306998",
41+
"#3B7AAF",
42+
"#4A8BC5",
43+
"#59A5DC",
44+
"#FFD43B",
45+
"#F97316",
46+
"#DC2626",
47+
"#F97316",
48+
"#FFD43B",
49+
"#59A5DC",
50+
"#4A8BC5",
51+
"#306998",
52+
]
53+
54+
# Create figure
55+
p = figure(
56+
width=4800,
57+
height=2700,
58+
title="Monthly Temperature Distributions",
59+
x_axis_label="Temperature (°C)",
60+
y_axis_label="",
61+
tools="",
62+
toolbar_location=None,
63+
)
64+
65+
# Calculate KDE for each category and plot as overlapping ridges
66+
n_categories = len(months)
67+
overlap = 0.7 # Overlap factor for ridgeline effect
68+
x_range = np.linspace(-10, 35, 500)
69+
70+
# Plot from bottom to top (reversed order for proper layering)
71+
for idx, month in enumerate(reversed(months)):
72+
values = data[month]
73+
kde = stats.gaussian_kde(values)
74+
density = kde(x_range)
75+
76+
# Normalize density and scale for visual effect
77+
density_scaled = density / density.max() * 0.8
78+
79+
# Calculate vertical offset for this category
80+
y_offset = idx * overlap
81+
82+
# Create patch coordinates (filled area)
83+
xs = np.concatenate([[x_range[0]], x_range, [x_range[-1]]])
84+
ys = np.concatenate([[y_offset], density_scaled + y_offset, [y_offset]])
85+
86+
# Draw filled patch
87+
color_idx = n_categories - 1 - idx
88+
p.patch(xs, ys, fill_color=colors[color_idx], fill_alpha=0.7, line_color="white", line_width=1.5)
89+
90+
# Configure y-axis to show month labels
91+
y_positions = [i * overlap for i in range(n_categories)]
92+
y_labels = list(reversed(months))
93+
94+
p.yaxis.ticker = y_positions
95+
p.yaxis.major_label_overrides = dict(zip(y_positions, y_labels, strict=False))
96+
97+
# Styling
98+
p.y_range = Range1d(-0.3, (n_categories - 1) * overlap + 1.2)
99+
p.x_range = Range1d(-10, 35)
100+
101+
p.title.text_font_size = "32pt"
102+
p.xaxis.axis_label_text_font_size = "28pt"
103+
p.xaxis.major_label_text_font_size = "22pt"
104+
p.yaxis.major_label_text_font_size = "22pt"
105+
106+
p.xgrid.grid_line_color = "#E0E0E0"
107+
p.xgrid.grid_line_alpha = 0.5
108+
p.ygrid.grid_line_color = None
109+
110+
p.outline_line_color = None
111+
p.background_fill_color = "#FAFAFA"
112+
113+
# Save outputs
114+
export_png(p, filename="plot.png")
115+
116+
output_file("plot.html")
117+
save(p)

0 commit comments

Comments
 (0)