Right, let’s talk about making your plots not look like they were generated by a spreadsheet program from 1997. The default Matplotlib styles are… functional. They get the point across, but they scream “default settings.” We’re better than that. Customization is where a simple chart becomes your chart, where you guide the viewer’s eye and reinforce your narrative. It’s the difference between pointing at something and handing your reader a highlighter.

The Holy Trinity: Titles, Axes Labels, and Legends

Any plot worth its salt tells you what it’s about. plt.title(), plt.xlabel(), and plt.ylabel() are your best friends here. But let’s be direct: the default font sizes are too small. Always bump them up. A legend, created automatically when you plot labeled data, is crucial for distinguishing elements. Its default location ('best') is Matplotlib politely throwing its hands in the air and asking the algorithm to guess. It’s often wrong. Take control.

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)

plt.figure(figsize=(8, 5)) # Pro tip: Set figure size first thing
plt.plot(x, y1, label='Sin(x)') # Label your data!
plt.plot(x, y2, label='Cos(x)') # Seriously, do it.

plt.title('A Tale of Two Waves', fontsize=16, fontweight='bold') # Bigger, bolder.
plt.xlabel('Time (s)', fontsize=14)
plt.ylabel('Amplitude', fontsize=14)
plt.legend(loc='upper right', frameon=True, shadow=True) # Be specific. Frames and shadows are nice.
plt.grid(True, alpha=0.3) # Grids are good, but make them subtle.

plt.tight_layout() # This is non-negotiable. It fixes layout jank.
plt.show()

The tight_layout() call is your insurance policy against cut-off labels. Use it religiously. For legends, loc accepts codes like 'upper left' or numbers (e.g., 0 for “best”, but don’t be a coward—pick a real spot).

Taming the Color Cycle

Matplotlib has a default color cycle. It’s fine. It’s also deeply boring and can be problematic for colorblind readers. You can change the entire cycle using plt.rcParams['axes.prop_cycle'], but for individual plots, it’s easier to just specify colors directly. Use hex codes ('#ff5500') or HTML color names ('tomato'—yes, that’s a real one). Better yet, use a accessible palette from Seaborn.

# A more thoughtful approach to color
custom_colors = ['#1f77b4', '#ff7f0e', '#2ca02c'] # The "better" default cycle
plt.plot(x, y1, color=custom_colors[0], linewidth=2.5, label='Sin(x)') # Note linewidth too!
plt.plot(x, y2, color=custom_colors[1], linewidth=2.5, label='Cos(x)')

Why linewidth=2.5? Because the default is a spindly 1.5. A little more heft makes your lines stand out clearly, especially in presentations.

The Power (and Danger) of rcParams

Here’s where you go from user to wizard. Matplotlib’s rcParams dictionary controls virtually every default setting globally for your session. It’s incredibly powerful and equally dangerous. Change a setting here, and every subsequent plot will obey. This is how you enforce a consistent style across all figures in a report.

# Set global defaults. Do this once at the top of your script/notebook.
plt.rcParams.update({
    'font.size': 12,           # Larger base font size
    'axes.titlesize': 16,      # Bigger titles
    'axes.labelsize': 14,      # Bigger axis labels
    'lines.linewidth': 2.5,    # Thicker lines
    'grid.alpha': 0.3,         # Subtler grid
    'figure.figsize': (8, 5),  # Sensible default figure size
    'figure.autolayout': True  # Can replace tight_layout() calls
})

The best practice? Set all your rcParams at the very beginning of your code. It creates consistency and saves you from repetitive formatting calls. The worst practice? Changing them mid-script and wondering why your fifth plot looks different from your first.

Styles: The Instant Makeover

If tweaking individual rcParams feels like micromanaging, use a style. plt.style.use() is like slapping a filter on your entire plot. 'ggplot', 'seaborn-v0_8', and 'seaborn-whitegrid' are massive improvements over the default. 'dark_background' is great for presentations.

plt.style.use('seaborn-v0_8-whitegrid') # Clean, modern, with a subtle grid
# Now plot your data. It will automatically inherit this style.
plt.plot(x, y1, label='Sin(x)')
plt.plot(x, y2, label='Cos(x)')
plt.legend()
plt.show()

You can also combine styles: plt.style.use(['seaborn-v0_8-whitegrid', 'seaborn-v0_8-poster']). The right-most style in the list takes precedence. This is the fastest way to make your plots look professionally designed without the fuss. The choice isn’t arbitrary; 'seaborn-whitegrid' is popular for a reason—the light grid lines improve readability for comparing values without becoming visual clutter. It’s a design choice that respects the data.