+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 379 of 541

πŸš€ Matplotlib Advanced: Subplots and Styling

Master matplotlib advanced subplots and styling in Python with practical examples, best practices, and real-world applications πŸš€

πŸš€Intermediate
35 min read

Prerequisites

  • Basic understanding of programming concepts πŸ“
  • Python installation (3.8+) 🐍
  • VS Code or preferred IDE πŸ’»

What you'll learn

  • Understand the concept fundamentals 🎯
  • Apply the concept in real projects πŸ—οΈ
  • Debug common issues πŸ›
  • Write clean, Pythonic code ✨

🎯 Introduction

Welcome to this exciting tutorial on Matplotlib Advanced: Subplots and Styling! πŸŽ‰ In this guide, we’ll explore how to create professional, publication-ready visualizations that tell compelling data stories.

You’ll discover how advanced subplot layouts and custom styling can transform your data visualizations from basic charts to stunning insights. Whether you’re building dashboards πŸ“Š, scientific reports πŸ“ˆ, or interactive presentations 🎭, mastering these techniques is essential for effective data communication.

By the end of this tutorial, you’ll feel confident creating complex multi-panel figures with custom themes! Let’s dive in! πŸŠβ€β™‚οΈ

πŸ“š Understanding Subplots and Styling

πŸ€” What are Subplots?

Subplots are like apartment buildings for your plots 🏒. Think of it as dividing your canvas into multiple rooms, where each room can display its own visualization that tells part of your data story.

In Matplotlib terms, subplots allow you to create multiple axes within a single figure. This means you can:

  • ✨ Compare different datasets side by side
  • πŸš€ Show multiple perspectives of the same data
  • πŸ›‘οΈ Create dashboard-style visualizations

πŸ’‘ Why Master Advanced Subplots?

Here’s why data scientists love advanced subplot techniques:

  1. Efficient Space Usage πŸ”’: Pack more insights into less space
  2. Better Comparisons πŸ’»: Easy side-by-side analysis
  3. Professional Layouts πŸ“–: Publication-ready figures
  4. Storytelling Power πŸ”§: Guide viewers through your data narrative

Real-world example: Imagine building a weather dashboard 🌀️. With subplots, you can show temperature trends, precipitation, humidity, and wind patterns all in one cohesive view!

πŸ”§ Basic Syntax and Usage

πŸ“ Simple Subplot Creation

Let’s start with a friendly example:

import matplotlib.pyplot as plt
import numpy as np

# πŸ‘‹ Hello, Subplots!
fig, axes = plt.subplots(2, 2, figsize=(10, 8))

# 🎨 Generate some sample data
x = np.linspace(0, 10, 100)

# πŸ“Š Plot in each subplot
axes[0, 0].plot(x, np.sin(x))
axes[0, 0].set_title('Sine Wave 🌊')

axes[0, 1].plot(x, np.cos(x), 'r-')
axes[0, 1].set_title('Cosine Wave πŸ”΄')

axes[1, 0].plot(x, x**2, 'g-')
axes[1, 0].set_title('Quadratic πŸ“ˆ')

axes[1, 1].plot(x, np.sqrt(x), 'm-')
axes[1, 1].set_title('Square Root 🌿')

plt.tight_layout()
plt.show()

πŸ’‘ Explanation: Notice how we use subplots(2, 2) to create a 2x2 grid! The tight_layout() automatically adjusts spacing for a clean look.

🎯 Common Subplot Patterns

Here are patterns you’ll use daily:

# πŸ—οΈ Pattern 1: Uneven layouts with GridSpec
from matplotlib.gridspec import GridSpec

fig = plt.figure(figsize=(12, 8))
gs = GridSpec(3, 3, figure=fig)

# 🎨 Create subplots of different sizes
ax1 = fig.add_subplot(gs[0, :])      # Top row, all columns
ax2 = fig.add_subplot(gs[1:, 0])     # Bottom 2 rows, first column
ax3 = fig.add_subplot(gs[1:, 1:])    # Bottom 2 rows, last 2 columns

# πŸ”„ Pattern 2: Sharing axes
fig, axes = plt.subplots(2, 2, sharex=True, sharey=True)

