Skip to main content


GrAF (Graph Archive Format) is a file format for saving graphs — the data and the formatting together — in a way that stays readable across languages and across the years.

import graf
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot(frequency, power, label='measured')
ax.legend()

graf.save_graf(fig, 'figure1.graf')     # data + formatting, both preserved
fig = graf.load_graf('figure1.graf')    # reopen it anywhere, any time

Where this fits compared to other formats

Ways of saving a graph sit on a spectrum, and the trade-off is always the same: fidelity of appearance versus access to the underlying data.

Format Formatting Data Cross-language
PNG / bitmap Exact Lost — you must read points off the picture Yes
SVG Mostly preserved, editable Lost — shapes, not measurements Yes
Pickled matplotlib figure Exact Present, but tangled in matplotlib's object graph No — Python only
MATLAB .fig Exact Present, similarly hard to reach No — MATLAB only
GrAF Key aspects preserved Plain lists of floats, trivially accessible Yes

GrAF sits deliberately at one end of it. It does not promise the graph will look pixel-identical everywhere — fonts, line weights and sizing can vary between platforms. What it promises is that the aspects of the figure that carry scientific meaning survive: the data is stored as plain floats that are easy to read in any language, and the formatting that constitutes the visual language of the plot — axis limits, scales, line types, markers, colours, labels, legends — comes back with it.

That makes some things easy that are otherwise tedious:

  • Reopen figures from old publications and restyle them to a single coherent theme for a new talk.
  • Merge data from several plots into one.
  • Change the plot type to better convey a point, without hunting for the original script.
  • Pull the raw numbers back out years later, from a language that did not exist when you saved them.

Provenance

Every GrAF file records where it came from. A creation record is written once and never rewritten; a mutation history is appended to whenever the data changes. Both are stamped automatically on save, so a file cannot be written without them:

g = graf.Graf()
g.read_graf('figure1.graf')

g.info.provenance   # who/what/when created it, and on what machine
g.info.history      # append-only record of every change since

Pass include_system_info=False to save_graf to omit hostname and machine details.

Installation

pip install graf-format

Python 3.10+. That is everything needed to read and write .graf files. The package is graf-format on PyPI and imports as graf.

Optional extras

Extra Install Adds Cost
gui pip install 'graf-format[gui]' PyQt6 + mplcursors — the graf-viewer command and rich_show() +235 MB
test pip install 'graf-format[test]' pytest + pytest-cov, for running the suite ~10 MB
dev pip install 'graf-format[dev]' Both of the above +235 MB

Quote the name in zsh (macOS default), which treats [...] as a glob.

The viewer is optional because Qt is 235 MB — larger than GrAF and all its other dependencies combined (~149 MB). Nothing in the save/load path touches it: import graf never imports PyQt6, and a base install saves, loads and upgrades files perfectly well. Only import graf.widgets needs it, and it says so clearly if the extra is missing.

Commands

graf-upgrade -r ./figures                 # rewrite old files in the current format
graf-viewer figure1.graf --serif --bold   # open and restyle        (needs [gui])

Full details in the installation guide.

GrAF reads files written by older versions without any conversion — fields added since simply take their defaults, and Graf.unpack_report says which. Upgrading is optional; it makes those defaults explicit and is what eventually allows support for old layouts to be retired.

Fonts

A GrAF file states a font stack — an ordered list of candidates, most specific first, ending in a generic role:

g.style.set_all_font_families(["MFB Oldstyle", "Georgia", "serif"])
g.style.set_all_font_families("monospace")     # or just ask for a type

Asking for one exact typeface and asking for "any monospace" are the same mechanism; the second is simply the shortest stack. Because the stack always ends in a role, a reader that has none of the named families still knows what kind of type the figure was set in.

Each candidate is looked for among the fonts GrAF bundles, then any directories you have added, then the fonts installed on your system. First hit wins. If nothing matches, the trailing role decides — so a serif request degrades to another serif rather than jumping to a sans face — and GrAF warns once, naming what it used. Files also record resolved_family, the typeface actually in use when the figure was saved, so you can always tell faithful reproduction from substitution.

You can set what the generic roles mean on your machine, and point GrAF at your own fonts, without waiting for a GrAF release:

graf.set_font_default("serif", "EB Garamond")
graf.add_font_path("~/Library/Fonts")

Both persist to a per-user config file (graf.user_config_path()), and apply to every GrAF figure you open — including files written by someone else. The file states the author's intent; your machine states your preference.

GrAF bundles one good face per role (SUSE, MFB Oldstyle, Spline Sans Mono) so figures render sensibly out of the box. Every bundled font is under a licence permitting redistribution (SIL OFL 1.1 or CC0), and each family's licence text ships beside it — see src/graf/assets/fonts/LICENSES/.

Documentation

Full documentation, including tutorials, is at graf.readthedocs.io.

Licence

MIT — see LICENSE. Bundled fonts carry their own licences, noted above.

Metadata

Release files for graf-format 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for graf-format 0.1.0
File Size Uploaded Provenance Actions
graf_format-0.1.0.tar.gz 601.3 kB Without provenance Details for graf_format-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for graf-format 0.1.0
File Interpreter ABI Platform Actions
graf_format-0.1.0-py3-none-any.whl Python 3 none any Details for graf_format-0.1.0-py3-none-any.whl

Total release size: 1.2 MB

Release files / graf_format-0.1.0.tar.gz

Download URL graf_format-0.1.0.tar.gz
Size 601.3 kB
Tags Source
SHA-256 checksum
How to use checksums
d9f54284250bca886232cd37ba4ed1f7c6534ba06860554fc66772455af7b706
BLAKE2b-256 checksum
How to use checksums
5a224d4fce6d3f92a23e255aed428bdb616f053316961852dcd91437d096effc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.3

Release files / graf_format-0.1.0-py3-none-any.whl

Download URL graf_format-0.1.0-py3-none-any.whl
Size 552.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
22c73cebe96adc0de803eddce1925669f80bb8728e6e9509ea065245f47c6625
BLAKE2b-256 checksum
How to use checksums
52a2f44292cb9e2dafab7525095762c9eb695baf22ac4736673b231ff56ad6c1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.3

Security for graf-format 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page