<DataInsights />
  • 🏠 Home
  • 📊 SQL
  • 🐍 Python
  • 📈 Power BI
  • 📗 Excel
  • 💼 Career
  • 🎯 Interview Q&A
  • 📁 Case Study
  • 📥 Downloads
  • 🚀 My Portfolio
<DataInsights />

Practical Data Analytics tutorials covering SQL, Python, Power BI, Excel and career guidance for aspiring analysts — 100% free.

Topics

  • SQL Tutorials
  • Python Guide
  • Power BI
  • Excel Tips
  • Career Guide

Quick Links

  • 🛠️ All Tools
  • 🗓️ Archive
  • 📬 Contact
  • 🔍 Search
  • Portfolio
  • Kaggle
  • GitHub

Legal & Info

  • About
  • Contact
  • Privacy Policy
  • Disclaimer
  • Terms & Conditions
  • DMCA
  • Sitemap
Copyright © 2026 Data Insights by Jatin Kumar. All Rights Reserved.Built with ❤️ for Data Analysts
Home/Python/Styling, Annotations And Layout in Matplotlib...

Styling, Annotations And Layout in Matplotlib

A
August 3, 2026 Jatin Kumar 15 min read Python
Data Insights Matplotlib Masterclass — Part 2

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
Titleax.set_title("...", fontsize=16, fontweight="bold")
Axis Labelsax.set_xlabel("...") / ax.set_ylabel("...")
Legendax.legend(loc="upper left", fontsize=11)
Gridax.grid(True, linestyle="--", alpha=0.3)
Hide Spinesax.spines["top"].set_visible(False)
Themeplt.style.use("seaborn-v0_8")
Data Labelsax.bar_label(bars, padding=5)
Annotationax.annotate("text", xy=(...), xytext=(...))
Reference Lineax.axhline(y=50, color="red", linestyle="--")
Fill Areaax.fill_between(x, y, threshold, alpha=0.3)
Subplots Gridfig, axes = plt.subplots(2, 2, figsize=(14,10))
Custom GridGridSpec(2, 3); fig.add_subplot(gs[0, :2])
Auto Spacingplt.tight_layout()
Save Chartplt.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! 📊🚀

👤
Jatin Kumar
Data Analyst & Educator

Python, SQL, Power BI aur Excel mein practical tutorials likhta hoon — taaki data analytics seekhna aasan ho. Portfolio: jatinanalytics.co.in

Portfolio LinkedIn GitHub Kaggle All Articles
Share:

💬 Comments (0)

Spam/links allowed nahi hain — respectful comments welcome!

Loading comments...

Was this article helpful?
Previous ArticleBasic Plot Creation in MatplotlibNext Article Advanced Charts, Dashboards And Export in Matplotlib

📚 More Articles Like This

Feature Engineering Functions in Pandas

Read Article

Merge, Join And Concat in Pandas

Read Article

Loops, Apply And Lambda Functions in Pandas

Read Article