# 🎯 Pattern 3: Subplot with different scales
fig, (ax1, ax2) = plt.subplots(1, 2)
ax2_twin = ax2.twinx()  # Create second y-axis

πŸ’‘ Practical Examples

πŸ“Š Example 1: Stock Market Dashboard

Let’s build something real:

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from datetime import datetime, timedelta

# πŸ“ˆ Generate sample stock data
dates = pd.date_range(start='2024-01-01', periods=100, freq='D')
stock_data = {
    'AAPL': 150 + np.cumsum(np.random.randn(100) * 2),
    'GOOGL': 100 + np.cumsum(np.random.randn(100) * 1.5),
    'MSFT': 300 + np.cumsum(np.random.randn(100) * 2.5),
    'Volume': np.random.randint(1000000, 5000000, 100)
}

# 🎨 Create custom style
plt.style.use('seaborn-v0_8-darkgrid')

# πŸ—οΈ Create dashboard layout
fig = plt.figure(figsize=(15, 10))
gs = GridSpec(3, 2, figure=fig, height_ratios=[2, 1, 1])

# πŸ“Š Main price chart
ax_main = fig.add_subplot(gs[0, :])
for stock, prices in stock_data.items():
    if stock != 'Volume':
        ax_main.plot(dates, prices, label=f'{stock} πŸ“ˆ', linewidth=2)

ax_main.set_title('Stock Price Trends πŸ’Ή', fontsize=16, fontweight='bold')
ax_main.set_xlabel('Date')
ax_main.set_ylabel('Price ($)')
ax_main.legend(loc='upper left')
ax_main.grid(True, alpha=0.3)

# πŸ“Š Volume chart
ax_volume = fig.add_subplot(gs[1, :])
ax_volume.bar(dates, stock_data['Volume'], color='skyblue', alpha=0.7)
ax_volume.set_title('Trading Volume πŸ“Š', fontsize=14)
ax_volume.set_ylabel('Volume')

# πŸ“ˆ Individual performance
ax_perf1 = fig.add_subplot(gs[2, 0])
returns = [(stock_data['AAPL'][-1] - stock_data['AAPL'][0]) / stock_data['AAPL'][0] * 100,
           (stock_data['GOOGL'][-1] - stock_data['GOOGL'][0]) / stock_data['GOOGL'][0] * 100,
           (stock_data['MSFT'][-1] - stock_data['MSFT'][0]) / stock_data['MSFT'][0] * 100]
colors = ['green' if r > 0 else 'red' for r in returns]
ax_perf1.bar(['AAPL', 'GOOGL', 'MSFT'], returns, color=colors)
ax_perf1.set_title('Returns % πŸ’°', fontsize=14)
ax_perf1.axhline(y=0, color='black', linestyle='-', linewidth=0.5)

# πŸ“Š Correlation heatmap
ax_corr = fig.add_subplot(gs[2, 1])
corr_data = np.random.rand(3, 3)
im = ax_corr.imshow(corr_data, cmap='RdYlGn', aspect='auto')
ax_corr.set_xticks([0, 1, 2])
ax_corr.set_yticks([0, 1, 2])
ax_corr.set_xticklabels(['AAPL', 'GOOGL', 'MSFT'])
ax_corr.set_yticklabels(['AAPL', 'GOOGL', 'MSFT'])
ax_corr.set_title('Correlation Matrix πŸ”—', fontsize=14)
plt.colorbar(im, ax=ax_corr)

plt.suptitle('Stock Market Dashboard πŸ“ˆ', fontsize=20, fontweight='bold')
plt.tight_layout()
plt.show()

🎯 Try it yourself: Add a moving average indicator and volatility chart!

🌑️ Example 2: Weather Analysis Dashboard

Let’s make it fun:

# 🌀️ Weather monitoring system
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Circle
from matplotlib.collections import PatchCollection

# 🎨 Custom color scheme
plt.rcParams['figure.facecolor'] = '#f0f0f0'
plt.rcParams['axes.facecolor'] = 'white'
plt.rcParams['axes.edgecolor'] = '#cccccc'
plt.rcParams['grid.color'] = '#e0e0e0'

