Show code
Matplotlib version: 3.10.3
By: MIN Sothearith
Data Visualization in Python
Matplotlib is the foundational plotting library for Python.
Why Learn Matplotlib?
Matplotlib gives you complete control over every aspect of your visualizations - essential for publication-quality figures and custom visualizations.
Install Matplotlib via pip:
Standard import convention:
Matplotlib version: 3.10.3
Note
matplotlib.pyplot is imported as plt (standard convention)
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Circle
from matplotlib.patheffects import withStroke
from matplotlib.ticker import AutoMinorLocator, MultipleLocator
royal_blue = [0, 20/256, 82/256]
# make the figure
np.random.seed(19680801)
X = np.linspace(0.5, 3.5, 100)
Y1 = 3+np.cos(X)
Y2 = 1+np.cos(1+X/0.75)/2
Y3 = np.random.uniform(Y1, Y2, len(X))
fig = plt.figure(figsize=(7.5, 7.5))
ax = fig.add_axes([0.2, 0.17, 0.68, 0.7], aspect=1)
ax.xaxis.set_major_locator(MultipleLocator(1.000))
ax.xaxis.set_minor_locator(AutoMinorLocator(4))
ax.yaxis.set_major_locator(MultipleLocator(1.000))
ax.yaxis.set_minor_locator(AutoMinorLocator(4))
ax.xaxis.set_minor_formatter("{x:.2f}")
ax.set_xlim(0, 4)
ax.set_ylim(0, 4)
ax.tick_params(which='major', width=1.0, length=10, labelsize=14)
ax.tick_params(which='minor', width=1.0, length=5, labelsize=10,
labelcolor='0.25')
ax.grid(linestyle="--", linewidth=0.5, color='.25', zorder=-10)
ax.plot(X, Y1, c='C0', lw=2.5, label="Blue signal", zorder=10)
ax.plot(X, Y2, c='C1', lw=2.5, label="Orange signal")
ax.plot(X[::3], Y3[::3], linewidth=0, markersize=9,
marker='s', markerfacecolor='none', markeredgecolor='C4',
markeredgewidth=2.5)
ax.set_title("Anatomy of a figure", fontsize=20, verticalalignment='bottom')
ax.set_xlabel("x Axis label", fontsize=14)
ax.set_ylabel("y Axis label", fontsize=14)
ax.legend(loc="upper right", fontsize=14)
# Annotate the figure
def annotate(x, y, text, code):
# Circle marker
c = Circle((x, y), radius=0.15, clip_on=False, zorder=10, linewidth=2.5,
edgecolor=royal_blue + [0.6], facecolor='none',
path_effects=[withStroke(linewidth=7, foreground='white')])
ax.add_artist(c)
# use path_effects as a background for the texts
# draw the path_effects and the colored text separately so that the
# path_effects cannot clip other texts
for path_effects in [[withStroke(linewidth=7, foreground='white')], []]:
color = 'white' if path_effects else royal_blue
ax.text(x, y-0.2, text, zorder=100,
ha='center', va='top', weight='bold', color=color,
style='italic', fontfamily='monospace',
path_effects=path_effects)
color = 'white' if path_effects else 'black'
ax.text(x, y-0.33, code, zorder=100,
ha='center', va='top', weight='normal', color=color,
fontfamily='monospace', fontsize='medium',
path_effects=path_effects)
annotate(3.5, -0.13, "Minor tick label", "ax.xaxis.set_minor_formatter")
annotate(-0.03, 1.0, "Major tick", "ax.yaxis.set_major_locator")
annotate(0.00, 3.75, "Minor tick", "ax.yaxis.set_minor_locator")
annotate(-0.15, 3.00, "Major tick label", "ax.yaxis.set_major_formatter")
annotate(1.68, -0.39, "xlabel", "ax.set_xlabel")
annotate(-0.38, 1.67, "ylabel", "ax.set_ylabel")
annotate(1.52, 4.15, "Title", "ax.set_title")
annotate(1.75, 2.80, "Line", "ax.plot")
annotate(2.25, 1.54, "Markers", "ax.scatter")
annotate(3.00, 3.00, "Grid", "ax.grid")
annotate(3.60, 3.58, "Legend", "ax.legend")
annotate(2.5, 0.55, "Axes", "fig.subplots")
annotate(4, 4.5, "Figure", "plt.figure")
annotate(0.65, 0.01, "x Axis", "ax.xaxis")
annotate(0, 0.36, "y Axis", "ax.yaxis")
annotate(4.0, 0.7, "Spine", "ax.spines")
# frame around figure
fig.patch.set(linewidth=4, edgecolor='0.5')
plt.show()Understanding this hierarchy is crucial for effective plotting.
Object-Oriented (Recommended)
pyplot (MATLAB-style)
We’ll focus on Object-Oriented because it’s: - More explicit and clear - Better for complex figures - Industry standard
Creating a simple line plot:
The plt.subplots() function creates both a Figure and Axes object, giving you full control over your visualization.
Control canvas dimensions with figsize parameter:
Default size is (6.4, 4.8) inches. Use np.linspace() to create evenly spaced points.
Control every aspect of your lines:
fig, ax = plt.subplots(figsize=(14, 9))
x = np.linspace(0, 10, 100)
y = np.sin(x)
ax.plot(x, y,
color='red',
linestyle='--',
linewidth=3,
marker='o',
markersize=6,
markerfacecolor='blue',
markeredgecolor='black',
label='Sine Wave')
ax.set_xlabel('X', fontsize=16)
ax.set_ylabel('Y', fontsize=16)
ax.set_title('Customized Line Plot', fontsize=20)
ax.tick_params(labelsize=14)
ax.legend(fontsize=14)
ax.grid(True, alpha=0.3)
plt.show()Customize colors, line styles, markers, and more to create distinctive visualizations.
Use convenient shorthand notation:
fig, ax = plt.subplots(figsize=(14, 9))
x = np.linspace(0, 10, 100)
ax.plot(x, np.sin(x), 'r-', linewidth=3, label="'r-' = red solid")
ax.plot(x, np.sin(x) + 1, 'b--', linewidth=3, label="'b--' = blue dashed")
ax.plot(x, np.sin(x) + 2, 'go:', linewidth=3, markersize=8, label="'go:' = green dots with circles")
ax.plot(x, np.sin(x) + 3, 'k^-.', linewidth=3, markersize=8, label="'k^-.' = black dash-dot triangles")
ax.legend(fontsize=14)
ax.set_title('Line Style Shortcuts', fontsize=20)
ax.tick_params(labelsize=14)
ax.grid(True, alpha=0.3)
plt.show()Format: [color][marker][linestyle] - e.g., 'ro-' = red circles with solid line
Easily plot multiple datasets together:
fig, ax = plt.subplots(figsize=(14, 9))
x = np.linspace(0, 2*np.pi, 100)
ax.plot(x, np.sin(x), label='sin(x)', linewidth=3)
ax.plot(x, np.cos(x), label='cos(x)', linewidth=3)
ax.plot(x, np.sin(x) * np.cos(x), label='sin(x) × cos(x)', linewidth=3)
ax.set_xlabel('X (radians)', fontsize=16)
ax.set_ylabel('Y', fontsize=16)
ax.set_title('Multiple Functions', fontsize=20)
ax.tick_params(labelsize=14)
ax.legend(fontsize=14)
ax.grid(True, alpha=0.3)
plt.show()Each ax.plot() call adds another line to the same axes, perfect for comparisons.
Create complex layouts with multiple plots:
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(14, 12))
x = np.linspace(0, 2*np.pi, 100)
axes[0, 0].plot(x, np.sin(x), linewidth=3)
axes[0, 0].set_title('sin(x)', fontsize=16)
axes[0, 0].tick_params(labelsize=12)
axes[0, 0].grid(True, alpha=0.3)
axes[0, 1].plot(x, np.cos(x), 'r', linewidth=3)
axes[0, 1].set_title('cos(x)', fontsize=16)
axes[0, 1].tick_params(labelsize=12)
axes[0, 1].grid(True, alpha=0.3)
axes[1, 0].plot(x, np.tan(x), linewidth=3)
axes[1, 0].set_title('tan(x)', fontsize=16)
axes[1, 0].set_ylim(-5, 5)
axes[1, 0].tick_params(labelsize=12)
axes[1, 0].grid(True, alpha=0.3)
axes[1, 1].plot(x, np.sin(x)**2, 'g', linewidth=3)
axes[1, 1].set_title('sin²(x)', fontsize=16)
axes[1, 1].tick_params(labelsize=12)
axes[1, 1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()Access individual plots with axes[row, col]. Always use plt.tight_layout() to prevent overlapping.
Single row or column arrangements:
fig, axes = plt.subplots(1, 3, figsize=(16, 6))
x = np.linspace(0, 10, 100)
axes[0].plot(x, x**2, linewidth=3)
axes[0].set_title('Quadratic', fontsize=16)
axes[0].tick_params(labelsize=12)
axes[0].grid(True, alpha=0.3)
axes[1].plot(x, x**3, 'r', linewidth=3)
axes[1].set_title('Cubic', fontsize=16)
axes[1].tick_params(labelsize=12)
axes[1].grid(True, alpha=0.3)
axes[2].plot(x, 2**x, 'g', linewidth=3)
axes[2].set_title('Exponential', fontsize=16)
axes[2].tick_params(labelsize=12)
axes[2].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()With single row/column, axes is 1D array - use axes[i] instead of axes[i, j].
Share x or y axes for easier comparison:
fig, axes = plt.subplots(2, 2, figsize=(14, 12),
sharex=True, sharey=True)
x = np.linspace(0, 10, 100)
axes[0, 0].plot(x, x, linewidth=3)
axes[0, 0].set_title('Linear', fontsize=16)
axes[0, 0].tick_params(labelsize=12)
axes[0, 0].grid(True, alpha=0.3)
axes[0, 1].plot(x, x**2, linewidth=3)
axes[0, 1].set_title('Quadratic', fontsize=16)
axes[0, 1].tick_params(labelsize=12)
axes[0, 1].grid(True, alpha=0.3)
axes[1, 0].plot(x, x**3, linewidth=3)
axes[1, 0].set_title('Cubic', fontsize=16)
axes[1, 0].tick_params(labelsize=12)
axes[1, 0].grid(True, alpha=0.3)
axes[1, 1].plot(x, np.sqrt(x), linewidth=3)
axes[1, 1].set_title('Square Root', fontsize=16)
axes[1, 1].tick_params(labelsize=12)
axes[1, 1].grid(True, alpha=0.3)
axes[1, 0].set_xlabel('X', fontsize=14)
axes[1, 1].set_xlabel('X', fontsize=14)
axes[0, 0].set_ylabel('Y', fontsize=14)
axes[1, 0].set_ylabel('Y', fontsize=14)
plt.tight_layout()
plt.show()Shared axes maintain consistent scales across subplots, reducing visual clutter.
Create custom subplot arrangements:
import matplotlib.gridspec as gridspec
fig = plt.figure(figsize=(14, 12))
gs = gridspec.GridSpec(3, 3, figure=fig)
ax1 = fig.add_subplot(gs[0, :])
ax2 = fig.add_subplot(gs[1, :-1])
ax3 = fig.add_subplot(gs[1:, -1])
ax4 = fig.add_subplot(gs[-1, 0])
ax5 = fig.add_subplot(gs[-1, 1])
x = np.linspace(0, 10, 100)
ax1.plot(x, np.sin(x), linewidth=3)
ax1.set_title('Wide plot spanning all columns', fontsize=16)
ax1.tick_params(labelsize=12)
ax1.grid(True, alpha=0.3)
ax2.plot(x, np.cos(x), 'r', linewidth=3)
ax2.set_title('Medium plot', fontsize=16)
ax2.tick_params(labelsize=12)
ax2.grid(True, alpha=0.3)
ax3.plot(x, np.tan(x), 'g', linewidth=3)
ax3.set_title('Tall plot', fontsize=16)
ax3.set_ylim(-5, 5)
ax3.tick_params(labelsize=12)
ax3.grid(True, alpha=0.3)
ax4.plot(x, x**2, 'm', linewidth=3)
ax4.set_title('Small 1', fontsize=14)
ax4.tick_params(labelsize=10)
ax4.grid(True, alpha=0.3)
ax5.plot(x, x**3, 'c', linewidth=3)
ax5.set_title('Small 2', fontsize=14)
ax5.tick_params(labelsize=10)
ax5.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()GridSpec allows plots to span multiple cells - perfect for dashboards and complex layouts.
Visualize relationships between variables:
fig, ax = plt.subplots(figsize=(14, 9))
np.random.seed(42)
x = np.random.randn(100)
y = 2 * x + np.random.randn(100) * 0.5
ax.scatter(x, y,
color='blue',
alpha=0.6,
s=100,
edgecolors='black',
linewidth=1)
ax.set_xlabel('X Values', fontsize=16)
ax.set_ylabel('Y Values', fontsize=16)
ax.set_title('Scatter Plot Showing Correlation', fontsize=20)
ax.tick_params(labelsize=14)
ax.grid(True, alpha=0.3)
plt.show()Scatter plots reveal correlations and distributions. Use alpha for overlapping points.
Add a third dimension using colors:
fig, ax = plt.subplots(figsize=(14, 10))
np.random.seed(42)
x = np.random.randn(100)
y = np.random.randn(100)
colors = x + y
sizes = 1000 * np.abs(np.random.randn(100))
scatter = ax.scatter(x, y,
c=colors,
s=sizes,
alpha=0.6,
cmap='viridis',
edgecolors='black',
linewidth=1)
cbar = plt.colorbar(scatter, ax=ax)
cbar.set_label('X + Y Values', fontsize=14)
cbar.ax.tick_params(labelsize=12)
ax.set_xlabel('X', fontsize=16)
ax.set_ylabel('Y', fontsize=16)
ax.set_title('Scatter with Color and Size Mapping', fontsize=20)
ax.tick_params(labelsize=14)
ax.grid(True, alpha=0.3)
plt.show()Color mapping reveals patterns that aren’t visible in 2D alone. The colorbar provides a scale reference.
Compare categorical data:
fig, ax = plt.subplots(figsize=(14, 9))
categories = ['Product A', 'Product B', 'Product C', 'Product D', 'Product E']
values = [23, 45, 56, 78, 32]
bars = ax.bar(categories, values,
color='skyblue',
edgecolor='navy',
linewidth=2)
max_idx = values.index(max(values))
bars[max_idx].set_color('orange')
ax.set_ylabel('Sales (thousands)', fontsize=16)
ax.set_title('Product Sales Comparison', fontsize=20)
ax.tick_params(labelsize=14)
ax.grid(axis='y', alpha=0.3)
for bar in bars:
height = bar.get_height()
ax.text(bar.get_x() + bar.get_width()/2., height,
f'{height}',
ha='center', va='bottom', fontsize=14)
plt.show()Bar plots excel at categorical comparisons. Add value labels for clarity, highlight significant bars.
Compare multiple groups side by side:
fig, ax = plt.subplots(figsize=(14, 9))
categories = ['Q1', 'Q2', 'Q3', 'Q4']
sales_2023 = [45, 52, 48, 60]
sales_2024 = [50, 58, 55, 65]
x = np.arange(len(categories))
width = 0.35
bars1 = ax.bar(x - width/2, sales_2023, width,
label='2023', color='skyblue', edgecolor='black', linewidth=1.5)
bars2 = ax.bar(x + width/2, sales_2024, width,
label='2024', color='lightcoral', edgecolor='black', linewidth=1.5)
ax.set_xlabel('Quarter', fontsize=16)
ax.set_ylabel('Sales (thousands)', fontsize=16)
ax.set_title('Quarterly Sales: 2023 vs 2024', fontsize=20)
ax.set_xticks(x)
ax.set_xticklabels(categories)
ax.tick_params(labelsize=14)
ax.legend(fontsize=14)
ax.grid(axis='y', alpha=0.3)
plt.show()Grouped bars show trends over time or across categories. Offset bars using width/2.
Show composition and totals:
fig, ax = plt.subplots(figsize=(14, 9))
categories = ['Q1', 'Q2', 'Q3', 'Q4']
online_sales = [30, 35, 32, 40]
store_sales = [15, 17, 16, 20]
ax.bar(categories, online_sales, label='Online', color='steelblue', edgecolor='black')
ax.bar(categories, store_sales, bottom=online_sales,
label='In-Store', color='coral', edgecolor='black')
ax.set_ylabel('Sales (thousands)', fontsize=16)
ax.set_title('Sales by Channel (Stacked)', fontsize=20)
ax.tick_params(labelsize=14)
ax.legend(fontsize=14)
ax.grid(axis='y', alpha=0.3)
plt.show()Stacked bars show both individual components and total values simultaneously.
Visualize data distributions:
fig, ax = plt.subplots(figsize=(14, 9))
np.random.seed(42)
data = np.random.randn(1000)
n, bins, patches = ax.hist(data,
bins=30,
color='steelblue',
edgecolor='black',
alpha=0.7,
linewidth=1.5)
ax.set_xlabel('Value', fontsize=16)
ax.set_ylabel('Frequency', fontsize=16)
ax.set_title('Distribution of Random Data', fontsize=20)
ax.tick_params(labelsize=14)
ax.axvline(data.mean(), color='red', linestyle='--',
linewidth=3, label=f'Mean: {data.mean():.2f}')
ax.legend(fontsize=14)
ax.grid(axis='y', alpha=0.3)
plt.show()Histograms reveal distribution shapes, central tendency, and spread. Adjust bin count for clarity.
Display proportions and percentages:
fig, ax = plt.subplots(figsize=(12, 10))
sizes = [35, 25, 20, 15, 5]
labels = ['Product A', 'Product B', 'Product C', 'Product D', 'Others']
colors = ['#ff9999', '#66b3ff', '#99ff99', '#ffcc99', '#ff99cc']
explode = (0.1, 0, 0, 0, 0)
wedges, texts, autotexts = ax.pie(sizes,
explode=explode,
labels=labels,
colors=colors,
autopct='%1.1f%%',
shadow=True,
startangle=90,
textprops={'fontsize': 14})
for autotext in autotexts:
autotext.set_color('white')
autotext.set_fontsize(14)
autotext.set_weight('bold')
ax.set_title('Market Share by Product', fontsize=20)
plt.show()Pie charts work best with few categories (5-7 max). Explode slices to emphasize important segments.
Visualize 2D data with color intensity:
fig, ax = plt.subplots(figsize=(14, 10))
np.random.seed(42)
data = np.random.rand(10, 12)
im = ax.imshow(data, cmap='YlOrRd', aspect='auto')
cbar = plt.colorbar(im, ax=ax)
cbar.set_label('Values', fontsize=14)
cbar.ax.tick_params(labelsize=12)
ax.set_xticks(np.arange(12))
ax.set_yticks(np.arange(10))
ax.set_xticklabels(['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], fontsize=12)
ax.set_yticklabels([f'Product {i+1}' for i in range(10)], fontsize=12)
plt.setp(ax.get_xticklabels(), rotation=45, ha="right")
ax.set_title('Monthly Sales Heatmap', fontsize=20)
plt.tight_layout()
plt.show()Heatmaps excel at showing patterns in matrix data. Color intensity represents magnitude.
Precise control over axis ticks:
fig, ax = plt.subplots(figsize=(14, 9))
x = np.linspace(0, 4*np.pi, 100)
y = np.sin(x)
ax.plot(x, y, linewidth=3)
ax.set_xticks([0, np.pi, 2*np.pi, 3*np.pi, 4*np.pi])
ax.set_xticklabels(['0', 'π', '2π', '3π', '4π'])
ax.tick_params(axis='both',
labelsize=16,
length=8,
width=2,
colors='darkblue')
ax.set_xlabel('X (radians)', fontsize=16)
ax.set_ylabel('Y', fontsize=16)
ax.set_title('Custom Tick Labels', fontsize=20)
ax.grid(True, alpha=0.3)
plt.show()Custom ticks improve readability for special scales (angles, logarithms, dates).
Handle wide value ranges:
fig, axes = plt.subplots(1, 2, figsize=(16, 8))
x = np.linspace(0.1, 10, 100)
y = np.exp(x)
axes[0].plot(x, y, linewidth=3)
axes[0].set_title('Linear Scale', fontsize=18)
axes[0].set_xlabel('X', fontsize=14)
axes[0].set_ylabel('Y = exp(x)', fontsize=14)
axes[0].tick_params(labelsize=12)
axes[0].grid(True, alpha=0.3)
axes[1].plot(x, y, linewidth=3)
axes[1].set_yscale('log')
axes[1].set_title('Logarithmic Y-Scale', fontsize=18)
axes[1].set_xlabel('X', fontsize=14)
axes[1].set_ylabel('Y = exp(x) [log scale]', fontsize=14)
axes[1].tick_params(labelsize=12)
axes[1].grid(True, alpha=0.3, which='both')
plt.tight_layout()
plt.show()Log scales reveal patterns in exponential data and compress large ranges.
Plot two different scales:
fig, ax1 = plt.subplots(figsize=(14, 9))
x = np.arange(0, 10, 0.1)
y1 = np.sin(x)
y2 = np.exp(x / 5)
color = 'tab:blue'
ax1.set_xlabel('X', fontsize=16)
ax1.set_ylabel('sin(x)', color=color, fontsize=16)
ax1.plot(x, y1, color=color, linewidth=3)
ax1.tick_params(axis='y', labelcolor=color, labelsize=14)
ax1.tick_params(axis='x', labelsize=14)
ax2 = ax1.twinx()
color = 'tab:red'
ax2.set_ylabel('exp(x/5)', color=color, fontsize=16)
ax2.plot(x, y2, color=color, linewidth=3)
ax2.tick_params(axis='y', labelcolor=color, labelsize=14)
ax1.set_title('Two Different Y-Axes', fontsize=20)
ax1.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()Twin axes enable comparing metrics with vastly different scales on the same plot.
Highlight important features:
fig, ax = plt.subplots(figsize=(14, 9))
x = np.linspace(0, 10, 100)
y = np.sin(x)
ax.plot(x, y, 'b-', linewidth=3)
max_idx = np.argmax(y)
ax.annotate('Maximum',
xy=(x[max_idx], y[max_idx]),
xytext=(x[max_idx] + 1, y[max_idx] + 0.3),
arrowprops=dict(facecolor='red', shrink=0.05, width=3),
fontsize=16,
bbox=dict(boxstyle='round', facecolor='wheat'))
ax.text(5, -0.8, 'This is a sine wave',
fontsize=16, ha='center',
bbox=dict(boxstyle='round', facecolor='lightblue'))
ax.set_xlabel('X', fontsize=16)
ax.set_ylabel('Y', fontsize=16)
ax.set_title('Plot with Annotations', fontsize=20)
ax.tick_params(labelsize=14)
ax.grid(True, alpha=0.3)
plt.show()Annotations guide reader attention to critical points and patterns in your data.
Show areas between curves:
fig, ax = plt.subplots(figsize=(14, 9))
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
ax.plot(x, y1, 'r-', linewidth=3, label='sin(x)')
ax.plot(x, y2, 'b-', linewidth=3, label='cos(x)')
ax.fill_between(x, y1, y2,
where=(y1 >= y2),
alpha=0.3,
color='green',
label='sin(x) > cos(x)')
ax.fill_between(x, y1, y2,
where=(y1 < y2),
alpha=0.3,
color='orange',
label='sin(x) < cos(x)')
ax.set_xlabel('X', fontsize=16)
ax.set_ylabel('Y', fontsize=16)
ax.set_title('Filled Area Between Curves', fontsize=20)
ax.tick_params(labelsize=14)
ax.legend(fontsize=14)
ax.grid(True, alpha=0.3)
plt.show()Fill regions highlight differences and relationships between multiple datasets.
Display measurement uncertainty:
fig, ax = plt.subplots(figsize=(14, 9))
x = np.arange(0, 10, 1)
y = np.exp(x / 5)
y_err = 0.1 * y
ax.errorbar(x, y,
yerr=y_err,
fmt='o-',
linewidth=3,
markersize=10,
capsize=7,
capthick=2,
ecolor='red',
label='Data with error')
ax.set_xlabel('X', fontsize=16)
ax.set_ylabel('Y', fontsize=16)
ax.set_title('Plot with Error Bars', fontsize=20)
ax.tick_params(labelsize=14)
ax.legend(fontsize=14)
ax.grid(True, alpha=0.3)
plt.show()Error bars communicate data quality and measurement precision - essential for scientific plots.
Represent 3D data in 2D:
fig, ax = plt.subplots(figsize=(14, 10))
x = np.linspace(-3, 3, 100)
y = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))
contour = ax.contour(X, Y, Z, levels=15, cmap='RdYlBu', linewidths=2)
ax.clabel(contour, inline=True, fontsize=10)
contourf = ax.contourf(X, Y, Z, levels=15, cmap='RdYlBu', alpha=0.6)
cbar = plt.colorbar(contourf, ax=ax)
cbar.set_label('Z values', fontsize=14)
cbar.ax.tick_params(labelsize=12)
ax.set_xlabel('X', fontsize=16)
ax.set_ylabel('Y', fontsize=16)
ax.set_title('Contour Plot', fontsize=20)
ax.tick_params(labelsize=14)
plt.show()Contour plots show elevation and gradients - widely used in geography, physics, and optimization.
Visualize functions of two variables:
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(14, 11))
ax = fig.add_subplot(111, projection='3d')
x = np.linspace(-5, 5, 50)
y = np.linspace(-5, 5, 50)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))
surf = ax.plot_surface(X, Y, Z,
cmap='viridis',
edgecolor='none',
alpha=0.8)
fig.colorbar(surf, ax=ax, shrink=0.5)
ax.set_xlabel('X', fontsize=14)
ax.set_ylabel('Y', fontsize=14)
ax.set_zlabel('Z', fontsize=14)
ax.set_title('3D Surface Plot', fontsize=18)
ax.tick_params(labelsize=11)
plt.show()3D plots provide intuitive understanding of multivariate functions and surfaces.
Apply professional themes instantly:
plt.style.use('seaborn-v0_8-darkgrid')
fig, ax = plt.subplots(figsize=(14, 9))
x = np.linspace(0, 10, 100)
ax.plot(x, np.sin(x), linewidth=3, label='sin(x)')
ax.plot(x, np.cos(x), linewidth=3, label='cos(x)')
ax.set_xlabel('X', fontsize=16)
ax.set_ylabel('Y', fontsize=16)
ax.set_title('Using Seaborn Style', fontsize=20)
ax.tick_params(labelsize=14)
ax.legend(fontsize=14)
plt.show()
plt.style.use('default')Style sheets ensure consistent, professional appearance across all plots.
Export in multiple formats:
fig, ax = plt.subplots(figsize=(14, 9))
x = np.linspace(0, 10, 100)
ax.plot(x, np.sin(x), linewidth=3)
ax.set_xlabel('X', fontsize=16)
ax.set_ylabel('Y', fontsize=16)
ax.set_title('Figure to Save', fontsize=20)
ax.tick_params(labelsize=14)
ax.grid(True, alpha=0.3)
fig.savefig('my_plot.png', dpi=300, bbox_inches='tight')
fig.savefig('my_plot.pdf', bbox_inches='tight')
fig.savefig('my_plot.svg', bbox_inches='tight')
print("✓ Figures saved successfully!")
plt.show()✓ Figures saved successfully!
Always use dpi=300 for publication quality, bbox_inches='tight' removes whitespace.
Putting it all together:
np.random.seed(42)
dates = pd.date_range('2023-01-01', periods=365, freq='D')
sales = 1000 + np.cumsum(np.random.randn(365) * 10)
profit = sales * 0.2 + np.random.randn(365) * 20
customers = 50 + np.cumsum(np.random.randn(365) * 2)
fig = plt.figure(figsize=(18, 14))
gs = gridspec.GridSpec(3, 2, height_ratios=[2, 1, 1], hspace=0.3, wspace=0.3)
# Main sales plot
ax1 = fig.add_subplot(gs[0, :])
ax1.plot(dates, sales, linewidth=3, color='steelblue', label='Daily Sales')
ax1.fill_between(dates, sales, alpha=0.3, color='steelblue')
ax1.set_title('Sales Dashboard 2023', fontsize=20, fontweight='bold')
ax1.set_ylabel('Sales ($)', fontsize=14)
ax1.tick_params(labelsize=12)
ax1.legend(loc='upper left', fontsize=12)
ax1.grid(True, alpha=0.3)
# Profit trend
ax2 = fig.add_subplot(gs[1, 0])
ax2.plot(dates, profit, linewidth=2, color='green', label='Daily Profit')
ax2.axhline(y=profit.mean(), color='red', linestyle='--', linewidth=2, label='Average')
ax2.set_ylabel('Profit ($)', fontsize=12)
ax2.set_title('Profit Trend', fontsize=14)
ax2.tick_params(labelsize=10)
ax2.legend(fontsize=10)
ax2.grid(True, alpha=0.3)
# Customer count
ax3 = fig.add_subplot(gs[1, 1])
ax3.plot(dates, customers, linewidth=2, color='coral')
ax3.set_ylabel('Customers', fontsize=12)
ax3.set_title('Customer Count', fontsize=14)
ax3.tick_params(labelsize=10)
ax3.grid(True, alpha=0.3)
# Monthly summary
ax4 = fig.add_subplot(gs[2, 0])
monthly_sales = [sales[i*30:(i+1)*30].sum() for i in range(12)]
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
ax4.bar(months, monthly_sales, color='skyblue', edgecolor='navy')
ax4.set_ylabel('Monthly Sales ($)', fontsize=12)
ax4.set_title('Monthly Sales Summary', fontsize=14)
ax4.tick_params(axis='x', rotation=45, labelsize=10)
ax4.tick_params(axis='y', labelsize=10)
ax4.grid(axis='y', alpha=0.3)
# Profit distribution
ax5 = fig.add_subplot(gs[2, 1])
ax5.hist(profit, bins=30, color='lightgreen', edgecolor='darkgreen', alpha=0.7)
ax5.set_xlabel('Profit ($)', fontsize=12)
ax5.set_ylabel('Frequency', fontsize=12)
ax5.set_title('Profit Distribution', fontsize=14)
ax5.tick_params(labelsize=10)
ax5.grid(axis='y', alpha=0.3)
plt.show()This dashboard combines multiple plot types to tell a complete data story.
Essential Guidelines
dpi=300 minimum| Plot Type | Method | Best For |
|---|---|---|
| Line | ax.plot() |
Trends, time series |
| Scatter | ax.scatter() |
Relationships, correlations |
| Bar | ax.bar() |
Categorical comparisons |
| Histogram | ax.hist() |
Distributions |
| Pie | ax.pie() |
Proportions (few categories) |
| Heatmap | ax.imshow() |
2D data, matrices |
| Contour | ax.contour() |
3D data in 2D |
| Error bars | ax.errorbar() |
Uncertainty |
Introduction to Matplotlib