Corner plot
Bayesian Inference — posterior samples
Example from the compendium of canonical charts
Python Code
"""Bayesian Inference — Corner plot (posterior samples)."""
from pathlib import Path
# ── Palette + theme (matching the Plotly Studio gallery these charts ship in) ──
VIOLET, TEAL, GREEN, PINK, ORANGE = "#845EEE", "#52B3D0", "#55B685", "#DA5597", "#E9A23B"
PRIMARY, SECONDARY = VIOLET, TEAL
COLORWAY = [VIOLET, TEAL, GREEN, PINK, ORANGE]
BG, TEXT, GRID, MUTED = "#ffffff", "#1c2024", "#d9d9e0", "#60646c"
FONT = "Inter, -apple-system, BlinkMacSystemFont, sans-serif"
COLORSCALE = [[0, "rgba(132, 94, 238, 0.05)"], [1, "rgba(132, 94, 238, 0.9)"]]
def apply_theme(fig):
"""Light gallery theme: white background, Inter font, soft gridlines."""
fig.update_layout(
paper_bgcolor=BG, plot_bgcolor=BG, colorway=COLORWAY,
font=dict(family=FONT, color=TEXT, size=12),
legend=dict(font=dict(color=TEXT)),
hoverlabel=dict(bgcolor="#f0f0f3", font=dict(color=TEXT, family=FONT), bordercolor=GRID),
)
fig.update_xaxes(gridcolor=GRID, linecolor=GRID, zerolinecolor=GRID)
fig.update_yaxes(gridcolor=GRID, linecolor=GRID, zerolinecolor=GRID)
def fetch_csv(url, **kwargs):
import io
import pandas as pd
import requests
r = requests.get(url, timeout=60)
r.raise_for_status()
return pd.read_csv(io.StringIO(r.text), **kwargs)
def fetch_json(url):
import requests
r = requests.get(url, timeout=60)
r.raise_for_status()
return r.json()
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots
def generate():
print("generating posterior corner plot …")
rng = np.random.default_rng(42)
n_samples = 5000
mean = [1.0, 2.0, 3.0]
cov = [
[1.0, 0.7, -0.5],
[0.7, 1.0, 0.3],
[-0.5, 0.3, 1.0],
]
samples = rng.multivariate_normal(mean, cov, n_samples)
param_names = ["θ₁", "θ₂", "θ₃"]
n_params = 3
# Compute axis ranges per parameter (±3.5 sigma)
ranges = []
for i in range(n_params):
mu = samples[:, i].mean()
sigma = samples[:, i].std()
ranges.append((mu - 3.5 * sigma, mu + 3.5 * sigma))
fig = make_subplots(
rows=n_params,
cols=n_params,
shared_xaxes=False,
shared_yaxes=False,
horizontal_spacing=0.06,
vertical_spacing=0.06,
)
for row in range(n_params):
for col in range(n_params):
if col > row:
# Upper triangle — leave blank
continue
elif col == row:
# Diagonal — 1D marginal histogram
fig.add_trace(
go.Histogram(
x=samples[:, row],
nbinsx=40,
marker_color=VIOLET,
opacity=0.8,
showlegend=False,
hovertemplate=f"{param_names[row]}: %{{x:.2f}}<br>Count: %{{y}}<extra></extra>",
),
row=row + 1,
col=col + 1,
)
# True-value vertical line
fig.add_vline(
x=mean[row],
line=dict(color=TEAL, width=1.5, dash="dash"),
row=row + 1,
col=col + 1,
)
else:
# Lower triangle — 2D joint histogram
fig.add_trace(
go.Histogram2d(
x=samples[:, col],
y=samples[:, row],
nbinsx=30,
nbinsy=30,
colorscale=COLORSCALE,
showscale=False,
hovertemplate=(
f"{param_names[col]}: %{{x:.2f}}<br>"
f"{param_names[row]}: %{{y:.2f}}<br>"
"Count: %{z}<extra></extra>"
),
),
row=row + 1,
col=col + 1,
)
# Axis labels on outer edges only
for i in range(n_params):
# Bottom row: x-axis labels
fig.update_xaxes(
title_text=param_names[i],
row=n_params,
col=i + 1,
)
# Left column: y-axis labels for off-diagonal
if i > 0:
fig.update_yaxes(
title_text=param_names[i],
row=i + 1,
col=1,
)
fig.update_layout(
margin=dict(t=50, b=60, l=80, r=60),
)
apply_theme(fig)
return fig
fig = generate()
fig.show()
Made with Plotly