# πŸ“Š Generate weather data
hours = np.arange(24)
temp = 20 + 10 * np.sin((hours - 6) * np.pi / 12) + np.random.randn(24)
humidity = 60 + 20 * np.sin((hours + 3) * np.pi / 12) + np.random.randn(24) * 5
wind_speed = 5 + 3 * np.sin(hours * np.pi / 6) + np.random.randn(24)

# πŸ—οΈ Create dashboard
fig = plt.figure(figsize=(16, 10))
gs = GridSpec(3, 3, figure=fig)

# 🌑️ Temperature gauge (main display)
ax_temp = fig.add_subplot(gs[:2, 0])
ax_temp.plot(hours, temp, 'r-', linewidth=3, label='Temperature')
ax_temp.fill_between(hours, temp, alpha=0.3, color='red')
ax_temp.set_title('Temperature Throughout the Day 🌑️', fontsize=16, pad=20)
ax_temp.set_xlabel('Hour of Day')
ax_temp.set_ylabel('Temperature (Β°C)')
ax_temp.grid(True, alpha=0.3)

# Add temperature zones
ax_temp.axhspan(30, 40, alpha=0.2, color='red', label='Hot πŸ”₯')
ax_temp.axhspan(20, 30, alpha=0.2, color='orange', label='Warm β˜€οΈ')
ax_temp.axhspan(10, 20, alpha=0.2, color='green', label='Comfortable 😊')

# πŸ’§ Humidity chart
ax_humid = fig.add_subplot(gs[0, 1:])
ax_humid.plot(hours, humidity, 'b-', linewidth=2, marker='o', markersize=4)
ax_humid.set_title('Humidity Levels πŸ’§', fontsize=14)
ax_humid.set_ylabel('Humidity (%)')
ax_humid.set_ylim(0, 100)
ax_humid.grid(True, alpha=0.3)

# πŸ’¨ Wind speed with direction
ax_wind = fig.add_subplot(gs[1, 1:])
ax_wind.barh(hours[::2], wind_speed[::2], color='skyblue', alpha=0.7)
ax_wind.set_title('Wind Speed πŸ’¨', fontsize=14)
ax_wind.set_xlabel('Speed (km/h)')
ax_wind.set_ylabel('Hour')

# β˜€οΈ UV Index circular plot
ax_uv = fig.add_subplot(gs[2, 0], projection='polar')
theta = np.linspace(0, 2*np.pi, 24)
uv_index = 5 + 3 * np.sin(theta) + np.random.rand(24)
ax_uv.plot(theta, uv_index, 'orange', linewidth=2)
ax_uv.fill(theta, uv_index, alpha=0.3, color='orange')
ax_uv.set_title('UV Index β˜€οΈ', pad=20)
ax_uv.set_ylim(0, 11)

# 🌈 Weather summary
ax_summary = fig.add_subplot(gs[2, 1:])
ax_summary.axis('off')
summary_text = f"""
🌑️ Max Temp: {temp.max():.1f}°C
🌑️ Min Temp: {temp.min():.1f}°C
πŸ’§ Avg Humidity: {humidity.mean():.1f}%
πŸ’¨ Max Wind: {wind_speed.max():.1f} km/h
β˜€οΈ Peak UV: {uv_index.max():.1f}

Weather Status: {"🌞 Sunny" if temp.mean() > 25 else "☁️ Cloudy"}
"""
ax_summary.text(0.1, 0.5, summary_text, fontsize=14, 
               transform=ax_summary.transAxes, verticalalignment='center',
               bbox=dict(boxstyle="round,pad=0.5", facecolor="lightblue", alpha=0.5))

plt.suptitle('Weather Monitoring Dashboard 🌀️', fontsize=20, fontweight='bold')
plt.tight_layout()
plt.show()

πŸš€ Advanced Concepts

πŸ§™β€β™‚οΈ Advanced Styling with Custom Themes

When you’re ready to level up, try creating professional themes:

