Skip to main content

Matplotlib utilities and color palettes for publication-ready figures

Project description

waxwing

Matplotlib utilities and color palettes for publication-ready figures.

Waxwing applies sensible defaults—clean spines, print-quality DPI, editable PDF fonts, bundled typefaces—so you spend less time fighting Matplotlib and more time on your figures.

Installation

pip install waxwing

Quick start

import matplotlib.pyplot as plt
import waxwing

waxwing.set_default_styles()          # apply all defaults at once

fig, ax = plt.subplots()
ax.plot(x, y)
waxwing.style_axes(ax)                # trim spines, expand limits, offset axes
plt.savefig("figure.pdf")

API

set_default_styles()

Applies the full waxwing style in one call. Call once at the top of your script or notebook.

Setting Value
Font Source Sans 3 Light
Figure size 6.5 × 3 in (single column, papers)
Color palette matcha
Screen DPI 150
Save DPI 300
Save bbox tight
PDF/PS font type 42 (editable in Illustrator)
Top/right spines removed
Axis margins 0

style_axes(ax=None, pad=10, trim_axes=True, encompass_data=True)

Call once per axes after all data is plotted. Combines three operations:

  • trim spines — clips left and bottom spines to the outermost tick marks
  • expand limits — extends axis limits so all data falls within the outermost ticks, preserving Matplotlib's tick placement
  • offset axes — moves spines outward from the data (pad points)
fig, ax = plt.subplots()
ax.scatter(x, y)
waxwing.style_axes(ax)

# or with options:
waxwing.style_axes(ax, pad=5, encompass_data=False)

set_font(font_name, weight="book")

Each weight is a distinct static font file — no Matplotlib weight-matching heuristics.

Name Style
"Source Sans 3" Neutral sans-serif (default)
"Noto Sans" Wide-coverage sans-serif
"Source Serif 4" Optical-size serif
"Literata" Literary serif

Available weights: "light", "book", "regular", "medium"

waxwing.set_font("Literata", "medium")
waxwing.set_font("Source Serif 4")        # defaults to "book"

set_palette(palette)

Sets the default color cycle. Accepts a palette name or any list of hex colors.

waxwing.set_palette("wax")
waxwing.set_palette(["#e07a5f", "#3d405b", "#81b29a"])

set_figsize(w, h)

waxwing.set_figsize(3.25, 2.5)    # half-column width
waxwing.set_figsize(6.5, 4)

Color palettes

All palettes live in waxwing.palettes and are plain Python lists of hex strings, so they work anywhere Matplotlib accepts a color sequence. YOu can also use them with seaborn.

Color palettes

from waxwing import palettes

ax.plot(x, y, color=palettes.clay[1])
ax.set_prop_cycle(color=palettes.spring)

# seaborn:
sns.color_palette(palettes.savanna)

# reversed colormap:
palettes.waxwing[::-1]

Categorical

Palette Colors Character
wax 6 yellow, red, green, gold, blue, violet
clay 5 warm earth tones
fruit 5 vibrant warm hues
matcha 5 muted purples and pastels
earth 5 dark browns and teal
lichen 8 greens, ochre, and lavender
solid 5 bold primaries
sport 5 vivid cyan, purple, gold

Sequential (dark → light)

Palette Colors Character
waxwing 7 warm taupe → off-white
twilight 6 navy → lavender → grey
canopy 6 forest green → cream
glacier 6 deep blue → near-white

Diverging

Palette Colors Character
savanna 6 brown ↔ olive green
heath 6 warm brown ↔ cool green
coastline 6 slate blue ↔ mauve

Large categorical cycle: spring

spring contains 31 colors organized in five tonal cycles (vivid → pale → dark → mid → near-black, plus greys). It is designed to support figures with many series while maintaining visual coherence.

ax.set_prop_cycle(color=palettes.spring)

Requirements

  • Python ≥ 3.8
  • matplotlib

Development

Creating image in readme of current color palettes:

python create_palettes.py

Adding a new font:

Only add fonts with licenses that allow distribution.

  1. Obtain the Variable font or each weight as separate ttf. I select 350 i.e. 'Book' as the default font weight, which is typically not provided but can be created from Variable fonts.
  2. If needed, instance the font into a static .ttf:
pip install fonttools
fonttools varLib.instancer \
  SourceSans3-VariableFont_wght.ttf \
  wght=350 \
  -o SourceSans3-Book.ttf
  1. Save copies under fonts/Name/static/[Name]-Light.ttf (or Medium, Regular, etc)
  2. Update waxwing.py: _font_files and FontName variables with Name and paths to the static font weight files

Publishing to PyPI

First release

  1. Create an account-scoped API token at pypi.org → Account Settings → API tokens
  2. Add a profile to ~/.pypirc:
    [waxwing]
    repository = https://upload.pypi.org/legacy/
    username = __token__
    password = pypi-...
    
  3. Build and upload:
    pip install build twine
    python -m build
    twine upload --repository waxwing dist/*
    
  4. After the project exists on PyPI, replace the account-scoped token with a project-scoped one (pypi.org → Account Settings → API tokens → Add token, scope to waxwing), and update ~/.pypirc

Subsequent releases

  1. Bump version in pyproject.toml
  2. Delete the old dist/ directory
  3. python -m build
  4. twine upload --repository waxwing dist/*

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

waxwing-0.1.1.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.

waxwing-0.1.1-py3-none-any.whl (3.2 MB view details)

Uploaded Python 3

File details

Details for the file waxwing-0.1.1.tar.gz.

File metadata

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

File hashes

Hashes for waxwing-0.1.1.tar.gz
Algorithm Hash digest
SHA256 851760a1d1323bcbf44a161f4ffe3c8d50f9fe156f5eb447a28d8607bb52ed83
MD5 0c56cb9d08e23c5b7bcdc13006c8e086
BLAKE2b-256 ad4231aa8078435d86f38f51a43b375b6d26a8c757550772f1939f7432168388

See more details on using hashes here.

File details

Details for the file waxwing-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: waxwing-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 3.2 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.13

File hashes

Hashes for waxwing-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 6e289db566a63abf86d3ff5b18108121a442e476ec51dc7b5355edf15841a4ba
MD5 8c00e3d449b2f9ac80e06235803388f0
BLAKE2b-256 794426d15290d2acee99ca13be66526e776f88a1d45b27f2c4865dc4fdc0220b

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