Skip to main content

Publication-ready scientific plotting with Matplotlib

Project description

pubfig

pubfig logo

Python 3.10+ Matplotlib 3.8+ License GitHub Stars

Language: English | 中文

Publication-ready scientific plotting with Matplotlib.

Highlights

  • Publication-Style Defaults — Compact titles, cleaner legends, explicit font handling, and line weights that read more like finished paper figures.
  • One Library for Common Figure Types — Statistical plots, distribution plots, dimensionality-reduction plots, evaluation curves, heatmaps, and flow plots in one API surface.
  • Export Specs Without Boilerplatesave_figure(...) directly handles single/double column widths, explicit output suffixes, raster DPI, and trimming.
  • Matplotlib-Native Workflow — Plot functions return Matplotlib Figure objects, so existing analysis scripts remain easy to integrate.
  • Explicit Layout Controls — Fine-grained control over tick direction, box/grid visibility, palettes, legends, and plot-specific layout options.

Recent News

  • 2026-03-25: Panel-first Figma loop polish — panel export now defaults to title-free assets for cleaner Figma assembly, pubfig-sync now keeps shared title / legend placeholders off by default, and bridge/watch flows now surface bundle provenance plus the exact manual-fallback bundle path.
  • 2026-03-20: Local bridge automation for Figma sync — added a bridge-backed pubfig figma bridge|sync|watch workflow, upgraded pubfig-sync with bridge connection mode, and enabled CLI-triggered vector import/refresh after one-time plugin connection.
  • 2026-03-20: Figma plugin v2 workflow polish — added auto / hero_top relayout presets, upgraded shared title / legend placeholders, and improved refresh behavior so manual Figma positioning is preserved more reliably unless relayout is requested.
View older changelog
  • 2026-03-20: CLI + Figma plugin workflow — added pubfig figma package|validate|inspect, introduced a single-file Figma bundle JSON format for exported panels, and scaffolded the figma-plugin/pubfig-sync plugin for node-level import and refresh.
  • 2026-03-20: Figma-first panel export workflow — added export_panel(...) and export_panels(...) for stable subplot asset export, introduced a minimal panel-index.json sync index, and documented the Codex + Figma MCP refinement path for multi-panel figures.
  • 2026-03-20: README alignment with pubtab style and homepage refresh — reorganized the README into a pubtab-style homepage with centered badges, language switch, highlights, dated recent news, showcase examples, and an embedded gallery hero.
  • 2026-03-20: Default full install and metadata simplification — changed pip install pubfig to install the full plotting stack by default, removed user-facing extras from the main install path, and aligned package metadata, GitHub About, and README wording.
  • 2026-03-19: Raincloud plot support and gallery refresh — added raincloud(...), tuned its default styling, integrated it into the gallery, and regenerated the exported figure set.
  • 2026-03-19: PCA biplot and radar default updates — expanded pca_biplot(...) with loading panel modes and group ellipses, refreshed radar defaults, unified font handling, and re-exported the gallery.

Examples

Showcase

Single-plot examples

Bar scatter example Raincloud example Line example

Radar example Scatter example Heatmap example

Composite figure examples assembled in Figma

Benchmark composite figure assembled in Figma

Intervention composite figure assembled in Figma

Stratification composite figure assembled in Figma

Full Gallery

Full gallery contact sheet

Quick Start

pip install pubfig

Python Quick Start

Start with the fewest possible parameters first:

import numpy as np
import pubfig as pf

rng = np.random.default_rng(0)
means = np.array([
    [0.78, 0.96],
    [0.88, 1.08],
    [0.84, 1.00],
], dtype=float)

data = rng.normal(loc=means[..., None], scale=0.08, size=(3, 2, 18))
data = np.clip(data, 0.0, None)

fig = pf.bar_scatter(data)
pf.save_figure(fig, "figure1.pdf")

This is enough to get your first figure out. You do not need to understand layout, export, or publication-specific parameters before the first run.

Most common next parameters

Once the minimal example works, these are usually the first parameters worth adding:

fig = pf.bar_scatter(
    data,
    category_names=["Condition A", "Condition B", "Condition C"],
    series_names=["Ctrl", "Treatment"],
    title="Bar + Scatter",
)

pf.save_figure(fig, "figure1.pdf", spec="nature", width="single")
  • category_names: names on the x-axis
  • series_names: names in the legend
  • title: figure title
  • spec / width: journal-style export presets

Only add parameters like aspect_ratio or trim when you already know why you need them.

Where to look up detailed parameters

If you want to understand a specific plot in more detail, start here:

help(pf.bar_scatter)
help(pf.line)
help(pf.heatmap)

You can also inspect runnable examples under examples/.

