Record and reproduce matplotlib figures
Project description
plotspec
Record and reproduce matplotlib figures.
plotspec captures matplotlib plotting calls and saves them as human-readable YAML "recipes" that can be used to reproduce figures exactly.
Example Output
| Original | Reproduced from Recipe |
Multi-panel figures with publication-quality styling:
Features
- Drop-in replacement: Use
ps.subplots()instead ofplt.subplots() - Automatic tracking: All plotting calls are recorded automatically
- YAML recipes: Human-readable format for figure specifications
- Efficient storage: Large arrays saved to separate CSV/NPZ files
- One-line reproduction:
fig, ax = ps.reproduce("figure.yaml") - Selective replay: Reproduce only specific plotting calls
- MM-based layout: Precise control with millimeter-based sizing
- Publication styling: SCITEX-inspired color palette and styling
- Seaborn support: Record and reproduce seaborn plots
Installation
pip install plotspec
Quick Start
Recording a Figure
import plotspec as ps
import numpy as np
# Create data
x = np.linspace(0, 10, 100)
y = np.sin(x)
# Create figure (drop-in replacement for plt.subplots)
fig, ax = ps.subplots()
# Plot as usual - calls are recorded automatically
ax.plot(x, y, color='red', linewidth=2, id='sine_wave')
ax.scatter(x[::10], y[::10], s=50, color='blue', id='peaks')
ax.set_xlabel('Time (s)')
ax.set_ylabel('Amplitude')
ax.set_title('Sine Wave Example')
ax.legend()
# Save the recipe
ps.save(fig, 'sine_wave.yaml')
Reproducing a Figure
import plotspec as ps
import matplotlib.pyplot as plt
# Reproduce the entire figure
fig, ax = ps.reproduce('sine_wave.yaml')
plt.show()
# Or reproduce only specific calls
fig, ax = ps.reproduce('sine_wave.yaml', calls=['sine_wave'])
plt.show()
Publication-Quality Figures with MM Layout
import plotspec as ps
import numpy as np
# Load SCITEX-style configuration
style = ps.load_style()
# Create figure with precise mm-based dimensions
fig, ax = ps.subplots(
axes_width_mm=60, # 60mm wide axes
axes_height_mm=40, # 40mm tall axes
margin_left_mm=15, # Space for y-axis labels
margin_bottom_mm=12, # Space for x-axis labels
apply_style_mm=True, # Apply publication style
)
# Plot with auto-cycling SCITEX colors
x = np.linspace(0, 2*np.pi, 100)
ax.plot(x, np.sin(x), label='sin(x)') # Auto: SCITEX blue
ax.plot(x, np.cos(x), label='cos(x)') # Auto: SCITEX orange
ax.set_xlabel('x (radians)')
ax.set_ylabel('y')
ax.legend()
# Save with transparent background
fig.fig.savefig('publication_figure.png', dpi=300, transparent=True)
ps.save(fig, 'publication_figure.yaml')
Inspecting a Recipe
import plotspec as ps
# Get recipe information without reproducing
info = ps.info('sine_wave.yaml')
print(f"Created: {info['created']}")
print(f"Figure size: {info['figsize']}")
print(f"Number of calls: {len(info['calls'])}")
for call in info['calls']:
print(f" - {call['id']}: {call['function']}()")
Recipe Format
Recipes are saved as YAML files:
plotspec: "1.0"
id: fig_a1b2c3d4
created: "2025-12-21T14:30:00"
matplotlib_version: "3.8.0"
figure:
figsize: [10, 6]
dpi: 100
axes:
ax_0_0:
calls:
- id: sine_wave
function: plot
args:
- name: x
data: sine_wave_data/sine_wave_x.csv
- name: y
data: sine_wave_data/sine_wave_y.csv
kwargs:
color: "#0072B2"
linewidth: 2
decorations:
- id: set_xlabel_000
function: set_xlabel
args:
- name: arg0
data: "Time (s)"
kwargs: {}
Advanced Usage
Custom Call IDs
Use the id parameter to give meaningful names to your plots:
ax.plot(x, y, color='red', id='experimental_data')
ax.plot(x, y_fit, color='blue', id='fitted_curve')
Multiple Subplots
fig, axes = ps.subplots(2, 2, figsize=(12, 10))
axes[0][0].plot(x, y1, id='top_left')
axes[0][1].scatter(x, y2, id='top_right')
axes[1][0].bar(categories, values, id='bottom_left')
axes[1][1].hist(data, id='bottom_right')
ps.save(fig, 'multi_panel.yaml')
Seaborn Integration
import plotspec as ps
import pandas as pd
df = pd.DataFrame({'x': x, 'y': y, 'category': categories})
fig, ax = ps.subplots()
ps.sns.scatterplot(data=df, x='x', y='y', hue='category', ax=ax, id='scatter')
ps.save(fig, 'seaborn_figure.yaml')
Temporarily Disable Recording
fig, ax = ps.subplots()
# Recorded
ax.plot(x, y, id='main_data')
# Not recorded
with ax.no_record():
ax.axhline(0, color='gray', linestyle='--')
# Recorded again
ax.scatter(x_points, y_points, id='highlights')
Style Configuration
plotspec includes a configurable style system via PLOTSPEC_STYLE.yaml:
import plotspec as ps
# Load and inspect style
style = ps.load_style()
print(f"Axes width: {style.axes.width_mm} mm")
print(f"Font: {style.fonts.family}")
print(f"Colors: {style.colors.palette[:4]}")
# Use style parameters directly
fig, ax = ps.subplots(**style.to_subplots_kwargs())
SCITEX Color Palette
Colorblind-friendly scientific colors:
- Blue:
#0072B2 - Orange:
#D55E00 - Green:
#009E73 - Purple:
#CC79A7 - Yellow:
#F0E442 - Cyan:
#56B4E9
Why plotspec?
Scientific Reproducibility
Share the exact specification of your figures alongside your papers. Reviewers and readers can reproduce your figures exactly.
Version Control Friendly
YAML recipes are human-readable and diff-friendly, making it easy to track changes to figures in git.
Data Separation
Large arrays are automatically saved to efficient CSV or NPZ files, keeping recipes small and readable while preserving full data fidelity.
Collaboration
Share figure "recipes" with collaborators who can modify and re-run them with their own data or styling preferences.
API Reference
Main Functions
ps.subplots(nrows=1, ncols=1, **kwargs)- Create recording-enabled figureps.save(fig, path)- Save figure recipe to YAMLps.reproduce(path, calls=None)- Reproduce figure from recipeps.info(path)- Get recipe informationps.load(path)- Load recipe as FigureRecord object
Style Functions
ps.load_style(path=None)- Load style configurationps.apply_style(ax, style=None)- Apply styling to axesps.STYLE- Global style proxy object
Unit Conversions
ps.mm_to_inch(mm)- Convert mm to inchesps.mm_to_pt(mm)- Convert mm to pointsps.inch_to_mm(inch)- Convert inches to mmps.pt_to_mm(pt)- Convert points to mm
License
MIT License - see LICENSE for details.
Contributing
Contributions are welcome! Please open an issue or submit a pull request.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file plotspec-0.3.3.tar.gz.
File metadata
- Download URL: plotspec-0.3.3.tar.gz
- Upload date:
- Size: 43.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
15432461a38174c4edd91756fbc4bc359a95137058a7cb17c9729210961f6b05
|
|
| MD5 |
38bbd9f43ec236f678cb986de2aa809a
|
|
| BLAKE2b-256 |
4ef84f68ad75aafaa86d58492011fe244148a063156cb717255fe3045d3350f4
|
File details
Details for the file plotspec-0.3.3-py3-none-any.whl.
File metadata
- Download URL: plotspec-0.3.3-py3-none-any.whl
- Upload date:
- Size: 38.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6799df41943eb3c414e2fee64b5d27c984a493f3398d7868c19e4cede7d46570
|
|
| MD5 |
f035f670d21d30fe5485b1dcb0391b16
|
|
| BLAKE2b-256 |
1da82d08c50447a6c4895d3893a388387326d8ae0512e62e49c212351acd11d7
|