Skip to content

Data Visualization with Matplotlib

Scientific visualization turns raw numerical simulation arrays into clear physical insights. Matplotlib is the standard plotting library in Python.

In this tutorial, you will learn: 1. The object-oriented fig, ax = plt.subplots() paradigm. 2. Styling electrochemical plots following IUPAC conventions. 3. Creating multi-panel figures with shared axes. 4. Exporting publication-ready vector figures (.svg and .pdf).


1. Object-Oriented Plotting Basics

While plt.plot(...) works for quick sketches, the object-oriented API (fig, ax = plt.subplots()) gives you full control over every element in your figure:

import matplotlib.pyplot as plt
import numpy as np
from softpotato.analytical import cottrell

# Generate sample data
t = np.linspace(0.01, 5.0, 200)
i_transient = cottrell(
    time=t,
    c_bulk=1.0,
    d=1e-9,
    area=1e-4,
    n=1,
)

# Create figure and axis
fig, ax = plt.subplots(figsize=(6, 4), dpi=100)

# Plot current in microamperes
ax.plot(
    t,
    i_transient * 1e6,
    color="#FD8031",
    linewidth=2,
    label="Planar Diffusion",
)

# Labels and title
ax.set_xlabel("Time $t$ / s", fontsize=11)
ax.set_ylabel("Current $I$ / µA", fontsize=11)
ax.set_title("Cottrell Chronoamperometry Transient", fontsize=12, pad=10)

# Grid and legend
ax.grid(True, linestyle="--", alpha=0.5)
ax.legend(frameon=True, facecolor="white", edgecolor="none")

# Clean layout
fig.tight_layout()
plt.show()

2. Formatting Cyclic Voltammograms

Electrochemical voltammograms require specific conventions:

# Simulated CV data
potential = np.linspace(-0.2, 0.6, 200)
# Example reversible shape approximation
current_anodic = 15.0 / (1.0 + np.exp(-(potential - 0.23) / 0.03))
current_cathodic = -15.0 / (1.0 + np.exp((potential - 0.17) / 0.03))
cv_current = (current_anodic + current_cathodic) * 1e-6

fig, ax = plt.subplots(figsize=(6, 5))

ax.plot(potential, cv_current * 1e6, color="#344050", lw=2, label="100 mV/s")

# Zero lines
ax.axhline(0, color="gray", lw=0.8, linestyle=":")
ax.axvline(0, color="gray", lw=0.8, linestyle=":")

# IUPAC Convention: Anodic > 0, Cathodic < 0
ax.set_xlabel("Potential $E$ vs Reference / V")
ax.set_ylabel("Current $I$ / µA")
ax.set_title("Reversible Cyclic Voltammogram")

ax.grid(True, alpha=0.3)
ax.legend()
fig.tight_layout()
plt.show()

3. Multi-Panel Figures (Subplots)

Often you need to show concentration profiles alongside the current response:

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(11, 4.5))

# Panel 1: Current vs Time
ax1.plot(t, i_transient * 1e6, color="#FD8031", lw=2)
ax1.set_xlabel("Time $t$ / s")
ax1.set_ylabel("Current $I$ / µA")
ax1.set_title("(a) Chronoamperometric Transient")
ax1.grid(True, alpha=0.3)

# Panel 2: Cottrell Plot (I vs 1 / sqrt(t))
inv_sqrt_t = 1.0 / np.sqrt(t)
ax2.plot(inv_sqrt_t, i_transient * 1e6, "o-", color="#83BEB2", markersize=3)
ax2.set_xlabel("Inverse Square Root Time $t^{-1/2}$ / s$^{-1/2}$")
ax2.set_ylabel("Current $I$ / µA")
ax2.set_title("(b) Linear Cottrell Diagnostic")
ax2.grid(True, alpha=0.3)

fig.tight_layout()
plt.show()

4. Exporting Publication-Ready Figures

When preparing figures for research papers, thesis chapters, or presentations, never export low-resolution blurry PNGs. Use vector graphics or high-DPI raster images:

# 1. Vector PDF (best for LaTeX / Overleaf documents)
fig.savefig("cottrell_analysis.pdf", bbox_inches="tight")

# 2. Vector SVG (best for web pages, presentations, and Inkscape editing)
fig.savefig("cottrell_analysis.svg", bbox_inches="tight")

# 3. High-resolution PNG (300 DPI for Word / PowerPoint)
fig.savefig("cottrell_analysis.png", dpi=300, bbox_inches="tight")

Chip's Tip: Use bbox_inches='tight'

Always pass bbox_inches='tight' when saving figures! This automatically removes excess whitespace and prevents axis labels from being accidentally clipped off outside the image canvas.