import numpy as np
from scipy.stats import gaussian_kde
from bokeh.plotting import figure, show
from bokeh.models import LinearColorMapper, ColorBar, FixedTicker
from bokeh.io import output_notebook
from matplotlib import cm
from matplotlib.colors import to_hex
output_notebook() # Remove this line if running as .py script
# --- 1. Setup years and simulate temperature anomaly data ---
N_YEARS = 40
years = np.arange(2024 - N_YEARS + 1, 2025) # 1985–2024
means = np.linspace(-0.3, 1.0, N_YEARS)[::-1] # Simulate increasing anomaly
data_per_year = [np.random.normal(loc=mu, scale=0.18, size=200) for mu in means]
# --- 2. Colormap setup ---
rdblue256 = [to_hex(cm.get_cmap('RdBu_r')(i/255)) for i in range(256)] # Blue (cold) to Red (warm)
min_anom, max_anom = float(min(means)), float(max(means))
color_mapper = LinearColorMapper(palette=rdblue256, low=min_anom, high=max_anom)
# --- 3. Bokeh figure setup ---
p = figure(
width=900,
height=700,
y_range=[str(y) for y in years[::-1]], # Newest year at top
x_axis_label="Temperature anomaly (°C)",
toolbar_location=None,
outline_line_color=None,
title="Global Annual Temperature Anomalies (Last 40 Years)"
)
# --- 4. Plot joyplots: LATEST year is on top ---
for i in reversed(range(len(years))):
year = years[i]
year_data = data_per_year[i]
mean_anom = means[i]
kde = gaussian_kde(year_data)
x = np.linspace(year_data.min() - 0.3, year_data.max() + 0.3, 300)
y = kde(x)
y_offset = i * 1.0
y_scaled = y / y.max() * 1.7
color = to_hex(cm.get_cmap('RdBu_r')((mean_anom - min_anom) / (max_anom - min_anom)))
p.patch(x, y_offset + y_scaled, color=color, alpha=1, line_color="black", line_width=1.0)
# --- 5. Style ---
p.yaxis.axis_label = "Year"
p.yaxis.major_label_text_font_size = "12pt"
p.xgrid.visible = False
p.ygrid.visible = False
p.background_fill_color = "#fafafa"
p.title.text_font_size = "18pt"
p.xaxis.axis_label_text_font_style = "bold"
p.yaxis.axis_label_text_font_style = "bold"
p.outline_line_alpha = 0
p.border_fill_color = '#fafafa'
# --- 6. Colorbar for anomaly color coding ---
color_bar = ColorBar(
color_mapper=color_mapper,
location=(0, 0),
width=24,
height=400,
title='Anomaly (°C)',
title_text_font_size="13pt",
major_label_text_font_size="12pt",
label_standoff=12,
ticker=FixedTicker(ticks=np.round(np.linspace(min_anom, max_anom, 7), 2)),
major_label_overrides={float(f"{v:.2f}"): f"{v:.2f}" for v in np.round(np.linspace(min_anom, max_anom, 7), 2)},
background_fill_color='#fafafa'
)
p.add_layout(color_bar, 'right')
show(p)
import numpy as np
from scipy.stats import gaussian_kde
from bokeh.plotting import figure, show
from bokeh.io import output_notebook
output_notebook() # Remove if running as script
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
# Use your favorite 12 colors
colors = [
"#1f77b4", "#ff7f0e", "#2ca02c", "#d62728",
"#9467bd", "#8c564b", "#e377c2", "#7f7f7f",
"#bcbd22", "#17becf", "#aec7e8", "#ffbb78"
]
p = figure(
width=800,
height=600,
y_range=months[::-1],
x_axis_label="Temperature (°C)",
toolbar_location=None,
outline_line_color=None
)
for i, month in enumerate(months):
# Simulate data for each month
mean = 5 + 10 * np.sin((i / 12) * 2 * np.pi) + 10
temps = np.random.normal(mean, 3, 200)
# KDE for smooth curve
kde = gaussian_kde(temps)
x = np.linspace(temps.min()-4, temps.max()+4, 300)
y = kde(x)
# Offset
y_offset = i * 1.0
y_scaled = y / y.max() * 0.7
p.patch(x, y_offset + y_scaled, color=colors[i], alpha=0.5, line_color="black", line_width=1.5)
# Style
p.yaxis.axis_label = "Month"
p.yaxis.major_label_text_font_size = "12pt"
p.xgrid.visible = False
p.ygrid.visible = False
p.background_fill_color = "#fafafa"
p.legend.visible = False
p.title.text = "Monthly Temperature Distribution Joyplot"
p.title.text_font_size = "18pt"
p.xaxis.axis_label_text_font_style = "bold"
p.yaxis.axis_label_text_font_style = "bold"
p.outline_line_alpha = 0
show(p)