Skip to content

Commit 2cdd688

Browse files
feat(altair): implement histogram-density (#2476)
## Implementation: `histogram-density` - altair Implements the **altair** version of `histogram-density`. **File:** `plots/histogram-density/implementations/altair.py` --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20584328879)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent adbefde commit 2cdd688

2 files changed

Lines changed: 101 additions & 0 deletions

File tree

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
""" pyplots.ai
2+
histogram-density: Density Histogram
3+
Library: altair 6.0.0 | Python 3.13.11
4+
Quality: 93/100 | Created: 2025-12-29
5+
"""
6+
7+
import altair as alt
8+
import numpy as np
9+
import pandas as pd
10+
from scipy import stats
11+
12+
13+
# Data - Generate bimodal distribution to show density histogram features
14+
np.random.seed(42)
15+
# Reaction times from two conditions: baseline and fatigued
16+
baseline_times = np.random.normal(loc=250, scale=30, size=350)
17+
fatigued_times = np.random.normal(loc=380, scale=45, size=150)
18+
reaction_times = np.concatenate([baseline_times, fatigued_times])
19+
20+
# Compute histogram data manually for density normalization
21+
num_bins = 25
22+
bins = np.linspace(reaction_times.min() - 10, reaction_times.max() + 10, num_bins + 1)
23+
counts, bin_edges = np.histogram(reaction_times, bins=bins, density=True)
24+
bin_width = bin_edges[1] - bin_edges[0]
25+
26+
# Create DataFrame with bin ranges for proper bar rendering
27+
hist_df = pd.DataFrame(
28+
{
29+
"bin_start": bin_edges[:-1],
30+
"bin_end": bin_edges[1:],
31+
"Density": counts,
32+
"bin_center": (bin_edges[:-1] + bin_edges[1:]) / 2,
33+
}
34+
)
35+
36+
# Create density histogram using rect mark for proper filled bars
37+
histogram = (
38+
alt.Chart(hist_df)
39+
.mark_rect(color="#306998", opacity=0.75, stroke="#1a3a5c", strokeWidth=1.5)
40+
.encode(
41+
x=alt.X("bin_start:Q", scale=alt.Scale(domain=[bins.min(), bins.max()]), title="Reaction Time (ms)"),
42+
x2="bin_end:Q",
43+
y=alt.Y("Density:Q", scale=alt.Scale(domain=[0, counts.max() * 1.1]), title="Density (probability per ms)"),
44+
tooltip=[
45+
alt.Tooltip("bin_center:Q", title="Bin Center", format=".0f"),
46+
alt.Tooltip("Density:Q", title="Density", format=".5f"),
47+
],
48+
)
49+
)
50+
51+
# Create KDE overlay for theoretical density reference
52+
kde = stats.gaussian_kde(reaction_times, bw_method=0.15)
53+
x_kde = np.linspace(reaction_times.min() - 20, reaction_times.max() + 20, 300)
54+
y_kde = kde(x_kde)
55+
56+
kde_df = pd.DataFrame({"Reaction Time (ms)": x_kde, "Density": y_kde})
57+
58+
kde_line = alt.Chart(kde_df).mark_line(color="#FFD43B", strokeWidth=4).encode(x="Reaction Time (ms):Q", y="Density:Q")
59+
60+
# Combine histogram and KDE
61+
chart = (
62+
alt.layer(histogram, kde_line)
63+
.properties(
64+
width=1600,
65+
height=900,
66+
title=alt.Title("histogram-density · altair · pyplots.ai", fontSize=28, anchor="middle", color="#333333"),
67+
)
68+
.configure_axis(labelFontSize=18, titleFontSize=22, gridColor="#cccccc", gridOpacity=0.3)
69+
.configure_view(strokeWidth=0)
70+
)
71+
72+
# Save as PNG (1600 × 900 × 3 = 4800 × 2700 px)
73+
chart.save("plot.png", scale_factor=3.0)
74+
75+
# Save interactive HTML version
76+
chart.save("plot.html")
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
library: altair
2+
specification_id: histogram-density
3+
created: '2025-12-29T22:49:29Z'
4+
updated: '2025-12-29T22:58:35Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20584328879
7+
issue: 0
8+
python_version: 3.13.11
9+
library_version: 6.0.0
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/histogram-density/altair/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/histogram-density/altair/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/histogram-density/altair/plot.html
13+
quality_score: 93
14+
review:
15+
strengths:
16+
- Excellent bimodal distribution demonstrates density histogram features very effectively
17+
- Clean visual design with proper color contrast between histogram bars and KDE
18+
overlay
19+
- Proper density normalization with clear axis labeling including units
20+
- Interactive tooltips provide additional value for exploration
21+
- Realistic cognitive psychology scenario with plausible reaction time values
22+
weaknesses:
23+
- Missing legend to identify the KDE line (yellow line has no label)
24+
- Could use Altair's built-in transform_density() instead of scipy for more idiomatic
25+
implementation

0 commit comments

Comments
 (0)