|
| 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") |
0 commit comments