A great visualisation can communicate a finding in seconds that a table of numbers cannot convey in minutes. Matplotlib is Python’s foundational plotting library — powerful and precise, but verbose. Seaborn builds on Matplotlib to provide beautiful statistical charts with minimal code. This guide covers both, from quick exploratory plots to publication-quality figures.
Matplotlib Fundamentals
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
# Figure and axes objects — the right way to use Matplotlib
fig, ax = plt.subplots(figsize=(10, 6))
x = np.linspace(0, 4 * np.pi, 200)
ax.plot(x, np.sin(x), label='sin(x)', linewidth=2, color='steelblue')
ax.plot(x, np.cos(x), label='cos(x)', linewidth=2, color='tomato', linestyle='--')
ax.set_title('Trigonometric Functions', fontsize=16, fontweight='bold', pad=15)
ax.set_xlabel('x (radians)', fontsize=13)
ax.set_ylabel('Amplitude', fontsize=13)
ax.legend(fontsize=12)
ax.grid(True, alpha=0.3)
ax.set_xlim(0, 4 * np.pi)
ax.set_ylim(-1.3, 1.3)
ax.spines[['top', 'right']].set_visible(False)
plt.tight_layout()
plt.savefig('trig.png', dpi=150, bbox_inches='tight')
plt.show()
Common Chart Types
np.random.seed(42)
df = pd.DataFrame({
'month': pd.date_range('2025-01', periods=12, freq='ME'),
'revenue': np.cumsum(np.random.normal(10000, 2000, 12)) + 50000,
'region': np.random.choice(['North', 'South', 'East', 'West'], 12),
'score': np.random.normal(70, 15, 12).clip(0, 100)
})
fig, axes = plt.subplots(2, 3, figsize=(16, 9))
# Line chart
axes[0, 0].plot(df['month'], df['revenue'], 'o-', color='steelblue')
axes[0, 0].set_title('Revenue Over Time'); axes[0, 0].tick_params(axis='x', rotation=45)
# Bar chart
region_rev = df.groupby('region')['revenue'].mean().sort_values()
axes[0, 1].barh(region_rev.index, region_rev.values, color='steelblue', edgecolor='white')
axes[0, 1].set_title('Avg Revenue by Region')
# Histogram
axes[0, 2].hist(df['score'], bins=8, color='steelblue', edgecolor='white', alpha=0.8)
axes[0, 2].axvline(df['score'].mean(), color='red', linestyle='--', label=f'Mean={df["score"].mean():.1f}')
axes[0, 2].legend(); axes[0, 2].set_title('Score Distribution')
# Scatter plot
x2 = np.random.normal(0, 1, 200); y2 = 2 * x2 + np.random.normal(0, 0.5, 200)
axes[1, 0].scatter(x2, y2, alpha=0.5, s=20, color='steelblue')
z = np.polyfit(x2, y2, 1)
axes[1, 0].plot(sorted(x2), np.poly1d(z)(sorted(x2)), 'r-')
axes[1, 0].set_title('Scatter with Trend')
# Box plot
data_box = [np.random.normal(loc, 1, 100) for loc in [0, 1, 2, 1.5]]
bp = axes[1, 1].boxplot(data_box, labels=['A', 'B', 'C', 'D'], patch_artist=True)
for patch, color in zip(bp['boxes'], ['steelblue', 'tomato', 'green', 'orange']):
patch.set_facecolor(color)
axes[1, 1].set_title('Distribution by Group')
# Pie chart
sizes = [35, 25, 20, 20]
axes[1, 2].pie(sizes, labels=['North', 'South', 'East', 'West'],
autopct='%1.0f%%', startangle=90,
colors=['steelblue', 'tomato', 'green', 'orange'])
axes[1, 2].set_title('Revenue Share by Region')
plt.suptitle('Sales Dashboard', fontsize=18, fontweight='bold', y=1.01)
plt.tight_layout()
plt.savefig('dashboard.png', dpi=150, bbox_inches='tight')
plt.show()
Seaborn for Statistical Visualisation
import seaborn as sns
# Load a real dataset
iris = sns.load_dataset('iris')
tips = sns.load_dataset('tips')
flights = sns.load_dataset('flights')
# Global style
sns.set_theme(style='whitegrid', palette='muted', font_scale=1.2)
# Distribution plots
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
sns.histplot(iris['sepal_length'], kde=True, ax=axes[0])
sns.boxplot(data=iris, x='species', y='sepal_length', ax=axes[1])
sns.violinplot(data=iris, x='species', y='sepal_length',
inner='quartile', ax=axes[2])
plt.tight_layout(); plt.show()
Heatmap and Pair Plot
# Correlation heatmap
fig, ax = plt.subplots(figsize=(8, 6))
corr = iris.drop('species', axis=1).corr()
sns.heatmap(corr, annot=True, fmt='.2f', cmap='RdYlGn',
center=0, square=True, linewidths=0.5, ax=ax)
ax.set_title('Feature Correlation Matrix')
plt.tight_layout(); plt.show()
# Pair plot — relationships between all feature pairs
g = sns.pairplot(iris, hue='species', diag_kind='kde',
plot_kws={'alpha': 0.6, 's': 40})
g.fig.suptitle('Iris Feature Pair Plot', y=1.02)
plt.show()
# FacetGrid — same plot across subgroups
g = sns.FacetGrid(tips, col='time', row='smoker', margin_titles=True)
g.map(sns.scatterplot, 'total_bill', 'tip', alpha=0.7)
g.add_legend()
plt.show()
Advanced Seaborn Charts
# Heatmap of flights pivot table
pivot = flights.pivot_table(index='month', columns='year', values='passengers')
plt.figure(figsize=(12, 6))
sns.heatmap(pivot, fmt='d', annot=True, cmap='YlOrRd', linewidths=0.3)
plt.title('Passenger Counts by Month and Year')
plt.tight_layout(); plt.show()
# Regression plot
plt.figure(figsize=(8, 5))
sns.regplot(data=tips, x='total_bill', y='tip',
scatter_kws={'alpha': 0.5}, line_kws={'color': 'red'})
plt.title('Tip vs Total Bill with Regression Line')
plt.show()
# Count and bar plots
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
sns.countplot(data=tips, x='day', hue='sex', ax=axes[0])
sns.barplot(data=tips, x='day', y='tip', hue='sex',
estimator='mean', ci=95, ax=axes[1])
axes[0].set_title('Count by Day and Sex')
axes[1].set_title('Mean Tip by Day and Sex')
plt.tight_layout(); plt.show()
Publication-Quality Figures
# Publication style
plt.rcParams.update({
'font.family': 'serif',
'font.size': 11,
'axes.titlesize': 13,
'axes.labelsize': 11,
'xtick.labelsize': 10,
'ytick.labelsize': 10,
'legend.fontsize': 10,
'figure.dpi': 150,
'axes.spines.top': False,
'axes.spines.right': False,
})
fig, ax = plt.subplots(figsize=(7, 4.5))
# ... your plot ...
plt.tight_layout()
plt.savefig('figure1.pdf', format='pdf', bbox_inches='tight') # vector
plt.savefig('figure1.png', format='png', dpi=300, bbox_inches='tight') # raster
Conclusion
Use Matplotlib when you need precise control over every element of the figure. Use Seaborn for statistical charts — its one-liners produce publication-quality output that would take 20 lines in raw Matplotlib. For interactive charts, switch to Plotly (plotly.express is Seaborn’s interactive equivalent). For embedded dashboards, use Streamlit or Plotly Dash. Whatever the tool, the most important skill is not the code — it is choosing the right chart type for the question you are answering.


