Master advanced plot customization, multi-axis layouts, and publication-quality figures with Matplotlib. Learn professional visualization techniques used in research and industry.
Matplotlib is the foundation of data visualization in Python, powering libraries like Seaborn and Pandas plotting. While creating basic plots is straightforward, mastering advanced Matplotlib techniques unlocks the ability to create publication-quality, professional visualizations that effectively communicate complex data insights.
Professional data scientists and researchers rely on Matplotlib's advanced features to create figures for academic papers, business presentations, and scientific journals. Understanding the object-oriented interface, customization options, and layout systems allows you to move beyond basic plots to create sophisticated, multi-panel figures that tell compelling data stories.
In this comprehensive lesson, you'll learn the advanced techniques that separate basic plotting from professional visualization. You'll master the figure-axes architecture, advanced customization, multi-plot layouts, and professional styling techniques that make Matplotlib the industry standard for scientific visualization.
Matplotlib's power comes from its hierarchical object-oriented architecture. Understanding this structure is essential for advanced customization.
The Matplotlib Hierarchy:
Why This Matters:
Many beginners use pyplot (plt) functions which create implicit figures and axes. Advanced users explicitly create and manipulate these objects for precise control.
Basic Approach (Implicit):
1import matplotlib.pyplot as plt
2plt.plot([1, 2, 3], [1, 4, 9])
3plt.show()Advanced Approach (Explicit):
1import matplotlib.pyplot as plt
2import numpy as np
3
4# Create figure and axes explicitly
5fig, ax = plt.subplots(figsize=(10, 6))
6
7# Generate data
8x = np.linspace(0, 10, 100)
9y1 = np.sin(x)
10y2 = np.cos(x)
11
12# Plot on the axes object
13line1 = ax.plot(x, y1, label='sin(x)', linewidth=2, color='#2563eb')
14line2 = ax.plot(x, y2, label='cos(x)', linewidth=2, color='#dc2626')
15
16# Customize axes
17ax.set_xlabel('X Values', fontsize=12, fontweight='bold')
18ax.set_ylabel('Y Values', fontsize=12, fontweight='bold')
19ax.set_title('Trigonometric Functions', fontsize=14, fontweight='bold')
20ax.legend(loc='upper right', frameon=True, shadow=True)
21ax.grid(True, alpha=0.3, linestyle='--')
22
23# Adjust figure-level properties
24fig.tight_layout()
25plt.show()Key Benefits:
Real-World Application:
Research papers often require multi-panel figures (Figure 1a, 1b, 1c). The object-oriented approach makes this manageable and reproducible.
Common Mistakes:
rcParams (runtime configuration parameters) control Matplotlib's default behavior. Mastering rcParams allows you to create consistent, professional styling across all visualizations.
What are rcParams?
rcParams are Matplotlib's global settings that control default values for figure size, fonts, colors, line widths, and hundreds of other properties.
Viewing Current Settings:
1import matplotlib as mpl
2print(mpl.rcParams['figure.figsize']) # Default figure size
3print(mpl.rcParams['font.size']) # Default font size
4print(mpl.rcParams['lines.linewidth']) # Default line widthTemporary Customization (Context Manager):
1import matplotlib.pyplot as plt
2import numpy as np
3
4# Temporarily change settings
5with plt.rc_context({'figure.figsize': (12, 8),
6 'font.size': 14,
7 'lines.linewidth': 2.5,
8 'lines.markersize': 10}):
9 fig, ax = plt.subplots()
10 x = np.linspace(0, 10, 50)
11 ax.plot(x, np.sin(x), 'o-', label='Sine Wave')
12 ax.legend()
13 plt.show()
14# Settings revert after contextGlobal Customization:
1import matplotlib.pyplot as plt
2
3# Set global defaults for entire session
4plt.rcParams['figure.figsize'] = (12, 8)
5plt.rcParams['font.family'] = 'sans-serif'
6plt.rcParams['font.sans-serif'] = ['Arial', 'Helvetica']
7plt.rcParams['font.size'] = 12
8plt.rcParams['axes.labelsize'] = 14
9plt.rcParams['axes.titlesize'] = 16
10plt.rcParams['xtick.labelsize'] = 11
11plt.rcParams['ytick.labelsize'] = 11
12plt.rcParams['legend.fontsize'] = 12
13plt.rcParams['lines.linewidth'] = 2
14plt.rcParams['axes.grid'] = True
15plt.rcParams['grid.alpha'] = 0.3Using Style Sheets:
Matplotlib includes built-in style sheets for professional looks:
1import matplotlib.pyplot as plt
2import numpy as np
3
4# Available styles
5print(plt.style.available)
6# ['seaborn', 'ggplot', 'fivethirtyeight', 'bmh', ...]
7
8# Use a style
9plt.style.use('seaborn-v0_8-darkgrid')
10
11# Or combine multiple styles
12plt.style.use(['seaborn-v0_8-whitegrid', 'seaborn-v0_8-talk'])
13
14# Create plot with applied style
15fig, ax = plt.subplots()
16x = np.linspace(0, 10, 100)
17ax.plot(x, np.exp(-x/10) * np.sin(x))
18plt.show()Custom Style Sheet:
Create a file
Creating sophisticated multi-panel figures is essential for comparing data, showing different perspectives, and creating publication-ready visualizations.
Basic Subplots:
1import matplotlib.pyplot as plt
2import numpy as np
3
4# Create 2x2 grid of subplots
5fig, axes = plt.subplots(2, 2, figsize=(12, 10))
6
7x = np.linspace(0, 10, 100)
8
9# Access individual axes
10axes[0, 0].plot(x, np.sin(x))
11axes[0, 0].set_title('Sine Wave')
12
13axes[0, 1].plot(x, np.cos(x), 'r-')
14axes[0, 1].set_title('Cosine Wave')
15
16axes[1, 0].plot(x, np.tan(x))
17axes[1, 0].set_title('Tangent Wave')
18axes[1, 0].set_ylim(-10, 10)
19
20axes[1, 1].plot(x, np.exp(-x/5))
21axes[1, 1].set_title('Exponential Decay')
22
23# Adjust spacing
24fig.tight_layout()
25plt.show()GridSpec for Asymmetric Layouts:
GridSpec allows precise control over subplot sizes and positions.
1import matplotlib.pyplot as plt
2import matplotlib.gridspec as gridspec
3import numpy as np
4
5fig = plt.figure(figsize=(12, 8))
6
7# Create grid: 3 rows, 3 columns
8gs = gridspec.GridSpec(3, 3, figure=fig,
9 hspace=0.3, wspace=0.3)
10
11# Large plot spanning top 2 rows, all columns
12ax1 = fig.add_subplot(gs[0:2, :])
13x = np.linspace(0, 10, 1000)
14ax1.plot(x, np.sin(x) * np.exp(-x/10))
15ax1.set_title('Main Plot: Damped Sine Wave', fontsize=14, fontweight='bold')
16
17# Three smaller plots on bottom row
18ax2 = fig.add_subplot(gs[2, 0])
19ax2.hist(np.random.randn(1000), bins=30, color='#2563eb', alpha=0.7)
20ax2.set_title('Histogram')
21
22ax3 = fig.add_subplot(gs[2, 1])
23ax3.scatter(np.random.rand(100), np.random.rand(100),
24 c=np.random.rand(100), cmap='viridis')
25ax3.set_title('Scatter Plot')
26
27ax4 = fig.add_subplot(gs[2, 2])
28ax4.boxplot([np.random.randn(100) for _ in range(4)])
29ax4.set_title('Box Plot')
30
31plt.show()Nested GridSpecs:
1import matplotlib.pyplot as plt
2import matplotlib.gridspec as gridspec
3
4fig = plt.figure(figsize=(14, 10))
5
6# Outer grid: 1 row, 2 columns
7outer_grid = gridspec.GridSpec(1, 2, figure=fig, wspace=0.3)
8
9# Left panel: single large plot
10ax_left = fig.add_subplot(outer_grid[0, 0])
11ax_left.plot([1, 2, 3, 4], [1, 4, 9, 16])
12ax_left.set_title('Main Analysis')
13
14# Right panel: nested 2x2 grid
15inner_grid = gridspec.GridSpecFromSubplotSpec(
16 2, 2, subplot_spec=outer_grid[0, 1],
17 wspace=0.3, hspace=0.3
18)
19
20for i in range(4):
21 ax = fig.add_subplot(inner_grid[i])
22 ax.plot([1, 2, 3], [i+1, (i+1)*2, (i+1)*3])
23 ax.set_title(f'Detail {i+1}')
24
25plt.show()Shared Axes:
1import matplotlib.pyplot as plt
2import numpy as np
3
4fig, axes = plt.subplots(2, 1, figsize=(10, 8), sharex=True)
5
6x = np.linspace(0, 10, 100)
7
8# Top plot
9axes[0].plot(x, np.sin(x))
10axes[0].set_ylabel('Sine')
11axes[0].grid(True, alpha=0.3)
12
13# Bottom plot shares X-axis
14axes[1].plot(x, np.cos(x), 'r-')
15axes[1].set_ylabel('Cosine')
16axes[1].set_xlabel('X Values')
17axes[1].grid(True, alpha=0.3)
18
19plt.show()Professional Use Case:
Academic papers often require "Figure 1: (a) Overview, (b) Detail 1, (c) Detail 2" layouts. GridSpec makes this reproducible and adjustable.
Color choice dramatically impacts visualization effectiveness. Advanced users create custom color schemes that enhance readability, accessibility, and professional appearance.
Color Theory Fundamentals:
Sequential Colormaps (for ordered data):
Diverging Colormaps (for data with meaningful center):
Qualitative Colormaps (for categorical data):
Using Built-in Colormaps:
1import matplotlib.pyplot as plt
2import numpy as np
3
4# Create sample data
5data = np.random.rand(10, 10)
6
7fig, axes = plt.subplots(1, 3, figsize=(15, 4))
8
9# Sequential
10im1 = axes[0].imshow(data, cmap='viridis')
11axes[0].set_title('Sequential: Viridis')
12plt.colorbar(im1, ax=axes[0])
13
14# Diverging
15im2 = axes[1].imshow(data - 0.5, cmap='RdBu')
16axes[1].set_title('Diverging: RdBu')
17plt.colorbar(im2, ax=axes[1])
18
19# Qualitative (discrete)
20im3 = axes[2].imshow(np.random.randint(0, 5, (10, 10)), cmap='tab10')
21axes[2].set_title('Qualitative: tab10')
22plt.colorbar(im3, ax=axes[2])
23
24plt.tight_layout()
25plt.show()Creating Custom Colormaps:
1import matplotlib.pyplot as plt
2import matplotlib.colors as mcolors
3import numpy as np
4
5# Method 1: From list of colors
6colors = ['#2563eb', '#3b82f6', '#60a5fa', '#93c5fd', '#dbeafe']
7n_bins = 100
8cmap = mcolors.LinearSegmentedColormap.from_list('custom_blue', colors, N=n_bins)
9
10# Method 2: Define RGB transitions
11cdict = {
12 'red': [(0.0, 0.0, 0.0),
13 (0.5, 1.0, 1.0),
14 (1.0, 1.0, 1.0)],
15 'green': [(0.0, 0.0, 0.0),
16 (0.5, 0.0, 0.0),
17 (1.0, 1.0, 1.0)],
18 'blue': [(0.0, 1.0, 1.0),
19 (0.5, 0.0, 0.0),
20 (1.0, 0.0, 0.0)]
21}
22custom_cmap = mcolors.LinearSegmentedColormap('custom_diverging', cdict)
23
24# Use custom colormap
25data = np.random.randn(100, 100)
26plt.imshow(data, cmap=custom_cmap)
27plt.colorbar()
28plt.title('Custom Diverging Colormap')
29plt.show()Discrete Color Cycles:
1import matplotlib.pyplot as plt
2import numpy as np
3from cycler import cycler
4
5# Custom color cycle for line plots
6custom_colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd']
7plt.rcParams['axes.prop_cycle'] = cycler(color=custom_colors)
8
9fig, ax = plt.subplots(figsize=(10, 6))
10for i in range(5):
11 ax.plot(np.random.randn(100).cumsum(), label=f'Series {i+1}')
12ax.legend()
13plt.show()Accessibility Considerations:
1# Colorblind-friendly palettes
2import matplotlib.pyplot as plt
3
4# Use colorblind-safe colormaps
5safe_colormaps = ['viridis', 'plasma', 'cividis', 'twilight']
6
7# Or specific colorblind-friendly colors
8colorblind_colors = {
9 'blue': '#0173B2',
10 'orange': '#DE8F05',
11 'green': '#029E73',
12 'red': '#CC78BC',
13 'purple': '#CA9161',
14 'brown': '#949494'
15}Professional Tips:
Real-World Example:
Scientific journals often require colorblind-safe figures. Nature recommends viridis and similar perceptually uniform colormaps.
Text elements - titles, labels, annotations, and legends - transform data into understandable insights. Professional formatting makes visualizations publication-ready.
Advanced Text Formatting:
1import matplotlib.pyplot as plt
2import numpy as np
3
4fig, ax = plt.subplots(figsize=(10, 6))
5
6x = np.linspace(0, 10, 100)
7y = np.sin(x)
8ax.plot(x, y, linewidth=2, color='#2563eb')
9
10# Title with custom formatting
11ax.set_title('Mathematical Functions: $\sin(x)$',
12 fontsize=16, fontweight='bold',
13 fontfamily='serif',
14 pad=20, # Padding above plot
15 color='#1e293b')
16
17# Axis labels with LaTeX
18ax.set_xlabel('$x$ values (radians)',
19 fontsize=13, fontweight='bold',
20 labelpad=10) # Distance from axis
21
22ax.set_ylabel('$f(x) = \sin(x)$',
23 fontsize=13, fontweight='bold',
24 labelpad=10)
25
26# Tick label formatting
27ax.tick_params(axis='both', labelsize=11, length=6, width=1.5)
28
29plt.show()Annotations and Arrows:
1import matplotlib.pyplot as plt
2import numpy as np
3
4fig, ax = plt.subplots(figsize=(12, 7))
5
6x = np.linspace(0, 2*np.pi, 100)
7y = np.sin(x)
8ax.plot(x, y, linewidth=2.5, color='#2563eb', label='sin(x)')
9
10# Annotate maximum point
11max_idx = np.argmax(y)
12ax.annotate('Maximum',
13 xy=(x[max_idx], y[max_idx]), # Point to annotate
14 xytext=(x[max_idx] + 1, y[max_idx] + 0.3), # Text position
15 fontsize=12,
16 fontweight='bold',
17 color='#dc2626',
18 arrowprops=dict(
19 arrowstyle='->',
20 connectionstyle='arc3,rad=0.3',
21 color='#dc2626',
22 lw=2
23 ),
24 bbox=dict(boxstyle='round,pad=0.5',
25 facecolor='#fef2f2',
26 edgecolor='#dc2626',
27 linewidth=2))
28
29# Annotate zero crossings
30zero_crossings = [np.pi, 2*np.pi]
31for zc in zero_crossings:
32 ax.annotate('',
33 xy=(zc, 0),
34 xytext=(zc, -0.3),
35 arrowprops=dict(arrowstyle='->',
36 color='#16a34a',
37 lw=2))
38 ax.text(zc, -0.5, f'$x = {zc:.2f}$',
39 ha='center', fontsize=10,
40 color='#16a34a')
41
42# Add text box
43textstr = 'Key Properties:\n• Period: $2\pi$\n• Amplitude: 1'
44ax.text(0.02, 0.98, textstr,
45 transform=ax.transAxes, # Use axes coordinates (0-1)
46 fontsize=11,
47 verticalalignment='top',
48 bbox=dict(boxstyle='round',
49 facecolor='#f0f9ff',
50 edgecolor='#2563eb',
51 alpha=0.8,
52 pad=0.8))
53
54ax.grid(True, alpha=0.3, linestyle='--')
55ax.legend(loc='upper right', fontsize=12, frameon=True, shadow=True)
56plt.show()Professional Legends:
1import matplotlib.pyplot as plt
2import numpy as np
3
4fig, ax = plt.subplots(figsize=(10, 6))
5
6# Plot multiple series
7x = np.linspace(0, 10, 100)
8for i, (func, name, color) in enumerate([
9 (np.sin, 'Sine Wave', '#2563eb'),
10 (np.cos, 'Cosine Wave', '#dc2626'),
11 (lambda x: np.sin(2*x), 'Double Frequency', '#16a34a')
12]):
13 ax.plot(x, func(x), label=name, linewidth=2.5, color=color)
14
15# Customized legend
16legend = ax.legend(
17 loc='upper right', # Position
18 ncol=1, # Number of columns
19 fontsize=11, # Font size
20 frameon=True, # Show frame
21 fancybox=True, # Rounded corners
22 shadow=True, # Drop shadow
23 framealpha=0.95, # Frame transparency
24 edgecolor='#1e293b', # Frame color
25 title='Functions', # Legend title
26 title_fontsize=12 # Title font size
27)
28
29# Customize legend title
30legend.get_title().set_fontweight('bold')
31
32# Adjust line thickness in legend
33for line in legend.get_lines():
34 line.set_linewidth(3)
35
36ax.grid(True, alpha=0.3)
37ax.set_xlabel('X Values', fontsize=12, fontweight='bold')
38ax.set_ylabel('Y Values', fontsize=12, fontweight='bold')
39ax.set_title('Multiple Functions Comparison', fontsize=14, fontweight='bold')
40
41plt.show()Mathematical Expressions (LaTeX):
1import matplotlib.pyplot as plt
2
3fig, ax = plt.subplots(figsize=(10, 6))
4
5# LaTeX in title
6ax.set_title(r'$\int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}$',
7 fontsize=16)
8
9# Complex mathematical annotation
10ax.text(0.5, 0.5,
11 r'$\sum_{n=1}^{\infty} \frac{1}{n^2} = \frac{\pi^2}{6}$',
12 transform=ax.transAxes,
13 fontsize=20,
14 ha='center')
15
16# Greek letters and subscripts
17ax.set_xlabel(r'$\theta$ (radians)', fontsize=14)
18ax.set_ylabel(r'$f_{{\mathrm{{max}}}}(\theta)$', fontsize=14)
19
20plt.show()Best Practices:
Publication Standards:
Comprehensive demonstration of advanced Matplotlib techniques including figure architecture, custom styling, multi-plot layouts, color schemes, and professional annotations.
1import matplotlib.pyplot as plt
2import matplotlib.gridspec as gridspec
3import numpy as np
4from matplotlib.patches import Rectangle
5import matplotlib.colors as mcolors
6
7# Set publication-quality defaults
8plt.rcParams['figure.dpi'] = 100
9plt.rcParams['savefig.dpi'] = 300
10plt.rcParams['font.family'] = 'sans-serif'
11plt.rcParams['font.size'] = 10
12plt.rcParams['axes.labelsize'] = 11
13plt.rcParams['axes.titlesize'] = 12
14plt.rcParams['xtick.labelsize'] = 9
15plt.rcParams['ytick.labelsize'] = 9
16plt.rcParams['legend.fontsize'] = 10
17
18# Create figure with custom size
19fig = plt.figure(figsize=(14, 10))
20fig.suptitle('Advanced Matplotlib Techniques Showcase',
21 fontsize=16, fontweight='bold', y=0.98)
22
23# Create complex grid layout using GridSpec
24gs = gridspec.GridSpec(3, 3, figure=fig,
25 hspace=0.35, wspace=0.35,
26 top=0.95, bottom=0.05, left=0.08, right=0.95)
27
28# ==========================================
29# Panel 1: Main plot with advanced styling
30# ==========================================
31ax1 = fig.add_subplot(gs[0:2, 0:2])
32
33x = np.linspace(0, 10, 1000)
34y1 = np.sin(x) * np.exp(-x/10)
35y2 = np.cos(x) * np.exp(-x/10)
36
37# Plot with custom line styles
38line1 = ax1.plot(x, y1, linewidth=2.5, color='#2563eb',
39 label='Damped Sine', alpha=0.8)
40line2 = ax1.plot(x, y2, linewidth=2.5, color='#dc2626',
41 linestyle='--', label='Damped Cosine', alpha=0.8)
42
43# Fill between lines
44ax1.fill_between(x, y1, y2, alpha=0.2, color='#6366f1')
45
46# Annotate key features
47max_idx = np.argmax(y1)
48ax1.annotate(f'Peak: {y1[max_idx]:.3f}',
49 xy=(x[max_idx], y1[max_idx]),
50 xytext=(x[max_idx] + 1.5, y1[max_idx] + 0.2),
51 fontsize=10,
52 fontweight='bold',
53 color='#2563eb',
54 arrowprops=dict(arrowstyle='->',
55 connectionstyle='arc3,rad=0.3',
56 color='#2563eb', lw=2),
57 bbox=dict(boxstyle='round,pad=0.5',
58 facecolor='#eff6ff',
59 edgecolor='#2563eb'))
60
61# Styling
62ax1.set_xlabel('Time (seconds)', fontweight='bold')
63ax1.set_ylabel('Amplitude', fontweight='bold')
64ax1.set_title('A) Damped Oscillations - Main Analysis',
65 loc='left', fontweight='bold')
66ax1.grid(True, alpha=0.3, linestyle=':', linewidth=0.8)
67ax1.legend(loc='upper right', frameon=True, shadow=True, fancybox=True)
68ax1.set_xlim(0, 10)
69
70# ==========================================
71# Panel 2: Custom colormap demonstration
72# ==========================================
73ax2 = fig.add_subplot(gs[0, 2])
74
75# Create custom colormap
76colors_custom = ['#1e3a8a', '#3b82f6', '#60a5fa', '#93c5fd', '#dbeafe']
77n_bins = 100
78custom_cmap = mcolors.LinearSegmentedColormap.from_list(
79 'custom_blue', colors_custom, N=n_bins)
80
81# Generate 2D data
82data = np.random.randn(20, 20)
83im = ax2.imshow(data, cmap=custom_cmap, aspect='auto')
84
85ax2.set_title('B) Custom Colormap', loc='left', fontweight='bold')
86ax2.set_xlabel('X', fontsize=9)
87ax2.set_ylabel('Y', fontsize=9)
88
89# Add colorbar
90cbar = plt.colorbar(im, ax=ax2, fraction=0.046, pad=0.04)
91cbar.set_label('Intensity', rotation=270, labelpad=15, fontsize=9)
92
93# ==========================================
94# Panel 3: Polar plot
95# ==========================================
96ax3 = fig.add_subplot(gs[1, 2], projection='polar')
97
98theta = np.linspace(0, 2*np.pi, 100)
99r = 1 + 0.5 * np.sin(5*theta)
100
101ax3.plot(theta, r, linewidth=2.5, color='#16a34a')
102ax3.fill(theta, r, alpha=0.3, color='#86efac')
103
104ax3.set_title('C) Polar Coordinates', loc='left',
105 fontweight='bold', pad=20)
106ax3.grid(True, alpha=0.3)
107
108# ==========================================
109# Panel 4: Multiple subplots with shared axis
110# ==========================================
111ax4a = fig.add_subplot(gs[2, 0])
112ax4b = fig.add_subplot(gs[2, 1], sharey=ax4a)
113
114# Generate data
115x_data = np.linspace(0, 5, 100)
116y_data1 = np.exp(-x_data)
117y_data2 = np.exp(-2*x_data)
118
119ax4a.plot(x_data, y_data1, color='#9333ea', linewidth=2)
120ax4a.set_xlabel('Time', fontsize=9, fontweight='bold')
121ax4a.set_ylabel('Decay Rate', fontsize=9, fontweight='bold')
122ax4a.set_title('D) Exponential Decay 1', loc='left', fontweight='bold')
123ax4a.grid(True, alpha=0.3)
124
125ax4b.plot(x_data, y_data2, color='#ea580c', linewidth=2)
126ax4b.set_xlabel('Time', fontsize=9, fontweight='bold')
127ax4b.set_title('E) Exponential Decay 2', loc='left', fontweight='bold')
128ax4b.grid(True, alpha=0.3)
129plt.setp(ax4b.get_yticklabels(), visible=False) # Hide y-labels on shared axis
130
131# ==========================================
132# Panel 5: Statistical plot with annotations
133# ==========================================
134ax5 = fig.add_subplot(gs[2, 2])
135
136# Box plot data
137data_boxes = [np.random.normal(100, 10, 100),
138 np.random.normal(110, 15, 100),
139 np.random.normal(95, 8, 100)]
140
141bp = ax5.boxplot(data_boxes, patch_artist=True,
142 medianprops=dict(color='#dc2626', linewidth=2),
143 boxprops=dict(facecolor='#bfdbfe', edgecolor='#2563eb', linewidth=1.5),
144 whiskerprops=dict(color='#2563eb', linewidth=1.5),
145 capprops=dict(color='#2563eb', linewidth=1.5))
146
147ax5.set_xticklabels(['Group A', 'Group B', 'Group C'])
148ax5.set_ylabel('Values', fontsize=9, fontweight='bold')
149ax5.set_title('F) Statistical Distribution', loc='left', fontweight='bold')
150ax5.grid(True, alpha=0.3, axis='y')
151
152# Add mean markers
153means = [np.mean(d) for d in data_boxes]
154ax5.plot(range(1, 4), means, 'D', color='#16a34a',
155 markersize=8, label='Mean', zorder=3)
156ax5.legend(loc='upper right', fontsize=8)
157
158# ==========================================
159# Add figure-level text box with information
160# ==========================================
161textstr = '''Advanced Features Demonstrated:
162• Object-oriented figure/axes architecture
163• GridSpec for complex layouts
164• Custom colormaps and styling
165• Professional annotations and labels
166• Shared axes for comparison
167• Publication-quality formatting
168• Multiple plot types in single figure'''
169
170fig.text(0.02, 0.02, textstr, fontsize=8,
171 verticalalignment='bottom',
172 bbox=dict(boxstyle='round', facecolor='#f8fafc',
173 edgecolor='#64748b', alpha=0.8, pad=0.8))
174
175plt.show()This comprehensive demonstration showcases advanced Matplotlib techniques essential for professional data visualization:
🎯 Architecture and Layout:
fig.add_subplot() for precise control🎨 Advanced Styling:
📊 Multiple Plot Types:
✨ Professional Annotations:
🔧 Customization Techniques:
📈 Real-World Applications:
🎓 Best Practices Demonstrated:
💡 Key Takeaways:
This showcase serves as a template for creating sophisticated, publication-ready figures that effectively communicate complex data insights.
Excellent work mastering advanced Matplotlib techniques! You now understand figure architecture, advanced customization, multi-plot layouts, and professional styling. In our next lesson, "3D Visualizations and Advanced Plots," you'll learn to create 3D surface plots, heatmaps, contour plots, and animations using Matplotlib's 3D capabilities and advanced plotting functions. Get ready to add depth and motion to your visualizations!
Your progress is saved in this browser, on this device only.