Saving PNG / SVG / PDF

save_figure(...) now expects an explicit filename suffix:

  • pf.save_figure(fig, "figure1.pdf") → write PDF
  • pf.save_figure(fig, "figure1.svg") → write SVG
  • pf.save_figure(fig, "figure1.png") → write PNG
  • pf.save_figure(fig, "figure1.jpg") → write JPG

If you want multiple outputs, use batch_export(...) instead:

pf.batch_export(fig, "figure1", formats=("pdf", "svg", "png", "jpg"))

Plot recipes by family

These rows are the shortest useful plotting calls. When you want to export one, reuse pf.save_figure(fig, "name.pdf") from Quick Start.

Each row assumes:

import numpy as np
import pubfig as pf
Categorical and statistical
Plot Minimal call Common next parameters
bar pf.bar(np.array([3, 5, 4]), category_names=["A", "B", "C"]) category_names, title, color_palette
bar_scatter pf.bar_scatter(np.clip(np.random.default_rng(0).normal(loc=np.array([[0.78, 0.96], [0.88, 1.08], [0.84, 1.00]])[..., None], scale=0.08, size=(3, 2, 18)), 0.0, None)) category_names, series_names, show_statistics
stacked_bar pf.stacked_bar(np.array([[[3, 2], [4, 1]], [[2, 3], [3, 2]]], dtype=float), group_names=["Batch 1", "Batch 2"]) group_names, normalize, title
paired pf.paired(np.array([1.0, 2.0, 2.5, 3.0]), np.array([1.3, 2.1, 2.9, 3.2])) x_labels, y_label, title
Distribution
Plot Minimal call Common next parameters
box pf.box(np.random.default_rng(0).normal(size=(80, 3)), category_names=["A", "B", "C"]) category_names, show_means, title
violin pf.violin(np.random.default_rng(0).normal(size=(80, 3)), category_names=["A", "B", "C"]) category_names, show_box, show_points
strip pf.strip(np.random.default_rng(0).normal(size=(80, 3)), category_names=["A", "B", "C"]) category_names, jitter, title
raincloud pf.raincloud(np.random.default_rng(0).normal(size=(80, 3)), category_names=["A", "B", "C"]) category_names, orientation, title
density pf.density(np.random.default_rng(0).normal(size=400)) title, color_palette, bins
histogram pf.histogram(np.random.default_rng(0).normal(size=400), show_kde=True) bins, show_kde, title
ridgeline pf.ridgeline([np.random.default_rng(0).normal(loc=i, size=200) for i in range(4)], category_names=["S1", "S2", "S3", "S4"]) category_names, offset_step, title
Trend and relationship
Plot Minimal call Common next parameters
area pf.area(np.random.default_rng(0).random((20, 3)), series_names=["A", "B", "C"]) series_names, x, title
line pf.line(np.column_stack([0.72 + 0.05 * np.linspace(0, 10, 16) + 0.10 * np.sin(np.linspace(0, 10, 16) / 1.7), 0.88 + 0.035 * np.linspace(0, 10, 16) + 0.08 * np.cos(np.linspace(0, 10, 16) / 2.0 + 0.4)]), x=np.linspace(0, 10, 16), series_names=["Series 1", "Series 2"]) x_label, y_label, series_names, title
scatter pf.scatter(np.random.default_rng(0).normal(size=60), np.random.default_rng(1).normal(size=60)) labels, x_label, y_label
bubble pf.bubble(np.random.default_rng(0).normal(size=30), np.random.default_rng(1).normal(size=30), np.random.default_rng(2).uniform(1, 10, size=30)) labels, size_label, title
contour2d pf.contour2d(np.random.default_rng(0).normal(size=500), np.random.default_rng(1).normal(size=500)) bins, colorscale, title
radar pf.radar([[0.8, 0.7, 0.9, 0.75], [0.65, 0.85, 0.7, 0.8]], categories=["Speed", "Accuracy", "Recall", "Stability"], series_names=["Model A", "Model B"]) categories, series_names, title
Matrix and multivariate
Plot Minimal call Common next parameters
heatmap pf.heatmap(np.random.default_rng(0).uniform(size=(4, 4))) category_names, title, color scale related options
corr_matrix pf.corr_matrix(np.random.default_rng(0).normal(size=(60, 4)), variable_names=["A", "B", "C", "D"]) variable_names, method, title
clustermap pf.clustermap(np.random.default_rng(0).uniform(size=(8, 6))) row_category_names, column_category_names, title
dimreduce fig, _ = pf.dimreduce(np.random.default_rng(0).normal(size=(40, 8)), cluster_id=np.repeat([0, 1], 20), perplexity=10) cluster_id, labels, n_components
pca_biplot pf.pca_biplot(np.random.default_rng(0).normal(size=(40, 5)), labels=np.repeat(["A", "B"], 20), variable_names=["V1", "V2", "V3", "V4", "V5"]) labels, variable_names, loading_panel
parallel_coordinates pf.parallel_coordinates(np.random.default_rng(0).uniform(size=(20, 4)), variable_names=["W", "X", "Y", "Z"]) variable_names, color_col, title
Evaluation and flow
Plot Minimal call Common next parameters
roc pf.roc([np.array([0.0, 0.1, 0.3, 1.0])], [np.array([0.0, 0.7, 0.9, 1.0])], series_names=["Model A"]) series_names, baseline, title
pr_curve pf.pr_curve([np.array([1.0, 0.9, 0.8, 0.6])], [np.array([0.1, 0.4, 0.7, 1.0])], series_names=["Model A"]) series_names, title, xlim / ylim
sankey pf.sankey([0, 0, 1], [2, 3, 3], [10, 5, 8], node_names=["Input A", "Input B", "Path 1", "Outcome"]) node_names, title, color_palette