# 🎯 Create a custom publication-ready style
def create_publication_style():
    plt.rcParams.update({
        # 🎨 Figure settings
        'figure.figsize': (10, 6),
        'figure.dpi': 300,
        'savefig.dpi': 300,
        'savefig.bbox': 'tight',
        
        # πŸ“ Font settings
        'font.family': 'sans-serif',
        'font.sans-serif': ['Arial', 'DejaVu Sans'],
        'font.size': 12,
        'axes.titlesize': 16,
        'axes.labelsize': 14,
        'xtick.labelsize': 12,
        'ytick.labelsize': 12,
        'legend.fontsize': 12,
        
        # 🎨 Color settings
        'axes.prop_cycle': plt.cycler('color', 
            ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', 
             '#9467bd', '#8c564b', '#e377c2', '#7f7f7f']),
        
        # πŸ“Š Grid and spines
        'axes.grid': True,
        'grid.alpha': 0.3,
        'axes.spines.top': False,
        'axes.spines.right': False,
        'axes.linewidth': 1.5,
        
        # ✨ Special effects
        'figure.facecolor': 'white',
        'axes.facecolor': 'white',
        'axes.edgecolor': '#333333',
    })

# πŸͺ„ Apply the custom style
create_publication_style()

# 🎯 Demo with styled plot
fig, ax = plt.subplots()
x = np.linspace(0, 10, 100)
for i in range(4):
    ax.plot(x, np.sin(x + i), label=f'Wave {i+1} 🌊', linewidth=2.5)
ax.set_title('Professional Publication Style πŸ“š')
ax.set_xlabel('Time (s)')
ax.set_ylabel('Amplitude')
ax.legend(frameon=True, shadow=True)
plt.show()

πŸ—οΈ Complex Layouts with Nested Subplots

For the brave developers:

# πŸš€ Advanced nested subplot layout
fig = plt.figure(figsize=(16, 12))

# πŸ“Š Create main grid
outer_grid = GridSpec(2, 2, figure=fig, hspace=0.3, wspace=0.3)

# 🎯 Top-left: Multiple time series
inner_grid1 = GridSpec(3, 1, figure=fig, 
                      left=0.05, right=0.45, top=0.95, bottom=0.55)
for i in range(3):
    ax = fig.add_subplot(inner_grid1[i, 0])
    ax.plot(np.random.randn(100).cumsum())
    ax.set_title(f'Signal {i+1} πŸ“‘')

# πŸ“ˆ Top-right: Heatmap
ax_heat = fig.add_subplot(outer_grid[0, 1])
data = np.random.randn(20, 20)
im = ax_heat.imshow(data, cmap='viridis', aspect='auto')
ax_heat.set_title('Correlation Heatmap πŸ”₯')
plt.colorbar(im, ax=ax_heat)

# πŸ“Š Bottom: Large comparison chart
ax_bottom = fig.add_subplot(outer_grid[1, :])
categories = ['Alpha', 'Beta', 'Gamma', 'Delta', 'Epsilon']
values1 = np.random.rand(5) * 100
values2 = np.random.rand(5) * 100
x = np.arange(len(categories))
width = 0.35

bars1 = ax_bottom.bar(x - width/2, values1, width, label='Group A πŸ…°οΈ')
bars2 = ax_bottom.bar(x + width/2, values2, width, label='Group B πŸ…±οΈ')
ax_bottom.set_xlabel('Categories')
ax_bottom.set_ylabel('Values')
ax_bottom.set_title('Comparative Analysis πŸ“Š')
ax_bottom.set_xticks(x)
ax_bottom.set_xticklabels(categories)
ax_bottom.legend()

plt.suptitle('Advanced Nested Layout Demo 🎨', fontsize=20)
plt.show()

⚠️ Common Pitfalls and Solutions

😱 Pitfall 1: Overlapping Subplots

# ❌ Wrong way - subplots overlap!
fig, axes = plt.subplots(2, 2, figsize=(8, 6))
for ax in axes.flat:
    ax.plot(np.random.randn(100))
    ax.set_title('This title might overlap! 😰')
plt.show()

# βœ… Correct way - use tight_layout!
fig, axes = plt.subplots(2, 2, figsize=(8, 6))
for ax in axes.flat:
    ax.plot(np.random.randn(100))
    ax.set_title('Clean layout! 🎯')
