Skip to main content

Record and reproduce matplotlib figures with YAML recipes

Project description

figrecipe

Record and reproduce matplotlib figures.

PyPI version License: MIT

figrecipe 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 of plt.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: Style presets (SCIENTIFIC, MINIMAL, PRESENTATION) and colorblind-friendly palettes
  • Seaborn support: Record and reproduce seaborn plots

Installation

pip install figrecipe

Quick Start

Recording a Figure

import figrecipe 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 figrecipe 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 figrecipe as ps
import numpy as np

# List available style presets
print(ps.list_presets())  # ['MINIMAL', 'PRESENTATION', 'SCIENTIFIC']

# Load a style preset (SCIENTIFIC is default)
style = ps.load_style()  # or ps.load_style("SCIENTIFIC")

# 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 colorblind-friendly colors
x = np.linspace(0, 2*np.pi, 100)
ax.plot(x, np.sin(x), label='sin(x)')  # Auto: Blue (#0072B2)
ax.plot(x, np.cos(x), label='cos(x)')  # Auto: Orange (#D55E00)

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 figrecipe 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:

figrecipe: "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 figrecipe 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

figrecipe includes a preset-based style system with easy YAML customization:

import figrecipe as ps

# List available presets
print(ps.list_presets())  # ['MINIMAL', 'PRESENTATION', 'SCIENTIFIC']

# Load a preset (SCIENTIFIC is default)
style = ps.load_style()             # Default: SCIENTIFIC
style = ps.load_style("MINIMAL")    # Minimal, clean design
style = ps.load_style("PRESENTATION")  # Large fonts for slides

# Load custom YAML style
style = ps.load_style("/path/to/my_style.yaml")

# Inspect style values
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())

Colorblind-Friendly Palette

The SCIENTIFIC preset uses Wong (2011) colorblind-safe colors:

  • Blue: #0072B2
  • Orange: #D55E00
  • Green: #009E73
  • Purple: #CC79A7
  • Yellow: #F0E442
  • Cyan: #56B4E9

Why figrecipe?

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 figure
  • ps.save(fig, path) - Save figure recipe to YAML
  • ps.reproduce(path, calls=None) - Reproduce figure from recipe
  • ps.info(path) - Get recipe information
  • ps.load(path) - Load recipe as FigureRecord object

Style Functions

  • ps.list_presets() - List available style presets
  • ps.load_style(style=None) - Load style from preset name or YAML path
  • ps.apply_style(ax, style=None) - Apply styling to axes
  • ps.STYLE - Global style proxy object

Unit Conversions

  • ps.mm_to_inch(mm) - Convert mm to inches
  • ps.mm_to_pt(mm) - Convert mm to points
  • ps.inch_to_mm(inch) - Convert inches to mm
  • ps.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

figrecipe-0.4.0.tar.gz (3.2 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

figrecipe-0.4.0-py3-none-any.whl (60.5 kB view details)

Uploaded Python 3

File details

Details for the file figrecipe-0.4.0.tar.gz.

File metadata

  • Download URL: figrecipe-0.4.0.tar.gz
  • Upload date:
  • Size: 3.2 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for figrecipe-0.4.0.tar.gz
Algorithm Hash digest
SHA256 22c16798f147fc6949fa4c82fa6d957574a479cc4b9753fdab73929e3cf9a504
MD5 2fa7528cd320a850d4f310773c75e863
BLAKE2b-256 a8566daf8d481f4e53bb9b5ac821661b1309d2864548b11fa274d8fd10b9de08

See more details on using hashes here.

File details

Details for the file figrecipe-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: figrecipe-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 60.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for figrecipe-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9e7c174fb652f355204815acce8c290e3c8b8447e4e6d3a109587a44d14a918d
MD5 99b66c2a598d81f1956fcc0a4816c92c
BLAKE2b-256 b726b3f35daaf8dd95b97775a80d0a78e733ca52fde1ce8e251c9e2c195b88d5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page