For bar_scatter(...), significance spacing parameters now follow explicit orientation-based names:

fig = pf.bar_scatter(
    data,
    show_statistics=True,
    significance_ns_label_offset_ratio_vertical=0.08,
    significance_stars_label_offset_ratio_vertical=-0.12,
    significance_label_offset_ratio_vertical=0.07,
)

pubfig → Figma

What this gives you

pubfig exports clean panel artwork, and Figma stays the place where you assemble and finish the whole publication figure.

For day-to-day use, the main command is pubfig figma push.

Quick Start

  1. Install pubfig-sync in Figma Desktop the first time: go to Plugins → Development → Import plugin from manifest..., then select figma-plugin/pubfig-sync/manifest.json from this repo. After that, reopen it from Plugins → Development → pubfig-sync.
  2. Click Connect Bridge once in the plugin.
  3. Export your panels from Python.
  4. Run pubfig figma push <panel_dir> --figure-id <id> from the terminal.
  5. If the bridge path fails, load the written bundle in the plugin and use the manual buttons.
pubfig figma push panels --figure-id figure-01

Minimal example

Panel export now defaults to clean, title-free art so subplot titles can be handled at the Figma assembly layer. If you explicitly want embedded panel headers, pass include_title=True.

import numpy as np
import pubfig as pf

rng = np.random.default_rng(0)

panels = {
    "a": pf.bar(rng.uniform(0.4, 0.9, size=3), category_names=["A", "B", "C"]),
    "b": pf.scatter(rng.normal(size=40), rng.normal(size=40)),
}

pf.export_panels(panels, "panels", overwrite=True)  # title-free art by default
pubfig figma push panels --figure-id figure-01

This writes panel assets such as a.svg, b.svg, and panel-index.json, then uses push as the primary panel-first handoff into Figma.

How refresh works

  • Keep the same figure_id to refresh the existing figure in place.
  • Use a new figure_id to import a separate figure.

FAQ / Troubleshooting

What does Connect Bridge do?
It links the open Figma plugin to your local terminal workflow so later push commands know which live session to refresh.

What does pubfig figma push do automatically?
It is the primary agent-first command. It ensures the local bridge is available, selects the latest connected session, writes the bundle, and then syncs or refreshes the figure.

What is the .pubfig-figma.json file?
It is the exact Figma handoff bundle for one figure. Keep it around for manual import, refresh, debugging, or recovery.

How do I do manual fallback?
If bridge refresh stalls, load the latest written .pubfig-figma.json bundle in pubfig-sync, then use Import as New, Manual Refresh, or Refresh + Relayout.

When should I use pubfig figma package?
Use it as the secondary path when you only want to write a standalone bundle without pushing immediately.

pubfig figma package panels --figure-id figure-01

Where are the advanced commands?
Use these only for finer control or debugging after the normal push path:

pubfig figma sync figure-01.pubfig-figma.json --session latest
pubfig figma watch figure-01.pubfig-figma.json --session latest
pubfig figma bridge status

If you use Codex locally, the companion skill pubfig-figma-workflow can still orchestrate the panel export → Figma import → MCP review loop.

Plot Families

Categorical and Statistical Plots

Function Description Recipe
bar Simple bar chart and grouped bar chart recipe
bar_scatter Grouped bar chart with raw points and significance annotations recipe
stacked_bar Horizontal stacked bar chart recipe
paired Paired dot plot recipe

Distribution Plots