plt.tight_layout()  # Magic spacing fix! ✨
plt.show()

🀯 Pitfall 2: Inconsistent Scales

# ❌ Dangerous - different scales make comparison hard!
fig, (ax1, ax2) = plt.subplots(1, 2)
ax1.plot([1, 2, 3], [10, 20, 30])
ax2.plot([1, 2, 3], [1000, 2000, 3000])

# βœ… Safe - share y-axis for fair comparison!
fig, (ax1, ax2) = plt.subplots(1, 2, sharey=True)
ax1.plot([1, 2, 3], [10, 20, 30])
ax2.plot([1, 2, 3], [15, 25, 35])
plt.show()

πŸ› οΈ Best Practices

  1. 🎯 Plan Your Layout: Sketch before coding - know your story!
  2. πŸ“ Consistent Styling: Use the same colors and fonts throughout
  3. πŸ›‘οΈ White Space: Don’t overcrowd - let your plots breathe
  4. 🎨 Color Wisely: Consider colorblind-friendly palettes
  5. ✨ Label Everything: Axes, titles, and legends are essential

πŸ§ͺ Hands-On Exercise

🎯 Challenge: Build a Data Science Portfolio Dashboard

Create a comprehensive dashboard showing:

πŸ“‹ Requirements:

  • βœ… Model performance metrics (accuracy, precision, recall)
  • 🏷️ Feature importance visualization
  • πŸ‘€ Confusion matrix heatmap
  • πŸ“… Training history over time
  • 🎨 Custom color scheme matching your style!

πŸš€ Bonus Points:

  • Add interactive elements
  • Implement responsive layout
  • Create reusable plotting functions

πŸ’‘ Solution

πŸ” Click to see solution
# 🎯 Data Science Portfolio Dashboard
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.gridspec import GridSpec
import seaborn as sns

# 🎨 Set custom style
plt.style.use('seaborn-v0_8-whitegrid')
custom_colors = ['#3498db', '#e74c3c', '#2ecc71', '#f39c12', '#9b59b6']
plt.rcParams['axes.prop_cycle'] = plt.cycler(color=custom_colors)

# πŸ“Š Generate sample ML metrics
epochs = np.arange(1, 51)
train_loss = 1.0 / (1 + 0.1 * epochs) + 0.05 * np.random.randn(50)
val_loss = train_loss + 0.1 + 0.05 * np.random.randn(50)
accuracy = 1 - train_loss + 0.1 * np.random.randn(50)

# πŸ—οΈ Create dashboard
fig = plt.figure(figsize=(18, 12))
gs = GridSpec(3, 3, figure=fig, hspace=0.3, wspace=0.3)

# πŸ“ˆ Training history
ax_history = fig.add_subplot(gs[0, :2])
ax_history.plot(epochs, train_loss, label='Training Loss πŸ“‰', linewidth=2)
ax_history.plot(epochs, val_loss, label='Validation Loss πŸ“Š', linewidth=2)
ax_history.plot(epochs, accuracy, label='Accuracy 🎯', linewidth=2)
ax_history.set_title('Model Training History πŸ“š', fontsize=16, fontweight='bold')
ax_history.set_xlabel('Epoch')
ax_history.set_ylabel('Metric Value')
ax_history.legend()
ax_history.grid(True, alpha=0.3)

# πŸ“Š Performance metrics
ax_metrics = fig.add_subplot(gs[0, 2])
metrics = ['Accuracy', 'Precision', 'Recall', 'F1-Score']
values = [0.92, 0.89, 0.94, 0.91]
colors_metrics = ['#3498db', '#2ecc71', '#f39c12', '#e74c3c']
bars = ax_metrics.bar(metrics, values, color=colors_metrics)
ax_metrics.set_title('Model Performance πŸ†', fontsize=14)
ax_metrics.set_ylim(0, 1)
ax_metrics.set_ylabel('Score')

# Add value labels on bars
for bar, value in zip(bars, values):
    height = bar.get_height()
    ax_metrics.text(bar.get_x() + bar.get_width()/2., height + 0.02,
                    f'{value:.2f}', ha='center', va='bottom')

