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 (
padpoints)
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.
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.
- 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.
- 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
- Save copies under fonts/Name/static/[Name]-Light.ttf (or Medium, Regular, etc)
- Update
waxwing.py:_font_filesandFontNamevariables with Name and paths to the static font weight files
Publishing to PyPI
First release
- Create an account-scoped API token at pypi.org → Account Settings → API tokens
- Add a profile to
~/.pypirc:[waxwing] repository = https://upload.pypi.org/legacy/ username = __token__ password = pypi-...
- Build and upload:
pip install build twine python -m build twine upload --repository waxwing dist/*
- 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
- Bump
versioninpyproject.toml - Delete the old
dist/directory python -m buildtwine upload --repository waxwing dist/*
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
851760a1d1323bcbf44a161f4ffe3c8d50f9fe156f5eb447a28d8607bb52ed83
|
|
| MD5 |
0c56cb9d08e23c5b7bcdc13006c8e086
|
|
| BLAKE2b-256 |
ad4231aa8078435d86f38f51a43b375b6d26a8c757550772f1939f7432168388
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6e289db566a63abf86d3ff5b18108121a442e476ec51dc7b5355edf15841a4ba
|
|
| MD5 |
8c00e3d449b2f9ac80e06235803388f0
|
|
| BLAKE2b-256 |
794426d15290d2acee99ca13be66526e776f88a1d45b27f2c4865dc4fdc0220b
|