Skip to content

Axes Method - contour

The ax.contour() method draws contour lines for scalar fields.

Official Documentation

Basic Usage

Simple Contour Plot

import matplotlib.pyplot as plt
import numpy as np

def main():
    f = lambda x, y: np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
    x = np.linspace(0, 5, 50)
    y = np.linspace(0, 5, 40)
    X, Y = np.meshgrid(x, y)
    Z = f(X, Y)

    fig, ax = plt.subplots(figsize=(12, 4))
    ax.contour(X, Y, Z, levels=3, colors='black')
    plt.show()

if __name__ == "__main__":
    main()

Creating Meshgrid

Understanding np.meshgrid

import numpy as np

x = np.linspace(0, 5, 50)  # 50 points from 0 to 5
y = np.linspace(0, 5, 40)  # 40 points from 0 to 5
X, Y = np.meshgrid(x, y)

print(f"x shape: {x.shape}")      # (50,)
print(f"y shape: {y.shape}")      # (40,)
print(f"X shape: {X.shape}")      # (40, 50)
print(f"Y shape: {Y.shape}")      # (40, 50)

Computing Z Values

import numpy as np

f = lambda x, y: np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)

x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 40)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)

print(f"Z shape: {Z.shape}")      # (40, 50)
print(f"Z min: {Z.min():.3f}")
print(f"Z max: {Z.max():.3f}")

Levels Parameter

Number of Levels

import matplotlib.pyplot as plt
import numpy as np

f = lambda x, y: np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 40)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)

fig, axes = plt.subplots(1, 4, figsize=(16, 4))
level_counts = [3, 7, 15, 30]

for ax, n_levels in zip(axes, level_counts):
    ax.contour(X, Y, Z, levels=n_levels, colors='black')
    ax.set_title(f'levels={n_levels}')
    ax.set_aspect('equal')

plt.tight_layout()
plt.show()

Specific Level Values

import matplotlib.pyplot as plt
import numpy as np

f = lambda x, y: np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 40)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)

fig, axes = plt.subplots(1, 2, figsize=(12, 4))

# Evenly spaced levels
axes[0].contour(X, Y, Z, levels=np.linspace(-1, 1, 11), colors='black')
axes[0].set_title('levels=np.linspace(-1, 1, 11)')

# Custom specific levels
axes[1].contour(X, Y, Z, levels=[-0.5, 0, 0.5], colors='black')
axes[1].set_title('levels=[-0.5, 0, 0.5]')

for ax in axes:
    ax.set_aspect('equal')
plt.tight_layout()
plt.show()

Colors and Colormaps

Single Color

import matplotlib.pyplot as plt
import numpy as np

f = lambda x, y: np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 40)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)

fig, axes = plt.subplots(1, 3, figsize=(15, 4))
colors_list = ['black', 'blue', 'red']

for ax, color in zip(axes, colors_list):
    ax.contour(X, Y, Z, levels=10, colors=color)
    ax.set_title(f"colors='{color}'")
    ax.set_aspect('equal')

plt.tight_layout()
plt.show()

Using Colormaps

import matplotlib.pyplot as plt
import numpy as np

f = lambda x, y: np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 40)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)

fig, axes = plt.subplots(1, 3, figsize=(15, 4))
cmaps = ['viridis', 'plasma', 'coolwarm']

for ax, cmap in zip(axes, cmaps):
    cs = ax.contour(X, Y, Z, levels=15, cmap=cmap)
    ax.set_title(f"cmap='{cmap}'")
    ax.set_aspect('equal')
    fig.colorbar(cs, ax=ax, shrink=0.8)

plt.tight_layout()
plt.show()

Line Styles

Linewidths

import matplotlib.pyplot as plt
import numpy as np

f = lambda x, y: np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 40)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)

fig, axes = plt.subplots(1, 3, figsize=(15, 4))
linewidths = [0.5, 1.5, 3]

for ax, lw in zip(axes, linewidths):
    ax.contour(X, Y, Z, levels=10, colors='black', linewidths=lw)
    ax.set_title(f'linewidths={lw}')
    ax.set_aspect('equal')

plt.tight_layout()
plt.show()

Linestyles

