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.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

graf_format-0.1.0.tar.gz (601.3 kB view details)

Uploaded Source

Built Distribution

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

graf_format-0.1.0-py3-none-any.whl (552.0 kB view details)

Uploaded Python 3

File details

Details for the file graf_format-0.1.0.tar.gz.

File metadata

  • Download URL: graf_format-0.1.0.tar.gz
  • Upload date:
  • Size: 601.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.3

File hashes

Hashes for graf_format-0.1.0.tar.gz
Algorithm Hash digest
SHA256 d9f54284250bca886232cd37ba4ed1f7c6534ba06860554fc66772455af7b706
MD5 3d9a2f8377fc0d7e3abe46b34942a0d6
BLAKE2b-256 5a224d4fce6d3f92a23e255aed428bdb616f053316961852dcd91437d096effc

See more details on using hashes here.

File details

Details for the file graf_format-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: graf_format-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 552.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.3

File hashes

Hashes for graf_format-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 22c73cebe96adc0de803eddce1925669f80bb8728e6e9509ea065245f47c6625
MD5 6f04bad63b58efcde8018d25412f8c29
BLAKE2b-256 52a2f44292cb9e2dafab7525095762c9eb695baf22ac4736673b231ff56ad6c1

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

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