πŸ—žοΈ
Subplots Dashboard Grid Layout Week 7

Topic 7.5: The Newspaper Layout

Building multi-panel figure grids with plt.subplots() to present multiple stories side by side

πŸ“° From One Plot to Many β€” The Newspaper Principle
β–Ό

A newspaper front page never shows a single story. It arranges multiple articles side by side because context makes each story more meaningful. Reading about rainfall next to a story about agricultural output tells you something neither story alone could tell.

The same principle applies to data dashboards. Showing water level alongside flow rate for the same river station reveals a relationship. Seeing all four measurement stations on one page lets you compare upstream and downstream behavior at a glance. This is the purpose of subplots in Matplotlib.

ℹ️
The Nile Stations Dataset

All examples use a dataset of four Nile River measurement stations: Aswan, Luxor, Cairo, and Mansoura. Each station has twelve monthly records with four measurements: water_level_m, flow_rate_m3s, temp_c, and turbidity_ntu. Total: 48 rows.

Python
import pandas as pd
import matplotlib.pyplot as plt

# Load the Nile stations dataset
nile = pd.read_csv('../Datasets/7_5_nile_stations.csv')

print("Nile Stations Dataset:")
print(nile.head(12))
print("\nShape:", nile.shape)
print("\nStations:", nile['station'].unique())
print("Columns:", list(nile.columns))
β–Ά Output
Nile Stations Dataset: station month water_level_m flow_rate_m3s temp_c turbidity_ntu 0 Aswan January 178.2 2850 18.5 12.4 1 Aswan February 177.8 2720 20.1 11.8 2 Aswan March 177.1 2600 23.4 13.2 3 Aswan April 176.5 2480 27.8 14.9 4 Aswan May 175.9 2350 31.2 16.3 5 Aswan June 175.4 2200 34.6 18.7 6 Aswan July 176.8 3100 35.1 42.5 7 Aswan August 179.3 5200 34.8 98.3 8 Aswan September 182.1 6800 33.2 124.7 9 Aswan October 181.5 5900 29.4 87.6 10 Aswan November 180.2 4100 24.1 45.2 11 Aswan December 179.0 3300 20.3 22.1 Shape: (48, 6) Stations: <StringArray> ['Aswan', 'Luxor', 'Cairo', 'Mansoura'] Length: 4, dtype: str Columns: ['station', 'month', 'water_level_m', 'flow_rate_m3s', 'temp_c', 'turbidity_ntu']
πŸ”² The 1Γ—2 Layout β€” Side-by-Side Comparison
β–Ό

The plt.subplots(nrows, ncols) function creates a grid of Axes objects inside a single Figure. When called with nrows=1, ncols=2, it produces two plots side by side β€” the simplest multi-panel layout.

The function returns two objects: the Figure (fig) and an array of Axes (axes). With a 1Γ—2 layout, axes is a one-dimensional array β€” you access the left plot as axes[0] and the right plot as axes[1].

Python
# Filter to Cairo data only for this example
cairo = nile[nile['station'] == 'Cairo'].copy()

# Create a 1x2 subplot grid
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(12, 5))

# Left plot: water level across months
axes[0].plot(cairo['month'], cairo['water_level_m'],
             color='steelblue', linewidth=2)
axes[0].set_title('Cairo β€” Water Level (m)')
axes[0].set_xlabel('Month')
axes[0].set_ylabel('Water Level (m)')
axes[0].tick_params(axis='x', rotation=45)

# Right plot: flow rate across months
axes[1].plot(cairo['month'], cairo['flow_rate_m3s'],
             color='darkorange', linewidth=2)
axes[1].set_title('Cairo β€” Flow Rate (mΒ³/s)')
axes[1].set_xlabel('Month')
axes[1].set_ylabel('Flow Rate (mΒ³/s)')
axes[1].tick_params(axis='x', rotation=45)

plt.tight_layout()
plt.show()
Chart output
πŸ’‘
tick_params vs xticks β€” The Subplot-Safe Method

Inside subplots, use axes[i].tick_params(axis='x', rotation=45) instead of plt.xticks(rotation=45). The plt.xticks() function applies to the last active Axes only β€” in a multi-panel layout it will affect only the final subplot. The tick_params method applies to the specific Axes you call it on.

Notice that each Axes has its own independent title, x-label, y-label, and tick settings. The two panels share a Figure but are otherwise completely independent. This is the key design principle: one Figure, multiple independent stories.

πŸ—žοΈ The 2Γ—2 Dashboard Grid
β–Ό

A 2Γ—2 grid gives you four panels in a single Figure β€” enough to show all four Nile stations simultaneously. With plt.subplots(nrows=2, ncols=2), the returned axes array is now two-dimensional, indexed by [row, column].

πŸ—ΊοΈ 2Γ—2 Grid β€” Axes Indexing
Top Row (row 0)
  • axes[0, 0] β†’ Top-left panel
  • axes[0, 1] β†’ Top-right panel
  • Think of it as chess coordinates
Bottom Row (row 1)
  • axes[1, 0] β†’ Bottom-left panel
  • axes[1, 1] β†’ Bottom-right panel
  • First index = row, second = column
