plotastro 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plotastro-1.0.0/.github/workflows/ci.yml +48 -0
- plotastro-1.0.0/.github/workflows/publish.yml +44 -0
- plotastro-1.0.0/.gitignore +8 -0
- plotastro-1.0.0/CHANGELOG.md +48 -0
- plotastro-1.0.0/LICENSE +23 -0
- plotastro-1.0.0/PKG-INFO +432 -0
- plotastro-1.0.0/README.md +408 -0
- plotastro-1.0.0/examples/authors_example.csv +7 -0
- plotastro-1.0.0/examples/figures/cvd_check.png +0 -0
- plotastro-1.0.0/examples/figures/example_column.png +0 -0
- plotastro-1.0.0/examples/figures/example_full.png +0 -0
- plotastro-1.0.0/examples/figures/linestyles.png +0 -0
- plotastro-1.0.0/examples/figures/markers.png +0 -0
- plotastro-1.0.0/examples/figures/palette.png +0 -0
- plotastro-1.0.0/examples/figures/palette_okabe_ito.png +0 -0
- plotastro-1.0.0/examples/figures/redundant_encoding.png +0 -0
- plotastro-1.0.0/examples/make_reference_figures.py +90 -0
- plotastro-1.0.0/examples/tutorial.ipynb +1281 -0
- plotastro-1.0.0/pyproject.toml +42 -0
- plotastro-1.0.0/requirements-dev.txt +5 -0
- plotastro-1.0.0/requirements.txt +3 -0
- plotastro-1.0.0/src/plotastro/__init__.py +68 -0
- plotastro-1.0.0/src/plotastro/_authors.py +302 -0
- plotastro-1.0.0/src/plotastro/_colors.py +227 -0
- plotastro-1.0.0/src/plotastro/_core.py +256 -0
- plotastro-1.0.0/src/plotastro/_extras.py +187 -0
- plotastro-1.0.0/src/plotastro/styles/aanda.mplstyle +101 -0
- plotastro-1.0.0/src/plotastro/styles/apj.mplstyle +101 -0
- plotastro-1.0.0/src/plotastro/styles/jcap.mplstyle +100 -0
- plotastro-1.0.0/src/plotastro/styles/mnras.mplstyle +101 -0
- plotastro-1.0.0/src/plotastro/styles/natastro.mplstyle +102 -0
- plotastro-1.0.0/src/plotastro/styles/oja.mplstyle +102 -0
- plotastro-1.0.0/src/plotastro/styles/prd.mplstyle +101 -0
- plotastro-1.0.0/src/plotastro/styles/rasti.mplstyle +101 -0
- plotastro-1.0.0/tests/conftest.py +13 -0
- plotastro-1.0.0/tests/test_authors.py +144 -0
- plotastro-1.0.0/tests/test_colors.py +64 -0
- plotastro-1.0.0/tests/test_extras.py +55 -0
- plotastro-1.0.0/tests/test_sizing.py +74 -0
- plotastro-1.0.0/tests/test_styles.py +69 -0
- plotastro-1.0.0/tools/generate_styles.py +202 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ${{ matrix.os }}
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
os: [ubuntu-latest]
|
|
16
|
+
python-version: ["3.9", "3.11", "3.13"]
|
|
17
|
+
numpy: [""]
|
|
18
|
+
include:
|
|
19
|
+
- os: macos-latest
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
- os: windows-latest
|
|
22
|
+
python-version: "3.12"
|
|
23
|
+
# plotastro supports both NumPy major versions — test each explicitly
|
|
24
|
+
- os: ubuntu-latest
|
|
25
|
+
python-version: "3.11"
|
|
26
|
+
numpy: "numpy<2"
|
|
27
|
+
- os: ubuntu-latest
|
|
28
|
+
python-version: "3.12"
|
|
29
|
+
numpy: "numpy>=2"
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@v4
|
|
32
|
+
- uses: actions/setup-python@v5
|
|
33
|
+
with:
|
|
34
|
+
python-version: ${{ matrix.python-version }}
|
|
35
|
+
- name: Install package with test dependencies
|
|
36
|
+
shell: bash
|
|
37
|
+
run: |
|
|
38
|
+
python -m pip install ".[dev]"
|
|
39
|
+
if [ -n "${{ matrix.numpy }}" ]; then
|
|
40
|
+
python -m pip install "${{ matrix.numpy }}"
|
|
41
|
+
fi
|
|
42
|
+
- name: Run tests
|
|
43
|
+
run: pytest -v
|
|
44
|
+
- name: Check style files are in sync with the generator
|
|
45
|
+
if: runner.os == 'Linux'
|
|
46
|
+
run: |
|
|
47
|
+
python tools/generate_styles.py
|
|
48
|
+
git diff --exit-code src/plotastro/styles/
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Publishes to PyPI when you push a version tag, e.g.:
|
|
2
|
+
# git tag v1.0.0 && git push origin v1.0.0
|
|
3
|
+
#
|
|
4
|
+
# One-time setup (no secrets needed — uses PyPI "trusted publishing"):
|
|
5
|
+
# 1. Create the project on https://pypi.org/manage/account/publishing/
|
|
6
|
+
# ("Add a new pending publisher"): project name `plotastro`,
|
|
7
|
+
# this repository, workflow file `publish.yml`, environment `pypi`.
|
|
8
|
+
# 2. In the GitHub repo settings, create an environment named `pypi`.
|
|
9
|
+
|
|
10
|
+
name: Publish to PyPI
|
|
11
|
+
|
|
12
|
+
on:
|
|
13
|
+
push:
|
|
14
|
+
tags: ["v*"]
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
build:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: "3.12"
|
|
24
|
+
- name: Build sdist and wheel
|
|
25
|
+
run: |
|
|
26
|
+
python -m pip install build
|
|
27
|
+
python -m build
|
|
28
|
+
- uses: actions/upload-artifact@v4
|
|
29
|
+
with:
|
|
30
|
+
name: dist
|
|
31
|
+
path: dist/
|
|
32
|
+
|
|
33
|
+
publish:
|
|
34
|
+
needs: build
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
environment: pypi
|
|
37
|
+
permissions:
|
|
38
|
+
id-token: write # required for PyPI trusted publishing
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/download-artifact@v4
|
|
41
|
+
with:
|
|
42
|
+
name: dist
|
|
43
|
+
path: dist/
|
|
44
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0 — 2026-09-01
|
|
4
|
+
|
|
5
|
+
First release as an installable package, `plotastro`.
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- `pip install plotastro`; importing it registers all styles with
|
|
9
|
+
matplotlib, so `plt.style.use("mnras")` works everywhere.
|
|
10
|
+
- Journal styles: **MNRAS**, **RASTI**, **A&A**, **ApJ/ApJL (AASTeX)**,
|
|
11
|
+
**Open Journal of Astrophysics**, **PRD/PRL (REVTeX)**, **JCAP**, and
|
|
12
|
+
**Nature Astronomy** (sans-serif variant), plus `thesis`/`beamer`
|
|
13
|
+
width presets. All generated from one template
|
|
14
|
+
(`tools/generate_styles.py`) so they stay consistent.
|
|
15
|
+
- Helpers: `set_style`, `figsize`, `subplots`, `savefig`,
|
|
16
|
+
`style_cycler`, `lighten`/`darken`, `label_panels` (journal-style
|
|
17
|
+
(a)/(b)/(c) panel labels), reference charts
|
|
18
|
+
(`show_colors`/`show_markers`/`show_linestyles`).
|
|
19
|
+
- Colour-vision-deficiency checking: `simulate_cvd`, `check_colors`,
|
|
20
|
+
and `check_figure` (Machado et al. 2009 model; no extra dependencies).
|
|
21
|
+
- Palettes: the default colour-blind-friendly cycle (`COLORS`), Okabe &
|
|
22
|
+
Ito (`OKABE_ITO`), Petroff-10 (`PETROFF10`) and light/dark pairs
|
|
23
|
+
(`PAIRED`).
|
|
24
|
+
- Author-list generator: `authorlist("authors.csv", journal=...)` and the
|
|
25
|
+
`plotastro-authors` command-line tool turn a CSV of names/affiliations/
|
|
26
|
+
ORCIDs/emails into the journal's LaTeX author block (MNRAS, A&A,
|
|
27
|
+
AASTeX, REVTeX, JCAP and generic formats), with automatic affiliation
|
|
28
|
+
numbering and sharing. Reads real collaboration lists as-is
|
|
29
|
+
(`Authorname`/`Firstname`+`Lastname` columns, one row per affiliation,
|
|
30
|
+
embedded LaTeX accents, extra columns ignored).
|
|
31
|
+
- Zero-learning-curve mode: importing plotastro registers every style
|
|
32
|
+
with matplotlib, so `plt.style.use("mnras")` + ordinary matplotlib is
|
|
33
|
+
the entire integration (`pa.use(...)` is an alias of `set_style`).
|
|
34
|
+
- `requirements.txt` / `requirements-dev.txt` for pip users; NumPy 1.x
|
|
35
|
+
and 2.x both supported and tested in CI.
|
|
36
|
+
- Tests (pytest), CI and PyPI-publishing GitHub Actions workflows,
|
|
37
|
+
executed tutorial notebook, MIT license.
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
- Styles no longer require LaTeX: portable STIX mathtext by default,
|
|
41
|
+
with `set_style(..., usetex=True)` to opt in (newtx fonts).
|
|
42
|
+
- Submission-safe saving defaults: PDF, tight bbox, 450 dpi,
|
|
43
|
+
TrueType font embedding (`pdf.fonttype 42`).
|
|
44
|
+
|
|
45
|
+
### Migration from the original repo
|
|
46
|
+
- `MNRAS_Style.mplstyle` → `plt.style.use("mnras")` (after `import plotastro`).
|
|
47
|
+
- `myfigsize.set_size(...)` → `plotastro.set_size(...)` still works, but
|
|
48
|
+
prefer `plotastro.figsize(...)` / `plotastro.subplots(...)`.
|
plotastro-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Behnood Bandi
|
|
4
|
+
|
|
5
|
+
Based on mplstyle_for_MNRAS by M. Knabenhans (2018).
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
plotastro-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: plotastro
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Publication-quality matplotlib styles and helpers for astronomy journals (MNRAS, A&A, ApJ, OJA, PRD, JCAP, Nature Astronomy)
|
|
5
|
+
Project-URL: Homepage, https://github.com/BehnoodBandi/plotastro
|
|
6
|
+
Project-URL: Issues, https://github.com/BehnoodBandi/plotastro/issues
|
|
7
|
+
Author-email: Behnood Bandi <b.bandi@sussex.ac.uk>
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: aanda,apj,astronomy,colorblind,figures,matplotlib,mnras,plotting,publication
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Framework :: Matplotlib
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
18
|
+
Requires-Python: >=3.9
|
|
19
|
+
Requires-Dist: matplotlib>=3.5
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: numpy; extra == 'dev'
|
|
22
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# plotastro
|
|
26
|
+
|
|
27
|
+
**Publication-quality matplotlib figures for astronomy journals.**
|
|
28
|
+
|
|
29
|
+
One `pip install` gives you journal-matched styles for **MNRAS**, **RASTI**,
|
|
30
|
+
**A&A**, **ApJ/ApJL**, the **Open Journal of Astrophysics**, **PRD/PRL**,
|
|
31
|
+
**JCAP** and **Nature Astronomy** — figures at exactly the right physical
|
|
32
|
+
size, a colour-blind-friendly palette, and helpers that make the tedious
|
|
33
|
+
parts (sizing, panel labels, accessibility checks, saving) one-liners.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install plotastro
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Simplest usage — no new API to learn.** Importing plotastro registers the
|
|
40
|
+
styles with matplotlib itself; after one `plt.style.use` line you write
|
|
41
|
+
ordinary matplotlib, and the default figure size is already the journal's
|
|
42
|
+
column width:
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
import matplotlib.pyplot as plt
|
|
46
|
+
import plotastro # just to register the styles
|
|
47
|
+
|
|
48
|
+
plt.style.use("mnras") # or "aanda", "apj", "oja", "prd", ...
|
|
49
|
+
fig, ax = plt.subplots() # plain matplotlib from here on
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**With the helpers** (optional, but they make the tedious parts one-liners):
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
import plotastro as pa
|
|
56
|
+
|
|
57
|
+
pa.set_style("mnras")
|
|
58
|
+
fig, ax = pa.subplots() # one-column figure, golden-ratio height
|
|
59
|
+
ax.plot(x, y, label="model")
|
|
60
|
+
ax.set_xlabel("$x$")
|
|
61
|
+
ax.legend()
|
|
62
|
+
pa.savefig("myplot") # -> myplot.pdf, ready for \includegraphics
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
| One column | Full width |
|
|
66
|
+
|---|---|
|
|
67
|
+
|  |  |
|
|
68
|
+
|
|
69
|
+
**Start with the [tutorial notebook](examples/tutorial.ipynb)** — it walks
|
|
70
|
+
through every feature with runnable examples.
|
|
71
|
+
|
|
72
|
+
## Why this exists
|
|
73
|
+
|
|
74
|
+
Two problems ruin most paper figures:
|
|
75
|
+
|
|
76
|
+
1. **Wrong physical size.** If you hand LaTeX a 6-inch figure and it squeezes
|
|
77
|
+
it into an 84 mm column, every label shrinks by ~50 % and becomes
|
|
78
|
+
unreadable. The fix: build the figure at its final printed width, then
|
|
79
|
+
include it with a plain `\includegraphics{fig.pdf}` — no `[width=...]`.
|
|
80
|
+
2. **Inaccessible colours.** ~5 % of male readers have a colour-vision
|
|
81
|
+
deficiency, and MNRAS's
|
|
82
|
+
[author guidelines](https://academic.oup.com/mnras/pages/general_instructions)
|
|
83
|
+
explicitly ask for colour-blind-friendly figures. The default matplotlib
|
|
84
|
+
cycle is not; the one here is — and `pa.check_figure()` lets you verify it.
|
|
85
|
+
|
|
86
|
+
The styles share one visual language — Times-like serif fonts at ~9 pt with
|
|
87
|
+
~8 pt tick lettering, inward ticks on all four sides with minors, a subtle
|
|
88
|
+
grid, frameless legends — and differ only in figure width (plus the
|
|
89
|
+
sans-serif fonts Nature requires), so your plots stay **consistent between
|
|
90
|
+
papers** no matter where you submit.
|
|
91
|
+
|
|
92
|
+
## Supported journals
|
|
93
|
+
|
|
94
|
+
`pa.set_style(...)`, `pa.figsize(...)` and `plt.style.use(...)` accept
|
|
95
|
+
(aliases in parentheses):
|
|
96
|
+
|
|
97
|
+
| key | journal | one column | full width |
|
|
98
|
+
|---|---|---|---|
|
|
99
|
+
| `mnras` | Monthly Notices of the RAS | 240.0 pt = 3.32 in | 504.0 pt = 6.97 in |
|
|
100
|
+
| `rasti` | RAS Techniques & Instruments | 240.0 pt = 3.32 in | 504.0 pt = 6.97 in |
|
|
101
|
+
| `aanda` (`a&a`, `aa`) | Astronomy & Astrophysics | 250.4 pt = 3.46 in (88 mm) | 512.2 pt = 7.09 in (180 mm) |
|
|
102
|
+
| `apj` (`apjl`, `aastex`) | The Astrophysical Journal | 242.3 pt = 3.35 in | 513.1 pt = 7.10 in |
|
|
103
|
+
| `oja` | Open Journal of Astrophysics | ≈245.3 pt = 3.39 in | ≈508 pt = 7.03 in |
|
|
104
|
+
| `prd` (`prl`, `revtex`) | Physical Review D | 246.0 pt = 3.40 in | 510.0 pt = 7.06 in |
|
|
105
|
+
| `jcap` | J. Cosmology & Astroparticle Phys. | single-column ≈455 pt = 6.30 in | — |
|
|
106
|
+
| `natastro` (`nature`) | Nature Astronomy (sans-serif!) | 253.2 pt = 3.50 in (89 mm) | 520.7 pt = 7.20 in (183 mm) |
|
|
107
|
+
| `thesis` | A4 thesis text width | 426.8 pt = 5.91 in | — |
|
|
108
|
+
| `beamer` | Beamer slide text width | 307.3 pt = 4.25 in | — |
|
|
109
|
+
|
|
110
|
+
Widths come from each journal's LaTeX class / author guide. For a custom
|
|
111
|
+
document, put `\the\columnwidth` or `\the\textwidth` in your `.tex` body,
|
|
112
|
+
compile, read the value off the page, and pass it directly:
|
|
113
|
+
`pa.figsize(width=345.0)`.
|
|
114
|
+
|
|
115
|
+
The only hard dependency is matplotlib; plotastro works with both NumPy 1.x
|
|
116
|
+
and 2.x (CI tests each). Running the examples from a clone?
|
|
117
|
+
`pip install -r requirements-dev.txt`.
|
|
118
|
+
|
|
119
|
+
## Tutorial
|
|
120
|
+
|
|
121
|
+
### Figure sizing
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
pa.figsize("column") # one column, golden-ratio height
|
|
125
|
+
pa.figsize("full") # full text width
|
|
126
|
+
pa.figsize("column", fraction=0.5) # half a column
|
|
127
|
+
pa.figsize("column", aspect=1) # square panel (aspect = height/width)
|
|
128
|
+
pa.figsize("column", journal="aanda") # size for a specific journal
|
|
129
|
+
pa.figsize(345.0) # any width in LaTeX points
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`pa.subplots()` takes the same arguments *plus* everything `plt.subplots`
|
|
133
|
+
accepts, and scales the height with the grid so each panel keeps its aspect:
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
fig, ax = pa.subplots() # 1 panel, one column
|
|
137
|
+
fig, axes = pa.subplots(2, 2, width="full") # 2x2 grid, full width
|
|
138
|
+
fig, axes = pa.subplots(1, 2, width="full", aspect=0.75, sharey=True)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### The colour palette
|
|
142
|
+
|
|
143
|
+

|
|
144
|
+
|
|
145
|
+
The default cycle has 12 colours, all accessible by name via `pa.COLORS`
|
|
146
|
+
(e.g. `pa.COLORS["blue"]`), or as matplotlib's `"C0"`…`"C11"` shorthands:
|
|
147
|
+
|
|
148
|
+
- **C0–C8** are a colour-blind-safe re-ordering of the
|
|
149
|
+
[ColorBrewer](https://colorbrewer2.org) *Set1* qualitative palette
|
|
150
|
+
(popularised by [Thøger Rivera-Thorsen's CBcycle](https://gist.github.com/thriveth/8560036)).
|
|
151
|
+
Consecutive colours differ in **lightness as well as hue**, so adjacent
|
|
152
|
+
lines stay distinguishable under the common deficiencies (deuteranopia,
|
|
153
|
+
protanopia) *and* in greyscale print; the notorious red–green pair is
|
|
154
|
+
pushed far apart in the cycle (green is C2, red is C7), so plots with a
|
|
155
|
+
handful of lines never rely on it.
|
|
156
|
+
- **C9–C11** are light companions (from Tableau's *Color Blind 10*): use them
|
|
157
|
+
for uncertainty bands, reference curves, or de-emphasised data underneath a
|
|
158
|
+
saturated line of the same hue.
|
|
159
|
+
|
|
160
|
+
Matched shades without transparency (better for print and EPS):
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
ax.plot(x, y, color=pa.COLORS["blue"])
|
|
164
|
+
ax.fill_between(x, lo, hi, color=pa.lighten(pa.COLORS["blue"], 0.7))
|
|
165
|
+
pa.darken(pa.COLORS["orange"], 0.3) # the other direction
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Three more palettes ship with the package:
|
|
169
|
+
|
|
170
|
+
- `pa.OKABE_ITO` — [Okabe & Ito (2008)](https://jfly.uni-koeln.de/color/),
|
|
171
|
+
*the* classic CVD-safe recommendation for categorical colours in science;
|
|
172
|
+
- `pa.PETROFF10` — [Petroff (2021)](https://arxiv.org/abs/2107.02270), the
|
|
173
|
+
CVD-optimised 10-colour cycle used across particle physics;
|
|
174
|
+
- `pa.PAIRED` — light/dark pairs for data/model or before/after comparisons:
|
|
175
|
+
`pa.PAIRED["blue"]` → `("#a6cee3", "#1f78b4")`.
|
|
176
|
+
|
|
177
|
+
**Colormaps:** the styles default to `viridis` (perceptually uniform,
|
|
178
|
+
CVD-safe). Good picks: `viridis`/`magma`/`cividis` for sequential data,
|
|
179
|
+
`RdBu_r` or `coolwarm` for diverging data (red–*blue*, not red–green). Avoid
|
|
180
|
+
`jet`/`rainbow`. For more astro-friendly maps see
|
|
181
|
+
[cmasher](https://cmasher.readthedocs.io) and [cmocean](https://matplotlib.org/cmocean/).
|
|
182
|
+
|
|
183
|
+
### Checking accessibility yourself
|
|
184
|
+
|
|
185
|
+

|
|
186
|
+
|
|
187
|
+
Don't take the palette's word for it — simulate it
|
|
188
|
+
(Machado et al. 2009 model, no extra dependencies):
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
pa.check_colors() # any palette under deuteranopia/protanopia/greyscale
|
|
192
|
+
pa.check_colors(pa.PAIRED) # works on your own colour lists/dicts too
|
|
193
|
+
pa.check_figure(fig) # simulate a whole rendered figure — the
|
|
194
|
+
# final check before submission
|
|
195
|
+
pa.simulate_cvd("#e41a1c", "deuteranopia") # the raw transform
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
If two lines merge in any panel, add markers or dash patterns (below), or
|
|
199
|
+
pick colours further apart in the cycle. MNRAS recommends
|
|
200
|
+
[Color Oracle](https://colororacle.org) and ColorBrewer for exactly this;
|
|
201
|
+
now it's built in.
|
|
202
|
+
|
|
203
|
+
### Markers
|
|
204
|
+
|
|
205
|
+

|
|
206
|
+
|
|
207
|
+
`pa.MARKERS = ["o", "s", "^", "D", "v", "p", "*", "X"]` — filled shapes that
|
|
208
|
+
survive shrinking to 4 pt. Conventions worth knowing:
|
|
209
|
+
|
|
210
|
+
| marker | typical use in astro figures |
|
|
211
|
+
|---|---|
|
|
212
|
+
| `"o"` `"s"` `"D"` | primary data series |
|
|
213
|
+
| `"^"` / `"v"` | **lower / upper limits** (readers expect this) |
|
|
214
|
+
| `"*"` `"p"` | highlight special objects (the Sun, a best-fit point) |
|
|
215
|
+
| `"x"` `"+"` | thin crosses — dense scatter plots, since they don't occlude |
|
|
216
|
+
| `"."` | huge point clouds (use `ms=1`–`2`, or better, rasterized hexbin) |
|
|
217
|
+
|
|
218
|
+
Useful tricks: `markevery=7` thins markers on dense curves;
|
|
219
|
+
`mfc="none"` (hollow markers) keeps overlapping datasets readable;
|
|
220
|
+
`ms=` and `mew=` control size and edge width.
|
|
221
|
+
|
|
222
|
+
### Line styles
|
|
223
|
+
|
|
224
|
+

|
|
225
|
+
|
|
226
|
+
Beyond matplotlib's `"-"`, `"--"`, `":"`, `"-."`, the dict `pa.LINESTYLES`
|
|
227
|
+
provides named dash tuples of the form `(offset, (on, off, ...))` in points:
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
ax.plot(x, y, ls=pa.LINESTYLES["long dash"]) # (0, (9, 3))
|
|
231
|
+
ax.plot(x, y, ls=(0, (4, 1, 1, 1))) # or roll your own
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Guidelines: keep to ≤ 4 distinct dash patterns per panel (more becomes
|
|
235
|
+
noise); use solid for data / the headline result and dashes/dots for models
|
|
236
|
+
and references; MNRAS explicitly warns against triple-dot-dashed lines.
|
|
237
|
+
|
|
238
|
+
### Redundant encoding — the cycler
|
|
239
|
+
|
|
240
|
+
Colour should never be the *only* difference between curves. `pa.style_cycler`
|
|
241
|
+
advances colour, marker and/or line style **in step**, so every series is
|
|
242
|
+
unique in two or three channels at once (and survives greyscale printing):
|
|
243
|
+
|
|
244
|
+

|
|
245
|
+
|
|
246
|
+
```python
|
|
247
|
+
ax.set_prop_cycle(pa.style_cycler(markers=True)) # one axes
|
|
248
|
+
ax.set_prop_cycle(pa.style_cycler(linestyles=True, markers=True))
|
|
249
|
+
plt.rc("axes", prop_cycle=pa.style_cycler(markers=True)) # everywhere
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Panel labels
|
|
253
|
+
|
|
254
|
+
Journals want multi-panel figures labelled (a), (b), (c)…:
|
|
255
|
+
|
|
256
|
+
```python
|
|
257
|
+
fig, axes = pa.subplots(2, 2, width="full")
|
|
258
|
+
pa.label_panels(axes) # (a) (b) (c) (d)
|
|
259
|
+
pa.label_panels(axes, loc="outside", fmt="{}", fontweight="bold") # Nature style
|
|
260
|
+
pa.label_panels(axes, uppercase=True, loc="lower right") # (A) ... bottom-right
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### LaTeX text rendering
|
|
264
|
+
|
|
265
|
+
By default the styles use matplotlib **mathtext** with STIX fonts:
|
|
266
|
+
Times-compatible maths, zero dependencies. For pixel-perfect agreement with
|
|
267
|
+
your manuscript (custom macros, real kerning):
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
pa.set_style("mnras", usetex=True) # needs latex + dvipng + ghostscript
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
This loads the `newtx` Times fonts (matching the MNRAS/A&A house font), or
|
|
274
|
+
Helvetica for Nature Astronomy. Develop with `usetex=False`, flip it on for
|
|
275
|
+
the final version — LaTeX rendering is slow.
|
|
276
|
+
|
|
277
|
+
### Saving figures
|
|
278
|
+
|
|
279
|
+
The styles bake in submission-friendly defaults: **PDF** output, 450 dpi for
|
|
280
|
+
rasterised elements (journals want ≥ 300–400), tight bounding box, and
|
|
281
|
+
TrueType font embedding (`pdf.fonttype: 42`, so no Type-3 font rejections).
|
|
282
|
+
|
|
283
|
+
```python
|
|
284
|
+
pa.savefig("figure1") # figure1.pdf
|
|
285
|
+
pa.savefig("figure1", formats=("pdf", "png")) # + a PNG for slides/Slack
|
|
286
|
+
pa.savefig("figure1", fig=fig, dpi=600) # extra options pass through
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
If a journal insists on EPS, note EPS has **no transparency** — replace
|
|
290
|
+
`alpha=` with `pa.lighten()` shades (a good habit anyway).
|
|
291
|
+
|
|
292
|
+
### Author lists from a CSV
|
|
293
|
+
|
|
294
|
+
Assembling the author/affiliation block by hand is error-prone on long
|
|
295
|
+
collaborations. Feed plotastro the author CSV your collaboration already
|
|
296
|
+
maintains — it works with real-world lists exactly as they are
|
|
297
|
+
(this is [examples/authors_example.csv](examples/authors_example.csv)):
|
|
298
|
+
|
|
299
|
+
```csv
|
|
300
|
+
Lastname,Firstname,Authorname,Email,JoinedAsBuilder,Affiliation,ORCID,
|
|
301
|
+
Bandi,Behnood,Behnood Bandi, b.bandi@sussex.ac.uk, False,"Astronomy Centre, University of Sussex, Falmer, Brighton BN1 9QH, UK",0000-0001-5838-3903,
|
|
302
|
+
Rocher,Antoine,Antoine Rocher,antoine.rocher@epfl.ch,False,"EPFL, \'{E}cole polytechnique f\'{e}d\'{e}rale de Lausanne, Chemin des Maillettes, 51, 1290 Versoix, Switzerland",0000-0003-4349-6424,
|
|
303
|
+
Verdier,Aur\'{e}lien,Aur\'{e}lien Verdier,aurelien.verdier@epfl.ch,False,"EPFL, \'{E}cole polytechnique f\'{e}d\'{e}rale de Lausanne, Chemin des Maillettes, 51, 1290 Versoix, Switzerland",,
|
|
304
|
+
Richard,Johan,Johan Richard,johan.richard@univ-lyon1.fr,False,"CRAL, Centre de Recherche Astrophysique de Lyon, Universit\'{e} de Lyon, 9 avenue Charles Andr\'{e}, 69230 Saint-Genis-Laval, France",0000-0001-5492-1049,
|
|
305
|
+
Loveday,Jon ,Jon Loveday, j.loveday@sussex.ac.uk, False,"Astronomy Centre, University of Sussex, Falmer, Brighton BN1 9QH, UK",0000-0001-5290-8940,
|
|
306
|
+
Brown,Michael,Michael Brown,michael.brown@monash.edu,False,"Monash, School of Physics and Astronomy, Monash University, Wellington Road, Clayton, VIC 3800, Australia",0000-0002-1207-9137,
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
It recognises `Authorname` (or `name`, or `Firstname`+`Lastname`),
|
|
310
|
+
`Affiliation`/`affiliations` (several separated by `;`, or one row per
|
|
311
|
+
affiliation — repeated author rows are merged), and optional `ORCID` and
|
|
312
|
+
`Email`; **every other column is ignored** (`JoinedAsBuilder`, ...), stray
|
|
313
|
+
spaces are stripped, and LaTeX already in the file (accents like `\'{e}`)
|
|
314
|
+
passes through untouched. Affiliations are numbered in order of first
|
|
315
|
+
appearance and shared between authors automatically; the first author with
|
|
316
|
+
an email becomes the corresponding author.
|
|
317
|
+
|
|
318
|
+
```python
|
|
319
|
+
print(pa.authorlist("authors_example.csv", journal="mnras"))
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
```latex
|
|
323
|
+
\author[B. Bandi et al.]{
|
|
324
|
+
Behnood Bandi,$^{1}$\thanks{E-mail: b.bandi@sussex.ac.uk}
|
|
325
|
+
Antoine Rocher,$^{2}$
|
|
326
|
+
Aur\'{e}lien Verdier,$^{2}$
|
|
327
|
+
Johan Richard,$^{3}$
|
|
328
|
+
Jon Loveday$^{1}$
|
|
329
|
+
and Michael Brown$^{4}$
|
|
330
|
+
\\
|
|
331
|
+
% List of institutions
|
|
332
|
+
$^{1}$Astronomy Centre, University of Sussex, Falmer, Brighton BN1 9QH, UK\\
|
|
333
|
+
$^{2}$EPFL, \'{E}cole polytechnique f\'{e}d\'{e}rale de Lausanne, Chemin des Maillettes, 51, 1290 Versoix, Switzerland\\
|
|
334
|
+
$^{3}$CRAL, Centre de Recherche Astrophysique de Lyon, Universit\'{e} de Lyon, 9 avenue Charles Andr\'{e}, 69230 Saint-Genis-Laval, France\\
|
|
335
|
+
$^{4}$Monash, School of Physics and Astronomy, Monash University, Wellington Road, Clayton, VIC 3800, Australia
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
The same CSV works for every journal: `mnras`/`rasti`, `aanda`
|
|
340
|
+
(`\inst`/`\institute`), `apj`/`oja` (AASTeX `\author`/`\affiliation` with
|
|
341
|
+
ORCIDs), `prd` (REVTeX), `jcap` (lettered `\affiliation[a]`), or `generic`
|
|
342
|
+
for a plain numbered block. A command-line tool ships with the package, so
|
|
343
|
+
co-authors who don't use Python can run it too:
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
plotastro-authors authors.csv --journal aanda
|
|
347
|
+
plotastro-authors authors.csv -j apj -o authors.tex
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
See [examples/authors_example.csv](examples/authors_example.csv) for a
|
|
351
|
+
complete example.
|
|
352
|
+
|
|
353
|
+
## API summary
|
|
354
|
+
|
|
355
|
+
| | |
|
|
356
|
+
|---|---|
|
|
357
|
+
| `set_style(journal, usetex=, grid=, **rc)` | activate a journal's style (alias: `use`) |
|
|
358
|
+
| `authorlist(csv, journal=)` | LaTeX author/affiliation block from a CSV (CLI: `plotastro-authors`) |
|
|
359
|
+
| `figsize(width, journal=, fraction=, aspect=, ...)` | journal-correct figure dimensions |
|
|
360
|
+
| `subplots(...)` | `plt.subplots` with the size computed for you |
|
|
361
|
+
| `savefig(name, formats=("pdf",))` | save one figure in several formats |
|
|
362
|
+
| `label_panels(axes, ...)` | (a), (b), (c) panel labels |
|
|
363
|
+
| `style_cycler(markers=, linestyles=)` | redundant-encoding property cycle |
|
|
364
|
+
| `COLORS`, `CYCLE`, `OKABE_ITO`, `PETROFF10`, `PAIRED` | palettes |
|
|
365
|
+
| `lighten(c, f)`, `darken(c, f)` | matched shades without transparency |
|
|
366
|
+
| `simulate_cvd`, `check_colors`, `check_figure` | colour-vision-deficiency checks |
|
|
367
|
+
| `MARKERS`, `LINESTYLES` | curated marker / dash-pattern sequences |
|
|
368
|
+
| `show_colors()`, `show_markers()`, `show_linestyles()` | reference charts |
|
|
369
|
+
| `current_journal()`, `JOURNALS`, `GOLDEN` | introspection |
|
|
370
|
+
| `set_size(...)` | deprecated alias for the original `myfigsize` API |
|
|
371
|
+
|
|
372
|
+
## Tweaks and FAQ
|
|
373
|
+
|
|
374
|
+
- **Turn the grid off:** `pa.set_style("mnras", grid=False)`, or per-axes
|
|
375
|
+
`ax.grid(False)`.
|
|
376
|
+
- **Override anything:** `pa.set_style("mnras", **{"font.size": 10})`, or
|
|
377
|
+
`plt.rcParams[...] = ...` after `set_style`.
|
|
378
|
+
- **"Times New Roman not found" warning:** the font list falls back through
|
|
379
|
+
Times → Nimbus Roman → STIX → DejaVu automatically; install
|
|
380
|
+
`mscorefonts`/STIX to silence it, or ignore it.
|
|
381
|
+
- **Labels getting cut off?** They shouldn't be — the styles enable
|
|
382
|
+
`constrained_layout`. If you manage layout manually, disable it with
|
|
383
|
+
`plt.rcParams["figure.constrained_layout.use"] = False`.
|
|
384
|
+
- **Astronomical images:** use `origin="lower"` in `imshow` (or uncomment
|
|
385
|
+
`image.origin: lower` in the style file), and `ax.grid(False)`.
|
|
386
|
+
- **Figures look huge/small on screen:** that's just `figure.dpi: 150` for
|
|
387
|
+
display; the saved size is exact.
|
|
388
|
+
- **Styles without Python helpers:** after `import plotastro` once,
|
|
389
|
+
`plt.style.use("mnras")` works in any code; or copy the `.mplstyle` files
|
|
390
|
+
from `src/plotastro/styles/` into `matplotlib.get_configdir()/stylelib/`.
|
|
391
|
+
- **Old API:** `plotastro.set_size(...)` reproduces the original
|
|
392
|
+
`myfigsize.set_size()`; the old `MNRAS_Style.mplstyle` is now
|
|
393
|
+
`plt.style.use("mnras")`.
|
|
394
|
+
|
|
395
|
+
## Development
|
|
396
|
+
|
|
397
|
+
```bash
|
|
398
|
+
git clone <this repo> && cd <repo>
|
|
399
|
+
pip install -e ".[dev]"
|
|
400
|
+
pytest # run the test suite
|
|
401
|
+
python tools/generate_styles.py # regenerate styles/ after editing the template
|
|
402
|
+
python examples/make_reference_figures.py # regenerate README figures
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
The `.mplstyle` files are generated from a single template in
|
|
406
|
+
[tools/generate_styles.py](tools/generate_styles.py) — edit that, not the
|
|
407
|
+
files (CI checks they stay in sync). Releases: bump the version in
|
|
408
|
+
`pyproject.toml` and `CHANGELOG.md`, then push a `v*` tag — the
|
|
409
|
+
[publish workflow](.github/workflows/publish.yml) builds and uploads to PyPI
|
|
410
|
+
(see the one-time trusted-publishing setup notes in that file).
|
|
411
|
+
|
|
412
|
+
## Credits
|
|
413
|
+
|
|
414
|
+
- Original MNRAS style this grew from:
|
|
415
|
+
[M. Knabenhans' mplstyle_for_MNRAS](https://github.com/mischakn/mplstyle_for_MNRAS)
|
|
416
|
+
- Colour-blind-friendly Set1 ordering:
|
|
417
|
+
[Thøger Rivera-Thorsen](https://gist.github.com/thriveth/8560036);
|
|
418
|
+
light colours from Tableau *Color Blind 10*
|
|
419
|
+
- Palettes: [Okabe & Ito](https://jfly.uni-koeln.de/color/),
|
|
420
|
+
[Petroff (2021)](https://arxiv.org/abs/2107.02270), ColorBrewer *Paired*
|
|
421
|
+
- CVD model: Machado, Oliveira & Fernandes (2009), IEEE TVCG 15(6)
|
|
422
|
+
- Figure-size approach after
|
|
423
|
+
[Jack Walton's guide](https://jwalton.info/Embed-Publication-Matplotlib-Latex/)
|
|
424
|
+
- Journal guidelines:
|
|
425
|
+
[MNRAS](https://academic.oup.com/mnras/pages/general_instructions) ·
|
|
426
|
+
[A&A](https://www.aanda.org/for-authors) ·
|
|
427
|
+
[AAS Journals](https://journals.aas.org/graphics-guide/) ·
|
|
428
|
+
[OJA](https://astro.theoj.org/site/instructions) ·
|
|
429
|
+
[APS](https://journals.aps.org/authors) ·
|
|
430
|
+
[Nature](https://www.nature.com/nature/for-authors/formatting-guide)
|
|
431
|
+
|
|
432
|
+
MIT licensed — see [LICENSE](LICENSE).
|