Styling, Annotations And Layout in Matplotlib
Styling, Annotations & Layout in Matplotlib: Complete Guide
Chart banana easy hai — lekin professional dikhana art hai. Titles, colors, themes, data labels, annotations, subplots aur spacing — sab ek guide mein jo aapke charts ko presentation-ready banaye.
📑 Is Guide Mein 3 Parts Cover Honge:
- Part A — Plot Styling: Titles, Labels, Colors, Markers, Grid, Themes, Figure Size
- Part B — Annotations & Data Labels: Bar Labels, Pie Labels, annotate(), text(), Reference Lines, fill_between()
- Part C — Layout & Spacing: subplot(), subplots(), tight_layout(), subplots_adjust(), GridSpec
Part A — Plot Styling
A1. Title, Labels & Legend
🔍 Kya Hai: Title chart ka heading hai, Labels axes ko describe karte hain, aur Legend multiple lines/bars ko identify karta hai.
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot([1,2,3,4], [10,20,25,30], label="Sales")
# ===== TITLE =====
ax.set_title("Monthly Sales Report",
fontsize=18, # text size
fontweight="bold", # "normal","bold","light","heavy"
fontfamily="Arial", # font name
color="#2c3e50", # text color
pad=20, # space between title and chart
loc="center", # "center","left","right"
style="italic" # "normal","italic","oblique"
)
# ===== SUBTITLE (using suptitle) =====
fig.suptitle("Company Dashboard",
fontsize=22, fontweight="bold",
y=1.02 # position above figure (>1 = above)
)
# ===== AXIS LABELS =====
ax.set_xlabel("Month",
fontsize=14,
fontweight="bold",
labelpad=12, # space between label and axis
color="#34495e"
)
ax.set_ylabel("Revenue (₹)",
fontsize=14,
fontweight="bold",
labelpad=12,
color="#34495e",
rotation=90 # label rotation angle
)
# ===== LEGEND =====
ax.legend(
fontsize=12,
loc="upper left", # "upper right","lower left","center","best"
frameon=True, # border frame show/hide
framealpha=0.9, # frame transparency
facecolor="white", # background color
edgecolor="gray", # border color
shadow=True, # shadow effect
ncol=2, # columns mein legends arrange karna
title="Year", # legend ka title
title_fontsize=11, # title font size
bbox_to_anchor=(1.0, 1.0) # legend position fine-tune (x, y)
)
plt.savefig("title_labels_legend.png", dpi=150, bbox_inches="tight")
plt.show()
A2. Colors & Colormaps
🔍 Kya Hai: Matplotlib mein colors 6 tarike se define hote hain — named colors, hex codes, RGB tuples, aur colormaps jo gradients provide karte hain.
# ===== 6 WAYS TO DEFINE COLORS =====
# 1. Named Colors
color = "red" # "blue","green","orange","purple","cyan"
# 2. Hex Codes (Most Professional)
color = "#3498db" # 6-digit hex
color = "#e74c3c80" # 8-digit hex (last 2 = transparency)
# 3. RGB Tuple (0-1 range)
color = (0.2, 0.6, 0.9) # (R, G, B)
color = (0.2, 0.6, 0.9, 0.5) # (R, G, B, Alpha)
# 4. Single Letter Shortcuts
color = "r" # r=red, b=blue, g=green, k=black, w=white
# 5. CSS4 Named Colors
color = "dodgerblue" # "coral","salmon","teal","gold","navy"
# 6. Grayscale String
color = "0.7" # 0=black, 1=white, 0.5=medium gray
# ===== POPULAR COLOR PALETTES =====
# Professional Blue Theme
blues = ["#1a5276", "#2980b9", "#3498db", "#5dade2", "#85c1e9", "#aed6f1"]
# Dashboard Colors
dashboard = ["#3498db", "#2ecc71", "#e74c3c", "#f39c12", "#9b59b6", "#1abc9c"]
# Pastel Colors
pastels = ["#a8d8ea", "#aa96da", "#fcbad3", "#ffffd2", "#a8e6cf"]
# ===== COLORMAPS (Gradients) =====
# Sequential: "Blues","Greens","Reds","Oranges","Purples","YlOrRd"
# Diverging: "RdBu","coolwarm","RdYlGn","seismic"
# Qualitative: "Set1","Set2","Set3","Pastel1","tab10","tab20"
# Perceptual: "viridis","plasma","inferno","magma","cividis"
# Colormap use example
import matplotlib.cm as cm
colors_from_cmap = cm.viridis([0.0, 0.25, 0.5, 0.75, 1.0]) # 5 colors from viridis
A3. Line Styles & Markers — Complete Reference
🔍 Kya Hai: Line styles aur markers charts ko differentiate karte hain — especially multiple lines ek chart mein hon tab.
# ===== ALL LINE STYLES =====
# "-" → Solid line (default)
# "--" → Dashed line
# "-." → Dash-dot line
# ":" → Dotted line
# "" → No line (markers only)
# ===== ALL MARKERS =====
# "o" → Circle "s" → Square
# "^" → Triangle Up "v" → Triangle Down
# "D" → Diamond "d" → Thin Diamond
# "*" → Star "+" → Plus
# "x" → Cross "P" → Plus Filled
# "h" → Hexagon "p" → Pentagon
# "|" → Vertical Line "_" → Horizontal Line
# "." → Point (small) "," → Pixel
fig, ax = plt.subplots(figsize=(10, 6))
x = [1, 2, 3, 4, 5]
# Different combinations
ax.plot(x, [2,4,6,8,10], linestyle="-", marker="o", linewidth=2, markersize=8, label="Solid + Circle")
ax.plot(x, [3,5,7,9,11], linestyle="--", marker="s", linewidth=2, markersize=8, label="Dashed + Square")
ax.plot(x, [1,3,5,7,9], linestyle="-.", marker="^", linewidth=2, markersize=8, label="Dash-dot + Triangle")
ax.plot(x, [4,6,8,10,12], linestyle=":", marker="D", linewidth=2, markersize=8, label="Dotted + Diamond")
ax.legend(fontsize=10)
ax.set_title("Line Styles & Markers Reference", fontsize=14, fontweight="bold")
plt.savefig("line_markers_ref.png", dpi=150, bbox_inches="tight")
plt.show()
A4. Grid & Axis Control
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot([1,2,3,4,5], [10,25,20,35,30])
# ===== GRID =====
ax.grid(
visible=True, # grid on/off
which="major", # "major","minor","both"
axis="both", # "x","y","both"
linestyle="--", # grid line style
linewidth=0.5, # grid line thickness
alpha=0.3, # grid transparency
color="gray", # grid color
zorder=0 # grid behind data (IMPORTANT!)
)
# ===== AXIS LIMITS =====
ax.set_xlim(0, 6) # x-axis range
ax.set_ylim(0, 40) # y-axis range
# ===== CUSTOM TICKS =====
ax.set_xticks([1, 2, 3, 4, 5]) # tick positions
ax.set_xticklabels(["Mon", "Tue", "Wed", "Thu", "Fri"], # custom labels
fontsize=11, rotation=0)
ax.set_yticks([0, 10, 20, 30, 40])
ax.tick_params(
axis="both", # apply to both axes
direction="out", # "in","out","inout"
length=5, # tick mark length
width=1, # tick mark width
labelsize=11, # tick label font size
colors="#2c3e50" # tick color
)
# ===== SPINES (Borders) =====
ax.spines["top"].set_visible(False) # hide top border
ax.spines["right"].set_visible(False) # hide right border
ax.spines["bottom"].set_color("#7f8c8d") # bottom border color
ax.spines["left"].set_linewidth(1.5) # left border thickness
plt.savefig("grid_axis.png", dpi=150, bbox_inches="tight")
plt.show()
A5. Themes — Pre-Built Chart Styles
# ===== ALL AVAILABLE THEMES =====
print(plt.style.available)
# Popular ones:
# "seaborn-v0_8" → Clean, modern (MOST POPULAR)
# "ggplot" → R-style (gray background)
# "dark_background" → Dark mode charts
# "fivethirtyeight" → FiveThirtyEight blog style
# "bmh" → Bayesian Methods style
# "tableau-colorblind10" → Colorblind friendly
# "classic" → Matplotlib default (old school)
# ===== APPLY THEME =====
plt.style.use("seaborn-v0_8") # global apply
# OR temporary theme (recommended)
with plt.style.context("dark_background"):
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot([1,2,3], [10,20,30], color="cyan", linewidth=2)
ax.set_title("Dark Mode Chart", fontsize=16)
plt.savefig("dark_theme.png", dpi=150, bbox_inches="tight")
plt.show()
# After 'with' block, original theme restores automatically!
# Reset to default
plt.style.use("default")
A6. Figure Size & DPI
# ===== FIGURE SIZE =====
# figsize=(width, height) in INCHES
fig, ax = plt.subplots(figsize=(10, 6)) # Standard (landscape)
fig, ax = plt.subplots(figsize=(8, 8)) # Square (pie charts)
fig, ax = plt.subplots(figsize=(12, 4)) # Wide (time series)
fig, ax = plt.subplots(figsize=(6, 10)) # Tall (horizontal bars)
fig, ax = plt.subplots(figsize=(16, 9)) # 16:9 (presentations)
# ===== DPI (Dots Per Inch = Resolution) =====
fig, ax = plt.subplots(figsize=(10, 6), dpi=100) # Screen display (default)
# Save with different DPI
plt.savefig("chart_web.png", dpi=72) # Web/blog (small file)
plt.savefig("chart_screen.png", dpi=150) # Presentations (balanced)
plt.savefig("chart_print.png", dpi=300) # Print quality (high res)
# ===== RECOMMENDED SIZES =====
# Blog post: figsize=(10,6), dpi=150
# Presentation: figsize=(16,9), dpi=150
# Research paper: figsize=(8,6), dpi=300
# Dashboard: figsize=(12,8), dpi=100
Part B — Annotations & Data Labels
B1. Data Labels on Bar Charts
departments = ["IT", "HR", "Finance", "Marketing"]
values = [82500, 55800, 75200, 62400]
fig, ax = plt.subplots(figsize=(10, 6))
bars = ax.bar(departments, values, color="#3498db", width=0.6)
# ===== METHOD 1: Manual Loop =====
for bar, val in zip(bars, values):
ax.text(
bar.get_x() + bar.get_width() / 2, # x = center of bar
bar.get_height() + 800, # y = top of bar + offset
f"₹{val:,}", # formatted text
ha="center", # horizontal: "center","left","right"
va="bottom", # vertical: "top","bottom","center"
fontsize=11,
fontweight="bold",
color="#2c3e50"
)
# ===== METHOD 2: bar_label (Matplotlib 3.4+) =====
# Much simpler! One line!
ax.bar_label(bars,
labels=[f"₹{v:,}" for v in values], # custom formatted labels
padding=5, # distance from bar top
fontsize=11,
fontweight="bold",
color="#2c3e50"
)
# ===== LABELS INSIDE BARS =====
ax.bar_label(bars,
label_type="center", # "edge" (top) or "center" (inside)
fontsize=12, color="white", fontweight="bold"
)
ax.set_title("Salary with Data Labels", fontsize=15, fontweight="bold")
plt.savefig("bar_labels.png", dpi=150, bbox_inches="tight")
plt.show()
B2. plt.annotate() & plt.text() — Custom Annotations
months = ["Jan","Feb","Mar","Apr","May","Jun"]
revenue = [45,52,38,61,55,67]
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(months, revenue, "o-", color="#3498db", linewidth=2, markersize=8)
# ===== ANNOTATE (Arrow + Text) =====
ax.annotate("Peak Month! 🎉",
xy=(5, 67), # arrow points HERE (data coordinates)
xytext=(3, 70), # text sits HERE
fontsize=13,
fontweight="bold",
color="#e74c3c",
arrowprops=dict(
arrowstyle="->", # "->","","fancy","-|>","wedge"
color="#e74c3c",
linewidth=2,
connectionstyle="arc3,rad=0.3" # curved arrow
),
bbox=dict( # text background box
boxstyle="round,pad=0.3",
facecolor="#fadbd8",
edgecolor="#e74c3c",
alpha=0.9
)
)
# Annotate lowest point
ax.annotate("Dip ⚠️",
xy=(2, 38), xytext=(0.5, 30),
fontsize=12, color="#e67e22",
arrowprops=dict(arrowstyle="->", color="#e67e22", linewidth=1.5)
)
# ===== TEXT (No Arrow — Free Floating) =====
ax.text(0.5, 65, # x, y position (data coordinates)
"Target: ₹60K",
fontsize=11,
color="green",
style="italic",
bbox=dict(boxstyle="round", facecolor="#d5f5e3", alpha=0.8)
)
ax.set_title("Revenue with Annotations", fontsize=15, fontweight="bold")
plt.savefig("annotations.png", dpi=150, bbox_inches="tight")
plt.show()
B3. Reference Lines & fill_between()
import numpy as np
x = np.arange(1, 13)
actual = [42,48,55,52,60,58,65,70,62,75,80,90]
target = [50] * 12
fig, ax = plt.subplots(figsize=(12, 6))
ax.plot(x, actual, "o-", color="#3498db", linewidth=2, label="Actual")
# ===== HORIZONTAL REFERENCE LINE =====
ax.axhline(
y=50, # y position
color="red", # line color
linewidth=1.5, # thickness
linestyle="--", # dashed
alpha=0.7,
label="Target: 50K",
zorder=1
)
# ===== VERTICAL REFERENCE LINE =====
ax.axvline(
x=6, # x position
color="#9b59b6",
linewidth=1.5,
linestyle=":",
alpha=0.7,
label="Mid Year"
)
# ===== HORIZONTAL SPAN (shaded region) =====
ax.axhspan(
ymin=45, ymax=55, # y range to shade
color="red", alpha=0.1,
label="Target Zone"
)
# ===== FILL BETWEEN (conditional shading) =====
ax.fill_between(x, actual, 50,
where=[a > 50 for a in actual], # condition: above target
color="#2ecc71", # green = above target
alpha=0.3,
interpolate=True, # smooth fill at crossings
label="Above Target"
)
ax.fill_between(x, actual, 50,
where=[a 50 for a in actual], # below target
color="#e74c3c", # red = below target
alpha=0.3,
interpolate=True,
label="Below Target"
)
ax.set_title("Actual vs Target (Reference Lines + Fill)", fontsize=15, fontweight="bold")
ax.legend(fontsize=10)
ax.set_xlabel("Month")
ax.set_ylabel("Revenue (₹K)")
plt.savefig("reference_fill.png", dpi=150, bbox_inches="tight")
plt.show()
Part C — Layout & Spacing
C1. plt.subplots() — Grid Layout (Multiple Charts)
# ===== 2×2 GRID LAYOUT =====
fig, axes = plt.subplots(
nrows=2, # 2 rows
ncols=2, # 2 columns
figsize=(14, 10), # total figure size
sharex=False, # share x-axis across subplots
sharey=False # share y-axis
)
# Access: axes[row][col]
# Top-Left: Line Chart
axes[0][0].plot([1,2,3,4], [10,20,15,25], "o-", color="#3498db")
axes[0][0].set_title("Line Chart", fontweight="bold")
# Top-Right: Bar Chart
axes[0][1].bar(["A","B","C"], [30,20,25], color="#2ecc71")
axes[0][1].set_title("Bar Chart", fontweight="bold")
# Bottom-Left: Scatter
import numpy as np
axes[1][0].scatter(np.random.rand(50), np.random.rand(50), color="#e74c3c", alpha=0.6)
axes[1][0].set_title("Scatter Plot", fontweight="bold")
# Bottom-Right: Histogram
axes[1][1].hist(np.random.normal(50, 10, 200), bins=20, color="#f39c12", edgecolor="white")
axes[1][1].set_title("Histogram", fontweight="bold")
fig.suptitle("Dashboard — 4 Charts", fontsize=18, fontweight="bold", y=1.02)
plt.tight_layout()
plt.savefig("subplots_2x2.png", dpi=150, bbox_inches="tight")
plt.show()
C2. Different Grid Configurations
# ===== 1×3 HORIZONTAL LAYOUT =====
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(18, 5))
ax1.plot([1,2,3], [10,20,30])
ax1.set_title("Chart 1")
ax2.bar(["A","B"], [10,20])
ax2.set_title("Chart 2")
ax3.scatter([1,2,3], [5,10,15])
ax3.set_title("Chart 3")
plt.tight_layout()
plt.savefig("subplots_1x3.png", dpi=150, bbox_inches="tight")
plt.show()
# ===== 3×1 VERTICAL LAYOUT =====
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(10, 12))
# Same idea — vertical stacking
# ===== UNEQUAL SIZES — GridSpec =====
from matplotlib.gridspec import GridSpec
fig = plt.figure(figsize=(14, 8))
gs = GridSpec(2, 3, figure=fig) # 2 rows, 3 columns grid
# Big chart — spans 2 columns
ax1 = fig.add_subplot(gs[0, :2]) # row 0, columns 0-1
ax1.plot([1,2,3,4], [10,20,15,25], "o-", color="#3498db")
ax1.set_title("Main Chart (Wide)", fontweight="bold")
# Small chart — 1 column
ax2 = fig.add_subplot(gs[0, 2]) # row 0, column 2
ax2.pie([40,30,30], labels=["A","B","C"], autopct="%1.0f%%")
ax2.set_title("Pie", fontweight="bold")
# Full-width bottom chart — spans all 3 columns
ax3 = fig.add_subplot(gs[1, :]) # row 1, all columns
ax3.bar(["Mon","Tue","Wed","Thu","Fri"], [12,19,15,22,18], color="#2ecc71")
ax3.set_title("Weekly Sales (Full Width)", fontweight="bold")
plt.tight_layout()
plt.savefig("gridspec_layout.png", dpi=150, bbox_inches="tight")
plt.show()
C3. Spacing Control — tight_layout() & subplots_adjust()
# ===== METHOD 1: tight_layout() — AUTOMATIC (Recommended) =====
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
# ... add charts ...
plt.tight_layout(
pad=2.0, # padding around entire figure
h_pad=3.0, # vertical space between subplots
w_pad=3.0 # horizontal space between subplots
)
# ===== METHOD 2: subplots_adjust() — MANUAL =====
plt.subplots_adjust(
left=0.1, # left margin (0-1)
right=0.95, # right margin
top=0.92, # top margin
bottom=0.08, # bottom margin
hspace=0.4, # vertical space between subplots
wspace=0.3 # horizontal space between subplots
)
# ===== METHOD 3: constrained_layout (Newer, Better) =====
fig, axes = plt.subplots(2, 2, figsize=(12, 8),
layout="constrained" # auto-adjusts everything perfectly
)
# ===== WHEN TO USE WHAT =====
# tight_layout() → 90% cases, quick fix
# subplots_adjust() → fine-tuning specific margins
# constrained_layout → complex layouts, colorbars
Quick Reference: Styling & Layout Cheat Sheet
| Task | Code |
|---|---|
| Title | ax.set_title("...", fontsize=16, fontweight="bold") |
| Axis Labels | ax.set_xlabel("...") / ax.set_ylabel("...") |
| Legend | ax.legend(loc="upper left", fontsize=11) |
| Grid | ax.grid(True, linestyle="--", alpha=0.3) |
| Hide Spines | ax.spines["top"].set_visible(False) |
| Theme | plt.style.use("seaborn-v0_8") |
| Data Labels | ax.bar_label(bars, padding=5) |
| Annotation | ax.annotate("text", xy=(...), xytext=(...)) |
| Reference Line | ax.axhline(y=50, color="red", linestyle="--") |
| Fill Area | ax.fill_between(x, y, threshold, alpha=0.3) |
| Subplots Grid | fig, axes = plt.subplots(2, 2, figsize=(14,10)) |
| Custom Grid | GridSpec(2, 3); fig.add_subplot(gs[0, :2]) |
| Auto Spacing | plt.tight_layout() |
| Save Chart | plt.savefig("chart.png", dpi=150, bbox_inches="tight") |
Next Post: Part 3
Next part mein hum cover karenge: Advanced Charts — Heatmap, Violin Plot, Error Bars, Contour Plot, Dual Axis aur production-ready dashboard patterns.
Happy Visualizing! 📊🚀
💬 Comments (0)
Loading comments...