# Matplotlib Styling Guide Comprehensive guide for styling and customizing matplotlib visualizations. ## Colormaps ### Colormap Categories **1. Perceptually Uniform Sequential** Best for ordered data that progresses from low to high values. - `viridis` (default, colorblind-friendly) - `plasma` - `inferno` - `magma` - `cividis` (optimized for colorblind viewers) **Usage:** ```python im = ax.imshow(data, cmap='viridis') scatter = ax.scatter(x, y, c=values, cmap='plasma') ``` **2. Sequential** Traditional colormaps for ordered data. - `Blues`, `Greens`, `Reds`, `Oranges`, `Purples` - `YlOrBr`, `YlOrRd`, `OrRd`, `PuRd` - `BuPu`, `GnBu`, `PuBu`, `YlGnBu` **3. Diverging** Best for data with a meaningful center point (e.g., zero, mean). - `coolwarm` (blue to red) - `RdBu` (red-blue) - `RdYlBu` (red-yellow-blue) - `RdYlGn` (red-yellow-green) - `PiYG`, `PRGn`, `BrBG`, `PuOr`, `RdGy` **Usage:** ```python # Center colormap at zero im = ax.imshow(data, cmap='coolwarm', vmin=-1, vmax=1) ``` **4. Qualitative** Best for categorical/nominal data without inherent ordering. - `tab10` (10 distinct colors) - `tab20` (20 distinct colors) - `Set1`, `Set2`, `Set3` - `Pastel1`, `Pastel2` - `Dark2`, `Accent`, `Paired` **Usage:** ```python colors = plt.cm.tab10(np.linspace(0, 1, n_categories)) for i, category in enumerate(categories): ax.plot(x, y[i], color=colors[i], label=category) ``` **5. Cyclic** Best for cyclic data (e.g., phase, angle). - `twilight` - `twilight_shifted` - `hsv` ### Colormap Best Practices 1. **Avoid `jet` colormap** - Not perceptually uniform, misleading 2. **Use perceptually uniform colormaps** - `viridis`, `plasma`, `cividis` 3. **Consider colorblind users** - Use `viridis`, `cividis`, or test with colorblind simulators 4. **Match colormap to data type**: - Sequential: increasing/decreasing data - Diverging: data with meaningful center - Qualitative: categories 5. **Reverse colormaps** - Add `_r` suffix: `viridis_r`, `coolwarm_r` ### Creating Custom Colormaps ```python from matplotlib.colors import LinearSegmentedColormap # From color list colors = ['blue', 'white', 'red'] n_bins = 100 cmap = LinearSegmentedColormap.from_list('custom', colors, N=n_bins) # From RGB values colors = [(0, 0, 1), (1, 1, 1), (1, 0, 0)] # RGB tuples cmap = LinearSegmentedColormap.from_list('custom', colors) # Use the custom colormap ax.imshow(data, cmap=cmap) ``` ### Discrete Colormaps ```python import matplotlib.colors as mcolors # Create discrete colormap from continuous cmap = plt.cm.viridis bounds = np.linspace(0, 10, 11) norm = mcolors.BoundaryNorm(bounds, cmap.N) im = ax.imshow(data, cmap=cmap, norm=norm) ``` ## Style Sheets ### Using Built-in Styles ```python # List available styles print(plt.style.available) # Apply a style plt.style.use('seaborn-v0_8-darkgrid') # Apply multiple styles (later styles override earlier ones) plt.style.use(['seaborn-v0_8-whitegrid', 'seaborn-v0_8-poster']) # Temporarily use a style with plt.style.context('ggplot'): fig, ax = plt.subplots() ax.plot(x, y) ``` ### Popular Built-in Styles - `default` - Matplotlib's default style - `classic` - Classic matplotlib look (pre-2.0) - `seaborn-v0_8-*` - Seaborn-inspired styles - `seaborn-v0_8-darkgrid`, `seaborn-v0_8-whitegrid` - `seaborn-v0_8-dark`, `seaborn-v0_8-white` - `seaborn-v0_8-ticks`, `seaborn-v0_8-poster`, `seaborn-v0_8-talk` - `ggplot` - ggplot2-inspired style - `bmh` - Bayesian Methods for Hackers style - `fivethirtyeight` - FiveThirtyEight style - `grayscale` - Grayscale style ### Creating Custom Style Sheets Create a file named `custom_style.mplstyle`: ``` # custom_style.mplstyle # Figure figure.figsize: 10, 6 figure.dpi: 100 figure.facecolor: white # Font font.family: sans-serif font.sans-serif: Arial, Helvetica font.size: 12 # Axes axes.labelsize: 14 axes.titlesize: 16 axes.facecolor: white axes.edgecolor: black axes.linewidth: 1.5 axes.grid: True axes.axisbelow: True # Grid grid.color: gray grid.linestyle: -- grid.linewidth: 0.5 grid.alpha: 0.3 # Lines lines.linewidth: 2 lines.markersize: 8 # Ticks xtick.labelsize: 10 ytick.labelsize: 10 xtick.direction: in ytick.direction: in xtick.major.size: 6 ytick.major.size: 6 xtick.minor.size: 3 ytick.minor.size: 3 # Legend legend.fontsize: 12 legend.frameon: True legend.framealpha: 0.8 legend.fancybox: True # Savefig savefig.dpi: 300 savefig.bbox: tight savefig.facecolor: white ``` Load and use: ```python plt.style.use('path/to/custom_style.mplstyle') ``` ## rcParams Configuration ### Global Configuration ```python import matplotlib.pyplot as plt # Configure globally plt.rcParams['figure.figsize'] = (10, 6) plt.rcParams['font.size'] = 12 plt.rcParams['axes.labelsize'] = 14 # Or update multiple at once plt.rcParams.update({ 'figure.figsize': (10, 6), 'font.size': 12, 'axes.labelsize': 14, 'axes.titlesize': 16, 'lines.linewidth': 2 }) ``` ### Temporary Configuration ```python # Context manager for temporary changes with plt.rc_context({'font.size': 14, 'lines.linewidth': 2.5}): fig, ax = plt.subplots() ax.plot(x, y) ``` ### Common rcParams **Figure settings:** ```python plt.rcParams['figure.figsize'] = (10, 6) plt.rcParams['figure.dpi'] = 100 plt.rcParams['figure.facecolor'] = 'white' plt.rcParams['figure.edgecolor'] = 'white' plt.rcParams['figure.autolayout'] = False plt.rcParams['figure.constrained_layout.use'] = True ``` **Font settings:** ```python plt.rcParams['font.family'] = 'sans-serif' plt.rcParams['font.sans-serif'] = ['Arial', 'Helvetica', 'DejaVu Sans'] plt.rcParams['font.size'] = 12 plt.rcParams['font.weight'] = 'normal' ``` **Axes settings:** ```python plt.rcParams['axes.facecolor'] = 'white' plt.rcParams['axes.edgecolor'] = 'black' plt.rcParams['axes.linewidth'] = 1.5 plt.rcParams['axes.grid'] = True plt.rcParams['axes.labelsize'] = 14 plt.rcParams['axes.titlesize'] = 16 plt.rcParams['axes.labelweight'] = 'normal' plt.rcParams['axes.spines.top'] = True plt.rcParams['axes.spines.right'] = True ``` **Line settings:** ```python plt.rcParams['lines.linewidth'] = 2 plt.rcParams['lines.linestyle'] = '-' plt.rcParams['lines.marker'] = 'None' plt.rcParams['lines.markersize'] = 6 ``` **Save settings:** ```python plt.rcParams['savefig.dpi'] = 300 plt.rcParams['savefig.format'] = 'png' plt.rcParams['savefig.bbox'] = 'tight' plt.rcParams['savefig.pad_inches'] = 0.1 plt.rcParams['savefig.transparent'] = False ``` ## Color Palettes ### Named Color Sets ```python # Tableau colors tableau_colors = plt.cm.tab10.colors # CSS4 colors (subset) css_colors = ['steelblue', 'coral', 'teal', 'goldenrod', 'crimson'] # Manual definition custom_colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd'] ``` ### Color Cycles ```python # Set default color cycle from cycler import cycler colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728'] plt.rcParams['axes.prop_cycle'] = cycler(color=colors) # Or combine color and line style plt.rcParams['axes.prop_cycle'] = cycler(color=colors) + cycler(linestyle=['-', '--', ':', '-.']) ``` ### Palette Generation ```python # Evenly spaced colors from colormap n_colors = 5 colors = plt.cm.viridis(np.linspace(0, 1, n_colors)) # Use in plot for i, (x, y) in enumerate(data): ax.plot(x, y, color=colors[i]) ``` ## Typography ### Font Configuration ```python # Set font family plt.rcParams['font.family'] = 'serif' plt.rcParams['font.serif'] = ['Times New Roman', 'DejaVu Serif'] # Or sans-serif plt.rcParams['font.family'] = 'sans-serif' plt.rcParams['font.sans-serif'] = ['Arial', 'Helvetica'] # Or monospace plt.rcParams['font.family'] = 'monospace' plt.rcParams['font.monospace'] = ['Courier New', 'DejaVu Sans Mono'] ``` ### Font Properties in Text ```python from matplotlib import font_manager # Specify font properties ax.text(x, y, 'Text', fontsize=14, fontweight='bold', # 'normal', 'bold', 'heavy', 'light' fontstyle='italic', # 'normal', 'italic', 'oblique' fontfamily='serif') # Use specific font file prop = font_manager.FontProperties(fname='path/to/font.ttf') ax.text(x, y, 'Text', fontproperties=prop) ``` ### Mathematical Text ```python # LaTeX-style math ax.set_title(r'$\alpha > \beta$') ax.set_xlabel(r'$\mu \pm \sigma$') ax.text(x, y, r'$\int_0^\infty e^{-x} dx = 1$') # Subscripts and superscripts ax.set_ylabel(r'$y = x^2 + 2x + 1$') ax.text(x, y, r'$x_1, x_2, \ldots, x_n$') # Greek letters ax.text(x, y, r'$\alpha, \beta, \gamma, \delta, \epsilon$') ``` ### Using Full LaTeX ```python # Enable full LaTeX rendering (requires LaTeX installation) plt.rcParams['text.usetex'] = True plt.rcParams['text.latex.preamble'] = r'\usepackage{amsmath}' ax.set_title(r'\textbf{Bold Title}') ax.set_xlabel(r'Time $t$ (s)') ``` ## Spines and Grids ### Spine Customization ```python # Hide specific spines ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False) # Move spine position ax.spines['left'].set_position(('outward', 10)) ax.spines['bottom'].set_position(('data', 0)) # Change spine color and width ax.spines['left'].set_color('red') ax.spines['bottom'].set_linewidth(2) ``` ### Grid Customization ```python # Basic grid ax.grid(True) # Customized grid ax.grid(True, which='major', linestyle='--', linewidth=0.8, alpha=0.3) ax.grid(True, which='minor', linestyle=':', linewidth=0.5, alpha=0.2) # Grid for specific axis ax.grid(True, axis='x') # Only vertical lines ax.grid(True, axis='y') # Only horizontal lines # Grid behind or in front of data ax.set_axisbelow(True) # Grid behind data ``` ## Legend Customization ### Legend Positioning ```python # Location strings ax.legend(loc='best') # Automatic best position ax.legend(loc='upper right') ax.legend(loc='upper left') ax.legend(loc='lower right') ax.legend(loc='lower left') ax.legend(loc='center') ax.legend(loc='upper center') ax.legend(loc='lower center') ax.legend(loc='center left') ax.legend(loc='center right') # Precise positioning (bbox_to_anchor) ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left') # Outside plot area ax.legend(bbox_to_anchor=(0.5, -0.15), loc='upper center', ncol=3) # Below plot ``` ### Legend Styling ```python ax.legend( fontsize=12, frameon=True, # Show frame framealpha=0.9, # Frame transparency fancybox=True, # Rounded corners shadow=True, # Shadow effect ncol=2, # Number of columns title='Legend Title', # Legend title title_fontsize=14, # Title font size edgecolor='black', # Frame edge color facecolor='white' # Frame background color ) ``` ### Custom Legend Entries ```python from matplotlib.lines import Line2D # Create custom legend handles custom_lines = [Line2D([0], [0], color='red', lw=2), Line2D([0], [0], color='blue', lw=2, linestyle='--'), Line2D([0], [0], marker='o', color='w', markerfacecolor='green', markersize=10)] ax.legend(custom_lines, ['Label 1', 'Label 2', 'Label 3']) ``` ## Layout and Spacing ### Constrained Layout ```python # Preferred method (automatic adjustment) fig, axes = plt.subplots(2, 2, constrained_layout=True) ``` ### Tight Layout ```python # Alternative method fig, axes = plt.subplots(2, 2) plt.tight_layout(pad=1.5, h_pad=2.0, w_pad=2.0) ``` ### Manual Adjustment ```python # Fine-grained control plt.subplots_adjust(left=0.1, right=0.9, top=0.9, bottom=0.1, hspace=0.3, wspace=0.4) ``` ## Professional Publication Style Example configuration for publication-quality figures: ```python # Publication style configuration plt.rcParams.update({ # Figure 'figure.figsize': (8, 6), 'figure.dpi': 100, 'savefig.dpi': 300, 'savefig.bbox': 'tight', 'savefig.pad_inches': 0.1, # Font 'font.family': 'sans-serif', 'font.sans-serif': ['Arial', 'Helvetica'], 'font.size': 11, # Axes 'axes.labelsize': 12, 'axes.titlesize': 14, 'axes.linewidth': 1.5, 'axes.grid': False, 'axes.spines.top': False, 'axes.spines.right': False, # Lines 'lines.linewidth': 2, 'lines.markersize': 8, # Ticks 'xtick.labelsize': 10, 'ytick.labelsize': 10, 'xtick.major.size': 6, 'ytick.major.size': 6, 'xtick.major.width': 1.5, 'ytick.major.width': 1.5, 'xtick.direction': 'in', 'ytick.direction': 'in', # Legend 'legend.fontsize': 10, 'legend.frameon': True, 'legend.framealpha': 1.0, 'legend.edgecolor': 'black' }) ``` ## Dark Theme ```python # Dark background style plt.style.use('dark_background') # Or manual configuration plt.rcParams.update({ 'figure.facecolor': '#1e1e1e', 'axes.facecolor': '#1e1e1e', 'axes.edgecolor': 'white', 'axes.labelcolor': 'white', 'text.color': 'white', 'xtick.color': 'white', 'ytick.color': 'white', 'grid.color': 'gray', 'legend.facecolor': '#1e1e1e', 'legend.edgecolor': 'white' }) ``` ## Color Accessibility ### Colorblind-Friendly Palettes ```python # Use colorblind-friendly colormaps colorblind_friendly = ['viridis', 'plasma', 'cividis'] # Colorblind-friendly discrete colors cb_colors = ['#0173B2', '#DE8F05', '#029E73', '#CC78BC', '#CA9161', '#949494', '#ECE133', '#56B4E9'] # Test with simulation tools or use these validated palettes ``` ### High Contrast ```python # Ensure sufficient contrast plt.rcParams['axes.edgecolor'] = 'black' plt.rcParams['axes.linewidth'] = 2 plt.rcParams['xtick.major.width'] = 2 plt.rcParams['ytick.major.width'] = 2 ```