Export your visualizations for papers, presentations, and professional reports
Creating a beautiful plot is only half the battle - you need to save it in the right format, at the right resolution, with the right settings for your intended use. Whether it's for a scientific journal, a web article, or a high-stakes presentation, the way you export your figures can make or break their impact!
Think of this as the final polish on a masterpiece. The wrong file format or resolution can turn crisp, professional visualizations into pixelated or oversized files. Master these techniques to ensure your data stories look stunning everywhere they appear!
Calculate the optimal settings for your figure export:
Pixel Dimensions: 2400 × 1800
Estimated File Size: ~1.5 MB
Recommended Format: PNG
Essential commands for saving figures in different formats
import matplotlib.pyplot as plt
import numpy as np
# Create a sample figure
fig, ax = plt.subplots(figsize=(8, 6))
x = np.linspace(0, 10, 100)
ax.plot(x, np.sin(x), label='sin(x)')
ax.plot(x, np.cos(x), label='cos(x)')
ax.legend()
ax.set_title('Trigonometric Functions')
# Basic save - PNG format
plt.savefig('figure.png')
# Specify format explicitly
plt.savefig('figure.pdf', format='pdf')
plt.savefig('figure.svg', format='svg')
plt.savefig('figure.eps', format='eps')
plt.savefig('figure.jpg', format='jpg')
# Control quality and resolution
plt.savefig('high_quality.png', dpi=300) # 300 DPI for print
plt.savefig('web_quality.png', dpi=72) # 72 DPI for web
# JPEG with quality setting (0-100)
plt.savefig('compressed.jpg', quality=95, dpi=150)
# Save with tight bounding box (removes excess whitespace)
plt.savefig('tight.png', bbox_inches='tight')
# Save with transparent background
plt.savefig('transparent.png', transparent=True)
# Save with custom padding
plt.savefig('padded.png', bbox_inches='tight', pad_inches=0.5)
# Save specific figure (when you have multiple)
fig.savefig('specific_figure.png')
# Always good practice
plt.close() # Free up memory
Understanding when to use vector or raster formats
| Aspect | Vector (PDF/SVG/EPS) | Raster (PNG/JPG) |
|---|---|---|
| Scalability | ✅ Infinite | ❌ Fixed pixels |
| File Size | ✅ Small for simple | ❌ Large for high-res |
| Complex Images | ❌ Can be huge | ✅ Consistent size |
| Editing | ✅ Fully editable | ⚠️ Limited |
| Compatibility | ⚠️ Varies | ✅ Universal |
# Vector format advantages
# PDF - Best for publications
plt.savefig('publication.pdf',
format='pdf',
bbox_inches='tight')
# SVG - Best for web and editing
plt.savefig('web_figure.svg',
format='svg',
bbox_inches='tight')
# EPS - Legacy format for older journals
plt.savefig('legacy.eps',
format='eps',
bbox_inches='tight')
# Raster format advantages
# PNG - Lossless compression, transparency
plt.savefig('detailed_plot.png',
dpi=300,
transparent=True,
bbox_inches='tight')
# JPG - Smallest file size, no transparency
plt.savefig('photo_quality.jpg',
dpi=150,
quality=90,
bbox_inches='tight')
Master the relationship between DPI, figure size, and output quality
# Understanding DPI (Dots Per Inch)
import matplotlib.pyplot as plt
import numpy as np
# Figure size in inches × DPI = pixel dimensions
fig_width = 8 # inches
fig_height = 6 # inches
# Different DPI settings for different uses
dpi_settings = {
'screen': 72, # Web display
'print': 300, # Standard print
'poster': 150, # Large format print
'journal': 600, # High-quality publication
}
# Create figure with specific size
fig, ax = plt.subplots(figsize=(fig_width, fig_height))
# Your plot here
x = np.linspace(0, 10, 1000)
ax.plot(x, np.sin(x) * np.exp(-x/10))
# Save at different resolutions
for use, dpi in dpi_settings.items():
filename = f'figure_{use}_{dpi}dpi.png'
fig.savefig(filename, dpi=dpi, bbox_inches='tight')
# Calculate pixel dimensions
width_px = fig_width * dpi
height_px = fig_height * dpi
print(f'{use}: {width_px:.0f}×{height_px:.0f} pixels')
# Journal submission requirements example
def save_for_journal(fig, journal_name='nature'):
"""Save figure according to journal specifications"""
journal_specs = {
'nature': {
'single_column': 3.5, # inches
'double_column': 7.0, # inches
'dpi': 300,
'format': 'pdf'
},
'science': {
'single_column': 3.4,
'double_column': 6.8,
'dpi': 300,
'format': 'eps'
},
'ieee': {
'single_column': 3.5,
'double_column': 7.16,
'dpi': 600,
'format': 'pdf'
}
}
specs = journal_specs[journal_name]
# Resize figure
fig.set_size_inches(specs['single_column'],
specs['single_column'] * 0.75)
# Save with specifications
fig.savefig(f'{journal_name}_figure.{specs["format"]}',
format=specs['format'],
dpi=specs['dpi'],
bbox_inches='tight')
# Example usage
save_for_journal(fig, 'nature')
Professional techniques for perfect exports
# Advanced savefig options
import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages
import numpy as np
# 1. Multi-page PDF
with PdfPages('multipage.pdf') as pdf:
for i in range(4):
fig, ax = plt.subplots()
ax.plot(np.random.randn(100).cumsum())
ax.set_title(f'Page {i+1}')
pdf.savefig(fig, bbox_inches='tight')
plt.close(fig)
# Add metadata
d = pdf.infodict()
d['Title'] = 'Multipage Report'
d['Author'] = 'Data Science Team'
d['Subject'] = 'Quarterly Analysis'
d['Keywords'] = 'Data, Analysis, Report'
# 2. Save with specific backend
from matplotlib.backends.backend_agg import FigureCanvasAgg
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [1, 4, 9])
canvas = FigureCanvasAgg(fig)
canvas.print_png('backend_specific.png')
# 3. Save figure and data together
fig, ax = plt.subplots()
x = np.linspace(0, 10, 100)
y = np.sin(x)
ax.plot(x, y)
# Save figure
fig.savefig('figure_with_data.png')
# Save data
import pickle
with open('figure_data.pkl', 'wb') as f:
pickle.dump({'figure': fig, 'x': x, 'y': y}, f)
# 4. Custom metadata in PNG
from PIL import Image
from PIL.PngImagePlugin import PngInfo
# Save figure first
fig.savefig('temp.png', dpi=300)
# Add metadata
img = Image.open('temp.png')
metadata = PngInfo()
metadata.add_text('Author', 'Your Name')
metadata.add_text('Title', 'Figure Title')
metadata.add_text('Description', 'Detailed description')
metadata.add_text('Copyright', '2024 Your Organization')
img.save('figure_with_metadata.png', pnginfo=metadata)
# 5. Save for dark/light mode
def save_both_themes(fig, filename_base):
"""Save figure in both light and dark themes"""
# Light theme
with plt.style.context('seaborn-v0_8-whitegrid'):
fig.savefig(f'{filename_base}_light.png',
facecolor='white',
edgecolor='none')
# Dark theme
with plt.style.context('dark_background'):
fig.patch.set_facecolor('#1a1a1a')
fig.savefig(f'{filename_base}_dark.png',
facecolor='#1a1a1a',
edgecolor='none')
# 6. Batch export in multiple formats
def export_all_formats(fig, basename, dpi=300):
"""Export figure in all common formats"""
formats = {
'png': {'dpi': dpi, 'transparent': True},
'pdf': {'bbox_inches': 'tight'},
'svg': {'bbox_inches': 'tight'},
'jpg': {'dpi': dpi//2, 'quality': 95}
}
for fmt, kwargs in formats.items():
filename = f'{basename}.{fmt}'
fig.savefig(filename, format=fmt, **kwargs)
print(f'Saved: {filename}')
Precise control over figure dimensions and spacing
# Controlling figure size and layout for export
import matplotlib.pyplot as plt
import numpy as np
# 1. Set figure size for specific publication width
def set_size(width, fraction=1, subplots=(1, 1)):
"""Set figure dimensions to avoid scaling in LaTeX.
Parameters:
width: float - Document width in points
fraction: float - Fraction of the width for figure
subplots: tuple - Number of rows and columns
"""
# Width of figure (in pts)
fig_width_pt = width * fraction
# Convert from pt to inches
inches_per_pt = 1 / 72.27
# Golden ratio
golden_ratio = (5**.5 - 1) / 2
# Figure width in inches
fig_width_in = fig_width_pt * inches_per_pt
# Figure height in inches
fig_height_in = fig_width_in * golden_ratio * (subplots[0] / subplots[1])
return (fig_width_in, fig_height_in)
# LaTeX column width (get from \the\columnwidth)
width_pts = 469.75 # Example for two-column article
fig_size = set_size(width_pts)
fig, ax = plt.subplots(figsize=fig_size)
# 2. Tight layout with padding control
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
# Automatic spacing
fig.tight_layout(pad=3.0, # Padding around figure
w_pad=2.0, # Width padding between subplots
h_pad=2.0) # Height padding between subplots
# 3. Manual spacing control
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
plt.subplots_adjust(left=0.1, # Left margin
right=0.95, # Right margin
top=0.95, # Top margin
bottom=0.1, # Bottom margin
wspace=0.2, # Width spacing
hspace=0.25) # Height spacing
# 4. Constrained layout (newer, better)
fig, axes = plt.subplots(2, 2, figsize=(10, 8),
constrained_layout=True)
# Set padding in inches
fig.set_constrained_layout_pads(w_pad=0.1, h_pad=0.1)
# 5. Remove all margins for seamless integration
fig, ax = plt.subplots(figsize=(8, 6))
ax.plot(np.random.randn(100))
# Remove all white space
ax.set_position([0, 0, 1, 1])
ax.axis('off')
fig.savefig('no_margins.png', bbox_inches='tight', pad_inches=0)
# 6. Save with exact pixel dimensions
def save_exact_size(fig, filename, width_px, height_px):
"""Save figure with exact pixel dimensions"""
# Calculate required DPI
fig_width_in, fig_height_in = fig.get_size_inches()
dpi_w = width_px / fig_width_in
dpi_h = height_px / fig_height_in
dpi = max(dpi_w, dpi_h)
# Adjust figure size to match exactly
fig.set_size_inches(width_px/dpi, height_px/dpi)
# Save with calculated DPI
fig.savefig(filename, dpi=dpi, bbox_inches='tight',
pad_inches=0)
print(f'Saved {filename}: {width_px}×{height_px}px at {dpi:.1f} DPI')
# Example: Save for specific pixel dimensions
save_exact_size(fig, 'exact_1920x1080.png', 1920, 1080)
Optimize file size and rendering performance
# Optimization techniques for different scenarios
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Rectangle
import matplotlib.patches as mpatches
# 1. Rasterization for complex vector elements
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))
# Complex scatter plot (many points)
x = np.random.randn(10000)
y = np.random.randn(10000)
# Without rasterization (huge PDF)
ax1.scatter(x, y, alpha=0.5, s=1)
ax1.set_title('Vector (Large File)')
# With rasterization (small PDF)
ax2.scatter(x, y, alpha=0.5, s=1, rasterized=True)
ax2.set_title('Rasterized (Small File)')
# Save with mixed vector/raster
fig.savefig('optimized.pdf', dpi=150)
# 2. Simplify paths for web
from matplotlib.path import Path
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x = np.linspace(0, 10, 10000) # Many points
y = np.sin(x) + np.random.randn(10000) * 0.1
# Simplify the path
line, = ax.plot(x, y)
line.set_path_effects([path_effects.SimpleLineShadow(),
path_effects.Normal()])
line.set_simplify(True)
line.set_simplify_threshold(1.0) # Adjust threshold
# 3. Reduce file size for web
def optimize_for_web(fig, filename):
"""Optimize figure for web display"""
# Set reasonable DPI (screens are typically 72-96 DPI)
web_dpi = 96
# Use tight layout to remove whitespace
fig.tight_layout()
# Save as PNG with optimization
fig.savefig(filename,
format='png',
dpi=web_dpi,
bbox_inches='tight',
pad_inches=0.1,
facecolor='white',
edgecolor='none',
optimize=True) # PNG optimization
# Alternative: Save as compressed JPG
jpg_name = filename.replace('.png', '.jpg')
fig.savefig(jpg_name,
format='jpg',
dpi=web_dpi,
bbox_inches='tight',
quality=85, # Balance quality/size
optimize=True)
# 4. Batch processing for consistency
def batch_process_figures(figures, prefix='fig',
dpi=300, format='png'):
"""Process multiple figures with consistent settings"""
for i, fig in enumerate(figures):
# Apply consistent style
fig.tight_layout()
# Generate filename
filename = f'{prefix}_{i+1:03d}.{format}'
# Save with consistent settings
fig.savefig(filename,
format=format,
dpi=dpi,
bbox_inches='tight',
facecolor='white',
edgecolor='none')
print(f'Saved: {filename}')
# Close to free memory
plt.close(fig)
# 5. Memory management for large exports
def save_large_figure(create_figure_func, filename,
chunk_size=1000):
"""Save large figure with memory management"""
import gc
# Create figure
fig = create_figure_func()
# Force garbage collection before save
gc.collect()
# Save with memory-efficient backend
from matplotlib.backends.backend_agg import FigureCanvasAgg
canvas = FigureCanvasAgg(fig)
# Render and save
canvas.print_png(filename)
# Clean up
plt.close(fig)
del fig
gc.collect()
print(f'Saved large figure: {filename}')
Professional workflow for preparing figures for publication:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from pathlib import Path
import json
class PublicationFigure:
"""Class for managing publication-quality figures"""
def __init__(self, fig, metadata=None):
self.fig = fig
self.metadata = metadata or {}
self.export_dir = Path('exports')
self.export_dir.mkdir(exist_ok=True)
def set_journal_style(self, journal='nature'):
"""Apply journal-specific styling"""
journal_styles = {
'nature': {
'font.size': 7,
'axes.labelsize': 7,
'axes.titlesize': 7,
'xtick.labelsize': 6,
'ytick.labelsize': 6,
'legend.fontsize': 6,
'font.family': 'sans-serif',
'font.sans-serif': ['Arial', 'Helvetica']
},
'science': {
'font.size': 8,
'axes.labelsize': 8,
'axes.titlesize': 8,
'xtick.labelsize': 7,
'ytick.labelsize': 7,
'legend.fontsize': 7,
'font.family': 'sans-serif',
'font.sans-serif': ['Helvetica', 'Arial']
},
'ieee': {
'font.size': 8,
'axes.labelsize': 8,
'axes.titlesize': 9,
'xtick.labelsize': 8,
'ytick.labelsize': 8,
'legend.fontsize': 8,
'font.family': 'serif',
'font.serif': ['Times', 'Times New Roman']
}
}
if journal in journal_styles:
for key, value in journal_styles[journal].items():
plt.rcParams[key] = value
def check_requirements(self, journal='nature'):
"""Check if figure meets journal requirements"""
requirements = {
'nature': {
'max_width_single': 89, # mm
'max_width_double': 183, # mm
'max_height': 247, # mm
'min_dpi': 300,
'formats': ['pdf', 'eps', 'tiff'],
'color_mode': 'RGB'
}
}
req = requirements.get(journal, {})
width_inch, height_inch = self.fig.get_size_inches()
width_mm = width_inch * 25.4
height_mm = height_inch * 25.4
checks = {
'width_ok': width_mm <= req.get('max_width_double', float('inf')),
'height_ok': height_mm <= req.get('max_height', float('inf')),
'size_mm': (width_mm, height_mm)
}
return checks
def export_all_formats(self, base_name, formats=None, dpi=300):
"""Export in multiple formats for different uses"""
if formats is None:
formats = ['pdf', 'svg', 'png', 'jpg']
exports = {}
for fmt in formats:
filename = self.export_dir / f'{base_name}.{fmt}'
if fmt in ['pdf', 'svg', 'eps']:
# Vector formats
self.fig.savefig(filename,
format=fmt,
bbox_inches='tight')
elif fmt == 'png':
# PNG with transparency
self.fig.savefig(filename,
format=fmt,
dpi=dpi,
bbox_inches='tight',
transparent=True)
elif fmt == 'jpg':
# JPEG for presentations
self.fig.savefig(filename,
format=fmt,
dpi=dpi//2, # Lower DPI for JPG
bbox_inches='tight',
quality=95)
elif fmt == 'tiff':
# TIFF for some journals
self.fig.savefig(filename,
format=fmt,
dpi=dpi,
bbox_inches='tight',
compression='lzw')
exports[fmt] = filename
file_size = filename.stat().st_size / 1024 # KB
print(f'Exported {fmt}: {filename.name} ({file_size:.1f} KB)')
# Save metadata
metadata_file = self.export_dir / f'{base_name}_metadata.json'
with open(metadata_file, 'w') as f:
json.dump({
'base_name': base_name,
'formats': list(exports.keys()),
'dpi': dpi,
'size_inches': self.fig.get_size_inches(),
'metadata': self.metadata
}, f, indent=2)
return exports
def create_submission_package(self, journal='nature'):
"""Create complete submission package"""
base_name = self.metadata.get('figure_number', 'figure')
# Check requirements
checks = self.check_requirements(journal)
print(f'Size check: {checks["size_mm"][0]:.1f}×{checks["size_mm"][1]:.1f} mm')
print(f'Width OK: {checks["width_ok"]}, Height OK: {checks["height_ok"]}')
# Export in required formats
if journal == 'nature':
formats = ['pdf', 'eps', 'png']
dpi = 600
elif journal == 'science':
formats = ['pdf', 'tiff']
dpi = 600
else:
formats = ['pdf', 'png']
dpi = 300
exports = self.export_all_formats(base_name, formats, dpi)
# Create README
readme_path = self.export_dir / f'{base_name}_README.txt'
with open(readme_path, 'w') as f:
f.write(f'Figure: {base_name}\n')
f.write(f'Journal: {journal}\n')
f.write(f'Formats: {", ".join(formats)}\n')
f.write(f'DPI: {dpi}\n')
f.write(f'Size: {checks["size_mm"][0]:.1f}×{checks["size_mm"][1]:.1f} mm\n')
if self.metadata:
f.write('\nMetadata:\n')
for key, value in self.metadata.items():
f.write(f' {key}: {value}\n')
print(f'\nSubmission package created in: {self.export_dir}')
return exports
# Example usage
def create_publication_figure():
"""Create a publication-ready figure"""
# Generate sample data
np.random.seed(42)
x = np.linspace(0, 10, 100)
y1 = np.sin(x) * np.exp(-x/10)
y2 = np.cos(x) * np.exp(-x/10)
# Create figure with specific size (Nature single column)
width_mm = 89 # Single column width
width_inch = width_mm / 25.4
height_inch = width_inch * 0.75 # Aspect ratio
fig, (ax1, ax2) = plt.subplots(2, 1,
figsize=(width_inch, height_inch),
sharex=True)
# Top panel
ax1.plot(x, y1, 'b-', linewidth=1, label='Signal A')
ax1.plot(x, y2, 'r--', linewidth=1, label='Signal B')
ax1.set_ylabel('Amplitude (a.u.)')
ax1.legend(loc='upper right', frameon=False)
ax1.set_title('Figure 1: Experimental Results', fontweight='bold', pad=10)
# Bottom panel
ax2.fill_between(x, y1, y2, alpha=0.3, color='gray')
ax2.set_xlabel('Time (s)')
ax2.set_ylabel('Difference (a.u.)')
# Remove top and right spines (Nature style)
for ax in [ax1, ax2]:
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# Tight layout
fig.tight_layout()
# Create publication figure object
metadata = {
'figure_number': 'Fig1',
'title': 'Experimental Results',
'author': 'Research Team',
'date': '2024-01-01',
'description': 'Time-series analysis of experimental signals'
}
pub_fig = PublicationFigure(fig, metadata)
# Apply journal style
pub_fig.set_journal_style('nature')
# Create submission package
pub_fig.create_submission_package('nature')
return pub_fig
# Run the complete pipeline
pub_fig = create_publication_figure()
# Additional specialized exports
fig = pub_fig.fig
# For presentations (16:9 aspect ratio)
fig_presentation = plt.figure(figsize=(16, 9))
# Copy content to presentation figure...
fig_presentation.savefig('presentation_slide.png', dpi=150)
# For social media (square format)
fig_social = plt.figure(figsize=(6, 6))
# Copy/adapt content for social...
fig_social.savefig('social_media.jpg', dpi=150, quality=90)
# For print poster (high resolution)
fig_poster = plt.figure(figsize=(24, 36)) # inches
# Scale up content for poster...
fig_poster.savefig('poster.pdf', format='pdf')
plt.show()