Skip to content

Commit 10aeb52

Browse files
feat(altair): implement subplot-grid-custom (#2883)
## Implementation: `subplot-grid-custom` - altair Implements the **altair** version of `subplot-grid-custom`. **File:** `plots/subplot-grid-custom/implementations/altair.py` **Parent Issue:** #2856 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20608471998)* --------- 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 3d12d5a commit 10aeb52

2 files changed

Lines changed: 154 additions & 0 deletions

File tree

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
""" pyplots.ai
2+
subplot-grid-custom: Custom Subplot Grid Layout
3+
Library: altair 6.0.0 | Python 3.13.11
4+
Quality: 91/100 | Created: 2025-12-30
5+
"""
6+
7+
import altair as alt
8+
import numpy as np
9+
import pandas as pd
10+
11+
12+
# Data
13+
np.random.seed(42)
14+
15+
# Time series data for main chart (spanning 2 columns)
16+
dates = pd.date_range("2024-01-01", periods=120, freq="D")
17+
base_price = 100 + np.cumsum(np.random.randn(120) * 2)
18+
price_df = pd.DataFrame({"date": dates, "price": base_price})
19+
20+
# Volume data for bar chart
21+
volume_df = pd.DataFrame({"date": dates, "volume": np.random.randint(500, 2000, 120)})
22+
23+
# Daily returns for histogram
24+
returns = np.diff(base_price) / base_price[:-1] * 100
25+
returns_df = pd.DataFrame({"returns": returns})
26+
27+
# Correlation scatter data
28+
scatter_df = pd.DataFrame({"feature_a": np.random.randn(80) * 15 + 50, "feature_b": np.random.randn(80) * 10 + 40})
29+
scatter_df["feature_b"] = scatter_df["feature_a"] * 0.6 + scatter_df["feature_b"]
30+
31+
# Category breakdown for pie-like (arc) chart
32+
category_df = pd.DataFrame(
33+
{"category": ["Product A", "Product B", "Product C", "Product D"], "value": [35, 28, 22, 15]}
34+
)
35+
36+
# Main time series chart (spans full width - equivalent to colspan=2)
37+
main_chart = (
38+
alt.Chart(price_df)
39+
.mark_line(strokeWidth=4, color="#306998")
40+
.encode(
41+
x=alt.X("date:T", title="Date", axis=alt.Axis(labelFontSize=16, titleFontSize=18)),
42+
y=alt.Y("price:Q", title="Price ($)", axis=alt.Axis(labelFontSize=16, titleFontSize=18)),
43+
)
44+
.properties(width=900, height=350, title=alt.Title("Daily Price Trend (Main View)", fontSize=22))
45+
)
46+
47+
# Volume bar chart
48+
volume_chart = (
49+
alt.Chart(volume_df)
50+
.mark_bar(color="#FFD43B", opacity=0.85)
51+
.encode(
52+
x=alt.X("date:T", title="Date", axis=alt.Axis(labelFontSize=14, titleFontSize=16)),
53+
y=alt.Y("volume:Q", title="Volume", axis=alt.Axis(labelFontSize=14, titleFontSize=16)),
54+
)
55+
.properties(width=440, height=200, title=alt.Title("Trading Volume", fontSize=18))
56+
)
57+
58+
# Returns histogram
59+
histogram_chart = (
60+
alt.Chart(returns_df)
61+
.mark_bar(color="#306998", opacity=0.75)
62+
.encode(
63+
x=alt.X(
64+
"returns:Q",
65+
bin=alt.Bin(maxbins=20),
66+
title="Daily Return (%)",
67+
axis=alt.Axis(labelFontSize=14, titleFontSize=16),
68+
),
69+
y=alt.Y("count():Q", title="Frequency", axis=alt.Axis(labelFontSize=14, titleFontSize=16)),
70+
)
71+
.properties(width=440, height=200, title=alt.Title("Return Distribution", fontSize=18))
72+
)
73+
74+
# Scatter plot for correlation
75+
scatter_chart = (
76+
alt.Chart(scatter_df)
77+
.mark_circle(size=120, color="#306998", opacity=0.65)
78+
.encode(
79+
x=alt.X("feature_a:Q", title="Feature A", axis=alt.Axis(labelFontSize=14, titleFontSize=16)),
80+
y=alt.Y("feature_b:Q", title="Feature B", axis=alt.Axis(labelFontSize=14, titleFontSize=16)),
81+
)
82+
.properties(width=440, height=200, title=alt.Title("Feature Correlation", fontSize=18))
83+
)
84+
85+
# Category breakdown using arc chart (donut)
86+
arc_chart = (
87+
alt.Chart(category_df)
88+
.mark_arc(innerRadius=50, outerRadius=90)
89+
.encode(
90+
theta=alt.Theta("value:Q"),
91+
color=alt.Color(
92+
"category:N",
93+
scale=alt.Scale(
94+
domain=["Product A", "Product B", "Product C", "Product D"],
95+
range=["#306998", "#FFD43B", "#4B8BBE", "#E6B800"],
96+
),
97+
legend=alt.Legend(labelFontSize=14, titleFontSize=16, orient="right", title="Category"),
98+
),
99+
)
100+
.properties(width=440, height=200, title=alt.Title("Category Breakdown", fontSize=18))
101+
)
102+
103+
# Create custom grid layout using vconcat and hconcat
104+
# Row 1: Main chart spanning full width (equivalent to colspan=2)
105+
# Row 2: Volume and Histogram side by side
106+
# Row 3: Scatter and Arc chart side by side
107+
108+
top_row = main_chart
109+
110+
middle_row = alt.hconcat(volume_chart, histogram_chart).resolve_scale(x="independent", y="independent")
111+
112+
bottom_row = alt.hconcat(scatter_chart, arc_chart).resolve_scale(x="independent", y="independent")
113+
114+
# Combine all rows vertically with main title
115+
combined = (
116+
alt.vconcat(top_row, middle_row, bottom_row)
117+
.resolve_scale(x="independent", y="independent")
118+
.configure(background="white")
119+
.configure_title(anchor="start", fontSize=24, offset=10)
120+
.configure_concat(spacing=25)
121+
.properties(title=alt.Title("subplot-grid-custom · altair · pyplots.ai", fontSize=30, anchor="middle", offset=15))
122+
)
123+
124+
# Save outputs
125+
combined.save("plot.png", scale_factor=3.0)
126+
combined.save("plot.html")
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
library: altair
2+
specification_id: subplot-grid-custom
3+
created: '2025-12-30T23:54:59Z'
4+
updated: '2025-12-31T00:01:20Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20608471998
7+
issue: 2856
8+
python_version: 3.13.11
9+
library_version: 6.0.0
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/subplot-grid-custom/altair/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/subplot-grid-custom/altair/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/subplot-grid-custom/altair/plot.html
13+
quality_score: 91
14+
review:
15+
strengths:
16+
- Excellent implementation of dashboard-style grid using Altair's native vconcat/hconcat
17+
composition
18+
- Strong visual hierarchy with main chart appropriately larger than detail panels
19+
- Good use of consistent color scheme (Python blue/gold theme)
20+
- Comprehensive financial dashboard scenario with meaningful interconnected data
21+
- Clean declarative code structure showcasing Altair's strengths
22+
weaknesses:
23+
- Legend for category breakdown appears isolated from the donut chart (top-right
24+
instead of near the chart)
25+
- Could demonstrate rowspan capability (e.g., a tall sidebar chart) in addition
26+
to colspan
27+
- Tick labels on x-axis of main chart are dense and could benefit from rotation
28+
or fewer ticks

0 commit comments

Comments
 (0)