Function Description Recipe
box Box plot recipe
violin Violin plot recipe
strip Strip plot recipe
raincloud Half-violin + box + raw-point raincloud plot recipe
density Density plot with KDE recipe
histogram Histogram with optional KDE recipe
ridgeline Ridgeline plot recipe

Trend and Relationship Plots

Function Description Recipe
line Line chart with optional CI recipe
area Stacked area chart recipe
scatter Scatter plot with optional grouped workflow recipe
bubble Bubble chart recipe
contour2d 2D contour plot with marginals recipe
radar Radar chart recipe

Matrix, Embedding, and Multivariate Plots

Function Description Recipe
heatmap Heatmap recipe
corr_matrix Correlation heatmap recipe
clustermap Clustered heatmap recipe
dimreduce Dimensionality-reduction scatter plot recipe
pca_biplot PCA biplot with optional loadings and group ellipses recipe
parallel_coordinates Parallel coordinates plot recipe

Evaluation and Flow Plots

Function Description Recipe
roc ROC curve with AUC recipe
pr_curve Precision-Recall curve with AP recipe
sankey Sankey diagram recipe

Themes, Specs, and Palettes

Built-in Themes

pubfig currently ships with these themes:

  • default
  • nature
  • science
  • cell
pf.set_default_theme("science")

Figure Specs

For export, save_figure(...) uses named figure specs:

  • nature
  • science
  • cell

Width can be specified as:

  • "single"
  • "double"
  • numeric millimeters such as 120
  • string millimeters such as "120mm"

Built-in Palettes

Built-in palettes include:

  • DEFAULT
  • NATURE
  • SCIENCE
  • LANCET
  • JAMA
from pubfig import NATURE, show_palette

show_palette(NATURE).show()

You can also fetch palettes by name:

palette = pf.get_palette("science")
palette = pf.get_palette("carto_blugrn")

These journal-style palettes are inspired palettes, not official journal standards. In pubfig, the NATURE, SCIENCE, LANCET, and JAMA cards are derived from widely used ggsci-derived community palettes rather than publisher-mandated color specifications.

Source note: ggsci documents these palettes as inspired by NPG / Nature Publishing Group, AAAS / Science, Lancet journals, and JAMA figures. See pal_npg, pal_aaas, pal_lancet, and pal_jama.

For a visual preview of all currently available palettes, see docs/palette-gallery.md.

Featured palettes

Gallery and Examples

Most files under examples/ are either:

  • runnable example scripts, or
  • rendered assets used by this README and the palette docs.

If you only want the main entry points, start here:

  • examples/gallery.py — quick visual walkthrough of supported plots
  • examples/export_gallery.py — exports the gallery to output_figures/
  • examples/export_composite_showcases_panels.py — exports the panel-first composite showcases that are assembled in Figma
  • examples/figma_workflow_demo.md — panel-first pubfig → Figma workflow guide
  • examples/generate_palette_gallery.py — regenerates the palette preview sheets and gallery docs
  • examples/README.md — keep/remove inventory for this folder

Advanced / secondary:

  • examples/export_gallery_mpl.py — focused Matplotlib export examples
  • figma-plugin/pubfig-sync/ — Figma plugin scaffold for panel import and refresh
  • docs/palette-gallery.md — visual palette gallery for built-in and Plotly-derived palettes

Development

Editable Install

pip install -e .[dev]

Run Tests

pytest

Lint

ruff check src tests examples

Regenerate Gallery

python examples/export_gallery.py

License

MIT

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

pubfig-0.2.0.tar.gz (6.6 MB view details)

Uploaded Source

Built Distribution

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

pubfig-0.2.0-py3-none-any.whl (119.0 kB view details)

Uploaded Python 3

File details

Details for the file pubfig-0.2.0.tar.gz.

File metadata

  • Download URL: pubfig-0.2.0.tar.gz
  • Upload date:
  • Size: 6.6 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.24 {"installer":{"name":"uv","version":"0.9.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for pubfig-0.2.0.tar.gz
Algorithm Hash digest
SHA256 887ee3f839bb24c28fbc37db6a451f28b2664e139cc4d37a7f92937f8c1af4b8
MD5 c243bb901ec210e579fb3101022f2d0a
BLAKE2b-256 8211cafc87e9bd45d8d655cad75c7e5918d6709d6de6f849b9443f538aefddad

See more details on using hashes here.

File details

Details for the file pubfig-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: pubfig-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 119.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.24 {"installer":{"name":"uv","version":"0.9.24","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for pubfig-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c02b022ae8d70d9282f923917d49842881c17a36642812d814de9958c12f0350
MD5 b5d35e492d3e6c436e2d025c95e6ee53
BLAKE2b-256 c02337a67ab2189136a1bee7d8714ef393b66395c3a1cb7c87bdcd0158d45c58

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