Python
# Filter each station
aswan    = nile[nile['station'] == 'Aswan'].copy()
luxor    = nile[nile['station'] == 'Luxor'].copy()
cairo    = nile[nile['station'] == 'Cairo'].copy()
mansoura = nile[nile['station'] == 'Mansoura'].copy()

# Create a 2x2 grid
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(14, 9))

# Top-left: Aswan
axes[0, 0].plot(aswan['month'], aswan['water_level_m'],
                color='steelblue', linewidth=2)
axes[0, 0].set_title('Aswan β€” Water Level (m)')
axes[0, 0].set_ylabel('Water Level (m)')
axes[0, 0].tick_params(axis='x', rotation=45)

# Top-right: Luxor
axes[0, 1].plot(luxor['month'], luxor['water_level_m'],
                color='saddlebrown', linewidth=2)
axes[0, 1].set_title('Luxor β€” Water Level (m)')
axes[0, 1].set_ylabel('Water Level (m)')
axes[0, 1].tick_params(axis='x', rotation=45)

# Bottom-left: Cairo
axes[1, 0].plot(cairo['month'], cairo['water_level_m'],
                color='darkorange', linewidth=2)
axes[1, 0].set_title('Cairo β€” Water Level (m)')
axes[1, 0].set_ylabel('Water Level (m)')
axes[1, 0].tick_params(axis='x', rotation=45)

# Bottom-right: Mansoura
axes[1, 1].plot(mansoura['month'], mansoura['water_level_m'],
                color='tomato', linewidth=2)
axes[1, 1].set_title('Mansoura β€” Water Level (m)')
axes[1, 1].set_ylabel('Water Level (m)')
axes[1, 1].tick_params(axis='x', rotation=45)

fig.suptitle('Nile River Stations β€” Annual Overview', fontsize=15, fontweight='bold')
plt.tight_layout()
plt.show()
Chart output
ℹ️
fig.suptitle() β€” The Page Headline

fig.suptitle() adds a title above the entire Figure β€” spanning all panels. This is the 'page headline' that gives context to the entire dashboard, while each panel's set_title() is a 'subheading' for that individual chart. Use fontweight='bold' to make it stand out.

πŸ”„ axes.flatten() β€” Looping Over Subplots
β–Ό

When you have a 2D array of Axes (from a 2Γ—2 or larger grid) and want to apply the same chart type to each one, writing out axes[0,0], axes[0,1], axes[1,0], axes[1,1] separately is repetitive and fragile. The axes.flatten() method converts the 2D array into a 1D array, enabling a clean loop.

Python
stations = ['Aswan', 'Luxor', 'Cairo', 'Mansoura']
colors   = ['steelblue', 'darkorange', 'seagreen', 'crimson']

# 2x2 grid, then flatten to 1D for easy loop indexing
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(14, 9))
axes_flat = axes.flatten()

for i in range(len(stations)):
    data = nile[nile['station'] == stations[i]]
    axes_flat[i].plot(data['month'], data['flow_rate_m3s'],
                      color=colors[i], linewidth=2)
    axes_flat[i].set_title(f'{stations[i]} β€” Flow Rate (mΒ³/s)')
    axes_flat[i].set_ylabel('Flow Rate (mΒ³/s)')
    axes_flat[i].tick_params(axis='x', rotation=45)

fig.suptitle('Nile Flow Rate: All Four Stations Compared', fontsize=15, fontweight='bold')
plt.tight_layout()
plt.show()
Chart output

After axes.flatten(), the loop uses a single index i to access both the station data (stations[i]), the color (colors[i]), and the Axes panel (axes_flat[i]). Adding a fifth station later would only require changing one number in plt.subplots().

plt.subplots(1, 2)
1 row, 2 columns β€” axes is a 1D array of length 2
plt.subplots(2, 2)
2 rows, 2 columns β€” axes is a 2D array, shape (2,2)
axes[0]
Access first panel (1D layout)
axes[0, 1]
Access top-right panel (2D layout, row=0, col=1)
axes.flatten()
Convert 2D axes array to 1D β€” enables simple loop indexing
fig.suptitle()
Add a master title spanning the entire Figure
plt.tight_layout()
Auto-adjust panel spacing to prevent overlapping labels
  • plt.subplots(nrows, ncols) creates a grid of Axes inside one Figure β€” enabling newspaper-style multi-panel layouts.
  • The function returns (fig, axes): fig is the canvas; axes is an array of Axes objects indexed by position.
  • In a 1D layout (1 row or 1 column), access panels with axes[i]. In a 2D layout (multiple rows and columns), use axes[row, col].
  • Each Axes is independent β€” it has its own title, labels, colors, and data. Use ax.set_title(), ax.set_xlabel(), and ax.tick_params() on each Axes separately.
  • axes.flatten() converts a 2D array of Axes into a 1D array, enabling clean for loops over all panels.
  • fig.suptitle() adds a master headline above the entire Figure β€” complementing, not replacing, the individual panel titles.
  • plt.tight_layout() automatically adjusts spacing between panels to prevent labels from overlapping.
πŸ“šExternal Resources
β–Ό