import matplotlib.pyplot as plt
import numpy as np

f = lambda x, y: np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 40)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)

fig, axes = plt.subplots(1, 3, figsize=(15, 4))
linestyles = ['solid', 'dashed', 'dotted']

for ax, ls in zip(axes, linestyles):
    ax.contour(X, Y, Z, levels=10, colors='black', linestyles=ls)
    ax.set_title(f"linestyles='{ls}'")
    ax.set_aspect('equal')

plt.tight_layout()
plt.show()

Filled Contours

contourf

import matplotlib.pyplot as plt
import numpy as np

f = lambda x, y: np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 40)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)

fig, axes = plt.subplots(1, 2, figsize=(12, 4))

# Line contour
axes[0].contour(X, Y, Z, levels=15, cmap='viridis')
axes[0].set_title('contour (lines)')

# Filled contour
cs = axes[1].contourf(X, Y, Z, levels=15, cmap='viridis')
axes[1].set_title('contourf (filled)')
fig.colorbar(cs, ax=axes[1], shrink=0.8)

for ax in axes:
    ax.set_aspect('equal')
plt.tight_layout()
plt.show()

Combined Lines and Fill

import matplotlib.pyplot as plt
import numpy as np

f = lambda x, y: np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 40)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)

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

# Filled contours
cf = ax.contourf(X, Y, Z, levels=15, cmap='viridis', alpha=0.8)

# Line contours on top
cs = ax.contour(X, Y, Z, levels=15, colors='black', linewidths=0.5)

fig.colorbar(cf, ax=ax)
ax.set_title('Filled Contours with Lines')
ax.set_aspect('equal')
plt.tight_layout()
plt.show()

Common Functions

Circle/Ellipse

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-3, 3, 100)
y = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x, y)
Z = X**2 + Y**2  # Circle

fig, ax = plt.subplots(figsize=(6, 6))
cs = ax.contour(X, Y, Z, levels=[1, 2, 4, 6, 8], colors='black')
ax.clabel(cs, inline=True, fontsize=10)
ax.set_title('Circles: $x^2 + y^2 = c$')
ax.set_aspect('equal')
plt.show()

Saddle Point

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-3, 3, 100)
y = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x, y)
Z = X**2 - Y**2  # Hyperbolic paraboloid

fig, ax = plt.subplots(figsize=(8, 6))
cs = ax.contour(X, Y, Z, levels=15, cmap='RdBu')
fig.colorbar(cs, ax=ax)
ax.set_title('Saddle: $z = x^2 - y^2$')
ax.set_aspect('equal')
plt.show()

Gaussian

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-3, 3, 100)
y = np.linspace(-3, 3, 100)
X, Y = np.meshgrid(x, y)
Z = np.exp(-(X**2 + Y**2))  # Gaussian

fig, ax = plt.subplots(figsize=(8, 6))
cs = ax.contourf(X, Y, Z, levels=20, cmap='hot')
ax.contour(X, Y, Z, levels=10, colors='black', linewidths=0.5)
fig.colorbar(cs, ax=ax)
ax.set_title('Gaussian: $z = e^{-(x^2 + y^2)}$')
ax.set_aspect('equal')
plt.show()

Practical Example

Topographic Map Style

import matplotlib.pyplot as plt
import numpy as np

# Create terrain-like surface
np.random.seed(42)
x = np.linspace(0, 10, 100)
y = np.linspace(0, 10, 100)
X, Y = np.meshgrid(x, y)

Z = (np.sin(X) * np.cos(Y) + 
     0.5 * np.sin(2*X) * np.cos(2*Y) +
     0.25 * np.sin(4*X) * np.cos(4*Y))

fig, ax = plt.subplots(figsize=(10, 8))

# Filled contours for elevation colors
cf = ax.contourf(X, Y, Z, levels=20, cmap='terrain')

# Contour lines
cs = ax.contour(X, Y, Z, levels=10, colors='black', linewidths=0.5)
ax.clabel(cs, inline=True, fontsize=8, fmt='%.1f')

fig.colorbar(cf, ax=ax, label='Elevation')
ax.set_title('Topographic Contour Map')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_aspect('equal')
plt.tight_layout()
plt.show()