|
| 1 | +""" pyplots.ai |
| 2 | +subplot-grid-custom: Custom Subplot Grid Layout |
| 3 | +Library: plotnine 0.15.2 | Python 3.13.11 |
| 4 | +Quality: 91/100 | Created: 2025-12-30 |
| 5 | +""" |
| 6 | + |
| 7 | +import numpy as np |
| 8 | +import pandas as pd |
| 9 | +from plotnine import ( |
| 10 | + aes, |
| 11 | + element_line, |
| 12 | + element_rect, |
| 13 | + element_text, |
| 14 | + geom_bar, |
| 15 | + geom_histogram, |
| 16 | + geom_line, |
| 17 | + geom_point, |
| 18 | + ggplot, |
| 19 | + labs, |
| 20 | + scale_fill_manual, |
| 21 | + stat_smooth, |
| 22 | + theme, |
| 23 | + theme_minimal, |
| 24 | +) |
| 25 | + |
| 26 | + |
| 27 | +# Data - Investment Portfolio Dashboard |
| 28 | +np.random.seed(42) |
| 29 | + |
| 30 | +# Main time series: Daily portfolio value over 60 days |
| 31 | +n_days = 60 |
| 32 | +dates = pd.date_range("2024-01-01", periods=n_days, freq="D") |
| 33 | +portfolio_value = 100000 + np.cumsum(np.random.randn(n_days) * 500 + 50) |
| 34 | +df_portfolio = pd.DataFrame({"day": range(n_days), "value": portfolio_value, "date": dates}) |
| 35 | + |
| 36 | +# Volume/Activity data - Trading volume per day |
| 37 | +volume = np.abs(np.random.randn(n_days) * 1000 + 3000) |
| 38 | +df_volume = pd.DataFrame({"day": range(n_days), "volume": volume}) |
| 39 | + |
| 40 | +# Asset allocation (pie chart equivalent - bar chart for plotnine) |
| 41 | +assets = ["Stocks", "Bonds", "Real Estate", "Cash"] |
| 42 | +allocations = [55, 25, 12, 8] |
| 43 | +df_allocation = pd.DataFrame( |
| 44 | + {"asset": pd.Categorical(assets, categories=assets, ordered=True), "allocation": allocations} |
| 45 | +) |
| 46 | + |
| 47 | +# Daily returns distribution |
| 48 | +returns = np.diff(portfolio_value) / portfolio_value[:-1] * 100 |
| 49 | +df_returns = pd.DataFrame({"returns": returns}) |
| 50 | + |
| 51 | +# Colors |
| 52 | +colors = ["#306998", "#FFD43B", "#4A8BBF", "#E07A5F"] |
| 53 | + |
| 54 | +# Shared theme - sized for 4800x2700 canvas |
| 55 | +base_theme = theme_minimal() + theme( |
| 56 | + plot_title=element_text(size=18, face="bold", ha="center", margin={"b": 8}), |
| 57 | + axis_title=element_text(size=16), |
| 58 | + axis_text=element_text(size=13), |
| 59 | + legend_text=element_text(size=13), |
| 60 | + legend_title=element_text(size=14, face="bold"), |
| 61 | + panel_grid_major=element_line(color="#cccccc", alpha=0.3), |
| 62 | + panel_grid_minor=element_line(color="#eeeeee", alpha=0.2), |
| 63 | + panel_background=element_rect(fill="white"), |
| 64 | + plot_margin=0.01, |
| 65 | +) |
| 66 | + |
| 67 | +# Small theme for right-side panels (more compact titles) |
| 68 | +small_theme = base_theme + theme( |
| 69 | + plot_title=element_text(size=16, face="bold", margin={"b": 5, "t": 5}), |
| 70 | + axis_title=element_text(size=14), |
| 71 | + axis_text=element_text(size=11), |
| 72 | +) |
| 73 | + |
| 74 | +# Main Plot (spans full left side): Portfolio Value Over Time |
| 75 | +p_main = ( |
| 76 | + ggplot(df_portfolio, aes(x="day", y="value")) |
| 77 | + + geom_line(size=1.8, color="#306998") |
| 78 | + + geom_point(size=2.5, color="#306998", alpha=0.6) |
| 79 | + + stat_smooth(method="lm", se=True, color="#FFD43B", fill="#FFD43B", alpha=0.2, size=1.2) |
| 80 | + + labs(title="Portfolio Value Trend", x="Trading Day", y="Portfolio Value ($)") |
| 81 | + + base_theme |
| 82 | + + theme(plot_title=element_text(size=22, margin={"b": 10, "t": 40})) |
| 83 | +) |
| 84 | + |
| 85 | +# Top Right: Asset Allocation (horizontal bar chart) |
| 86 | +p_allocation = ( |
| 87 | + ggplot(df_allocation, aes(x="asset", y="allocation", fill="asset")) |
| 88 | + + geom_bar(stat="identity", width=0.7, show_legend=False) |
| 89 | + + scale_fill_manual(values=colors) |
| 90 | + + labs(title="Asset Allocation", x="", y="Allocation (%)") |
| 91 | + + small_theme |
| 92 | + + theme(axis_text_x=element_text(size=11, angle=0)) |
| 93 | +) |
| 94 | + |
| 95 | +# Middle Right: Trading Volume |
| 96 | +p_volume = ( |
| 97 | + ggplot(df_volume, aes(x="day", y="volume")) |
| 98 | + + geom_bar(stat="identity", fill="#306998", alpha=0.7, width=0.8) |
| 99 | + + labs(title="Daily Trading Volume", x="Trading Day", y="Volume (Units)") |
| 100 | + + small_theme |
| 101 | +) |
| 102 | + |
| 103 | +# Bottom Right: Returns Distribution |
| 104 | +p_returns = ( |
| 105 | + ggplot(df_returns, aes(x="returns")) |
| 106 | + + geom_histogram(bins=15, fill="#FFD43B", color="#306998", alpha=0.8, size=0.5) |
| 107 | + + labs(title="Returns Distribution", x="Daily Return (%)", y="Frequency") |
| 108 | + + small_theme |
| 109 | +) |
| 110 | + |
| 111 | +# Create custom grid layout using plotnine composition operators |
| 112 | +# Layout: Main plot (full height left) | 3 stacked smaller plots (right column) |
| 113 | +# This creates a dashboard with main visualization and supporting detail panels |
| 114 | +right_column = p_allocation / p_volume / p_returns |
| 115 | +custom_grid = p_main | right_column |
| 116 | + |
| 117 | +# Draw and customize the figure |
| 118 | +fig = custom_grid.draw() |
| 119 | + |
| 120 | +# Set figure size for 4800x2700 px at 300 DPI |
| 121 | +fig.set_size_inches(16, 9) |
| 122 | + |
| 123 | +# Adjust subplot spacing for clean layout with main title |
| 124 | +fig.subplots_adjust(top=0.88, bottom=0.08, left=0.06, right=0.98, hspace=0.35, wspace=0.18) |
| 125 | + |
| 126 | +# Add main title positioned above all subplot titles |
| 127 | +fig.suptitle("subplot-grid-custom · plotnine · pyplots.ai", fontsize=28, fontweight="bold", y=0.98) |
| 128 | + |
| 129 | +# Save |
| 130 | +fig.savefig("plot.png", dpi=300, bbox_inches="tight", facecolor="white") |
0 commit comments