# πŸ”₯ Confusion matrix
ax_conf = fig.add_subplot(gs[1, :2])
conf_matrix = np.array([[85, 5, 2], [3, 92, 5], [1, 4, 88]])
sns.heatmap(conf_matrix, annot=True, fmt='d', cmap='Blues', 
            cbar=True, ax=ax_conf)
ax_conf.set_title('Confusion Matrix 🎲', fontsize=14)
ax_conf.set_xlabel('Predicted')
ax_conf.set_ylabel('Actual')

# πŸ“Š Feature importance
ax_feat = fig.add_subplot(gs[1, 2])
features = ['Feature A', 'Feature B', 'Feature C', 'Feature D', 'Feature E']
importance = [0.25, 0.20, 0.18, 0.15, 0.12]
ax_feat.barh(features, importance, color='#9b59b6')
ax_feat.set_title('Feature Importance πŸ”‘', fontsize=14)
ax_feat.set_xlabel('Importance Score')

# πŸ“ˆ ROC curve
ax_roc = fig.add_subplot(gs[2, 0])
fpr = np.linspace(0, 1, 100)
tpr = np.sqrt(fpr) + 0.1 * np.random.randn(100)
tpr = np.clip(tpr, 0, 1)
ax_roc.plot(fpr, tpr, 'b-', linewidth=2, label='Model (AUC=0.89)')
ax_roc.plot([0, 1], [0, 1], 'k--', alpha=0.5, label='Random')
ax_roc.set_title('ROC Curve πŸ“ˆ', fontsize=14)
ax_roc.set_xlabel('False Positive Rate')
ax_roc.set_ylabel('True Positive Rate')
ax_roc.legend()
ax_roc.grid(True, alpha=0.3)

# 🎯 Class distribution
ax_dist = fig.add_subplot(gs[2, 1])
classes = ['Class A', 'Class B', 'Class C']
counts = [150, 180, 120]
wedges, texts, autotexts = ax_dist.pie(counts, labels=classes, 
                                        autopct='%1.1f%%', startangle=90)
ax_dist.set_title('Dataset Distribution πŸ₯§', fontsize=14)

# πŸ“ Model summary
ax_summary = fig.add_subplot(gs[2, 2])
ax_summary.axis('off')
summary_text = """
πŸ€– Model: RandomForest
πŸ“Š Dataset: 10,000 samples
🎯 Task: Multi-class Classification
⏱️ Training Time: 45.3 minutes
πŸ’Ύ Model Size: 12.5 MB

Status: βœ… Production Ready!
"""
ax_summary.text(0.1, 0.5, summary_text, fontsize=12,
                transform=ax_summary.transAxes, verticalalignment='center',
                bbox=dict(boxstyle="round,pad=0.5", facecolor="#ecf0f1"))

plt.suptitle('Machine Learning Model Dashboard πŸš€', fontsize=24, fontweight='bold')
plt.tight_layout()
plt.show()

πŸŽ“ Key Takeaways

You’ve learned so much! Here’s what you can now do:

  • βœ… Create complex subplot layouts with confidence πŸ’ͺ
  • βœ… Apply professional styling to your visualizations πŸ›‘οΈ
  • βœ… Build dashboard-style figures for real projects 🎯
  • βœ… Debug layout issues like a pro πŸ›
  • βœ… Design beautiful data stories with Matplotlib! πŸš€

Remember: Great visualizations are about clarity, not complexity! Keep your audience in mind. 🀝

🀝 Next Steps

Congratulations! πŸŽ‰ You’ve mastered advanced subplots and styling!

Here’s what to do next:

  1. πŸ’» Practice with the dashboard exercise above
  2. πŸ—οΈ Build a visualization portfolio with your own data
  3. πŸ“š Move on to our next tutorial: Seaborn for Statistical Plots
  4. 🌟 Share your beautiful visualizations with the data science community!

Remember: Every data visualization expert was once a beginner. Keep plotting, keep styling, and most importantly, have fun telling data stories! πŸš€


Happy plotting! πŸŽ‰πŸš€βœ¨