EasyPlotLib 0.1.1__tar.gz → 0.2.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.
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib/__init__.py +26 -0
- easyplotlib-0.2.0/EasyPlotLib/export.py +58 -0
- easyplotlib-0.2.0/EasyPlotLib/helpers.py +53 -0
- easyplotlib-0.2.0/EasyPlotLib/palettes.py +178 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib/styles/nature.mplstyle +13 -0
- easyplotlib-0.2.0/EasyPlotLib.egg-info/PKG-INFO +135 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib.egg-info/SOURCES.txt +3 -0
- easyplotlib-0.2.0/PKG-INFO +135 -0
- easyplotlib-0.2.0/README.md +115 -0
- easyplotlib-0.1.1/EasyPlotLib.egg-info/PKG-INFO +0 -22
- easyplotlib-0.1.1/PKG-INFO +0 -22
- easyplotlib-0.1.1/README.md +0 -2
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib/cartopy_helper.py +0 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib/figsizes_set.py +0 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib/fonts/texgyreadventor-bold.ttf +0 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib/fonts/texgyreadventor-bolditalic.ttf +0 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib/fonts/texgyreadventor-italic.ttf +0 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib/fonts/texgyreadventor-regular.ttf +0 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib/fonts/texgyrechorus-mediumitalic.ttf +0 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib/fonts/texgyrecursor-bold.ttf +0 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib/fonts/texgyrecursor-bolditalic.ttf +0 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib/fonts/texgyrecursor-italic.ttf +0 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib/fonts/texgyrecursor-regular.ttf +0 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib/fonts/texgyreheros-bold.ttf +0 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib/fonts/texgyreheros-bolditalic.ttf +0 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib/fonts/texgyreheros-italic.ttf +0 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib/fonts/texgyreheros-regular.ttf +0 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib/fonts/texgyreschola-bold.ttf +0 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib/fonts/texgyreschola-bolditalic.ttf +0 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib/fonts/texgyreschola-italic.ttf +0 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib/fonts/texgyreschola-regular.ttf +0 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib.egg-info/dependency_links.txt +0 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/EasyPlotLib.egg-info/top_level.txt +0 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/setup.cfg +0 -0
- {easyplotlib-0.1.1 → easyplotlib-0.2.0}/setup.py +0 -0
|
@@ -7,7 +7,33 @@ import matplotlib.pyplot as plt
|
|
|
7
7
|
import EasyPlotLib
|
|
8
8
|
|
|
9
9
|
from .cartopy_helper import cartopy_plot_tickmarks
|
|
10
|
+
from .export import save_pub
|
|
10
11
|
from .figsizes_set import figsizes, subplot_labels
|
|
12
|
+
from .helpers import journal_style
|
|
13
|
+
from .palettes import (
|
|
14
|
+
COLORMAPS,
|
|
15
|
+
PALETTES,
|
|
16
|
+
SEMANTIC,
|
|
17
|
+
alpha_ramp,
|
|
18
|
+
get_palette,
|
|
19
|
+
set_palette,
|
|
20
|
+
shades,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"cartopy_plot_tickmarks",
|
|
25
|
+
"figsizes",
|
|
26
|
+
"subplot_labels",
|
|
27
|
+
"journal_style",
|
|
28
|
+
"save_pub",
|
|
29
|
+
"PALETTES",
|
|
30
|
+
"SEMANTIC",
|
|
31
|
+
"COLORMAPS",
|
|
32
|
+
"get_palette",
|
|
33
|
+
"set_palette",
|
|
34
|
+
"alpha_ramp",
|
|
35
|
+
"shades",
|
|
36
|
+
]
|
|
11
37
|
|
|
12
38
|
# register the bundled stylesheets in the matplotlib style library
|
|
13
39
|
EasyPlotLib_path = EasyPlotLib.__path__[0]
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Publication-quality figure export.
|
|
2
|
+
|
|
3
|
+
Saves a figure to several formats at once with settings that keep text
|
|
4
|
+
**editable** in Illustrator/Inkscape (``pdf.fonttype = 42``, ``svg.fonttype =
|
|
5
|
+
none`` — configured in the bundled ``nature`` style) and rasters at journal DPI.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
from typing import Iterable
|
|
10
|
+
|
|
11
|
+
import matplotlib.figure
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def save_pub(
|
|
15
|
+
fig: matplotlib.figure.Figure,
|
|
16
|
+
filename: str,
|
|
17
|
+
formats: Iterable[str] = ("pdf", "png"),
|
|
18
|
+
dpi: int = 600,
|
|
19
|
+
transparent: bool = False,
|
|
20
|
+
bbox_inches: str = "tight",
|
|
21
|
+
pad_inches: float = 0.02,
|
|
22
|
+
**savefig_kwargs,
|
|
23
|
+
) -> list:
|
|
24
|
+
"""Save ``fig`` to one file per format and return the written paths.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
filename:
|
|
29
|
+
Path *without* extension, e.g. ``"figures/fig1"``. Parent directories
|
|
30
|
+
are created if needed.
|
|
31
|
+
formats:
|
|
32
|
+
Any matplotlib-supported extensions. Vector formats (``pdf``, ``svg``,
|
|
33
|
+
``eps``) are preferred for line art; ``tiff``/``png`` at ``dpi`` for
|
|
34
|
+
raster submission. Most journals want one vector + one high-DPI raster.
|
|
35
|
+
dpi:
|
|
36
|
+
Resolution for raster formats. 600 dpi suits line art; use 300 for
|
|
37
|
+
photographic/heatmap content if file size matters.
|
|
38
|
+
"""
|
|
39
|
+
if isinstance(formats, str): # a bare "png" must not iterate into 'p','n','g'
|
|
40
|
+
formats = (formats,)
|
|
41
|
+
|
|
42
|
+
directory = os.path.dirname(filename)
|
|
43
|
+
if directory:
|
|
44
|
+
os.makedirs(directory, exist_ok=True)
|
|
45
|
+
|
|
46
|
+
written = []
|
|
47
|
+
for fmt in formats:
|
|
48
|
+
path = f"{filename}.{fmt.lstrip('.')}"
|
|
49
|
+
fig.savefig(
|
|
50
|
+
path,
|
|
51
|
+
dpi=dpi,
|
|
52
|
+
transparent=transparent,
|
|
53
|
+
bbox_inches=bbox_inches,
|
|
54
|
+
pad_inches=pad_inches,
|
|
55
|
+
**savefig_kwargs,
|
|
56
|
+
)
|
|
57
|
+
written.append(path)
|
|
58
|
+
return written
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""High-level convenience for setting up a journal-ready figure context."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import matplotlib.pyplot as plt
|
|
6
|
+
|
|
7
|
+
from .figsizes_set import figsizes
|
|
8
|
+
from .palettes import DEFAULT_PALETTE, set_palette
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def journal_style(
|
|
12
|
+
journal_key: str = "nat1",
|
|
13
|
+
*,
|
|
14
|
+
palette: str = DEFAULT_PALETTE,
|
|
15
|
+
base_style: str = "nature",
|
|
16
|
+
nrows: Optional[int] = None,
|
|
17
|
+
ncols: Optional[int] = None,
|
|
18
|
+
apply: bool = True,
|
|
19
|
+
**figsize_kwargs,
|
|
20
|
+
) -> dict:
|
|
21
|
+
"""Apply a complete publication style in one call.
|
|
22
|
+
|
|
23
|
+
Combines the bundled ``base_style`` stylesheet, a journal column-width
|
|
24
|
+
:func:`~EasyPlotLib.figsizes` figure size, and a qualitative
|
|
25
|
+
:func:`~EasyPlotLib.set_palette` colour cycle.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
journal_key:
|
|
30
|
+
A key from ``figsizes`` (e.g. ``"nat1"``/``"nat2"`` single/double
|
|
31
|
+
column for Nature, ``"science"`` keys ``"aaas1"``/``"aaas2"``, etc.).
|
|
32
|
+
palette:
|
|
33
|
+
Qualitative palette name (see :data:`EasyPlotLib.PALETTES`).
|
|
34
|
+
base_style:
|
|
35
|
+
A registered matplotlib style; ``"nature"`` is bundled.
|
|
36
|
+
nrows, ncols:
|
|
37
|
+
Passed to ``figsizes`` to auto-pick a sensible aspect ratio for a grid.
|
|
38
|
+
apply:
|
|
39
|
+
When ``True`` (default) mutate the global rc state. Set ``False`` to
|
|
40
|
+
only compute and return the figsize dict.
|
|
41
|
+
|
|
42
|
+
Returns
|
|
43
|
+
-------
|
|
44
|
+
dict
|
|
45
|
+
The figure-size rcParams dict (the return value of ``figsizes``), handy
|
|
46
|
+
to splice into ``plt.rcParams.update`` or ``plt.style.context``.
|
|
47
|
+
"""
|
|
48
|
+
fs = figsizes(journal_key, nrows=nrows, ncols=ncols, **figsize_kwargs)
|
|
49
|
+
if apply:
|
|
50
|
+
plt.style.use(base_style)
|
|
51
|
+
plt.rcParams.update(fs)
|
|
52
|
+
set_palette(palette)
|
|
53
|
+
return fs
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""Curated, journal-friendly qualitative color palettes.
|
|
2
|
+
|
|
3
|
+
The palettes are low-to-medium saturation sets that read well in print and are
|
|
4
|
+
colour-vision-deficiency aware. The names follow the well-known ``ggsci``
|
|
5
|
+
collections so they are familiar to anyone coming from R.
|
|
6
|
+
|
|
7
|
+
Usage
|
|
8
|
+
-----
|
|
9
|
+
>>> import EasyPlotLib as epl
|
|
10
|
+
>>> epl.set_palette("nature") # set the global color cycle
|
|
11
|
+
>>> colors = epl.get_palette("lancet") # grab the raw hex list
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from typing import List, Optional
|
|
15
|
+
|
|
16
|
+
import matplotlib.colors as mcolors
|
|
17
|
+
import matplotlib.pyplot as plt
|
|
18
|
+
from cycler import cycler
|
|
19
|
+
from matplotlib.axes import Axes
|
|
20
|
+
|
|
21
|
+
# Qualitative palettes. Hex values mirror the canonical ggsci collections,
|
|
22
|
+
# which are widely used in Nature/Science/Cell-family figures.
|
|
23
|
+
PALETTES = {
|
|
24
|
+
# Nature Publishing Group
|
|
25
|
+
"nature": [
|
|
26
|
+
"#E64B35", "#4DBBD5", "#00A087", "#3C5488", "#F39B7F",
|
|
27
|
+
"#8491B4", "#91D1C2", "#DC0000", "#7E6148", "#B09C85",
|
|
28
|
+
],
|
|
29
|
+
# Science / AAAS
|
|
30
|
+
"science": [
|
|
31
|
+
"#3B4992", "#EE0000", "#008B45", "#631879", "#008280",
|
|
32
|
+
"#BB0021", "#5F559B", "#A20056", "#808180", "#1B1919",
|
|
33
|
+
],
|
|
34
|
+
# New England Journal of Medicine
|
|
35
|
+
"nejm": [
|
|
36
|
+
"#BC3C29", "#0072B5", "#E18727", "#20854E", "#7876B1",
|
|
37
|
+
"#6F99AD", "#FFDC91", "#EE4C97",
|
|
38
|
+
],
|
|
39
|
+
# The Lancet
|
|
40
|
+
"lancet": [
|
|
41
|
+
"#00468B", "#ED0000", "#42B540", "#0099B4", "#925E9F",
|
|
42
|
+
"#FDAF91", "#AD002A", "#ADB6B6", "#1B1919",
|
|
43
|
+
],
|
|
44
|
+
# JAMA
|
|
45
|
+
"jama": [
|
|
46
|
+
"#374E55", "#DF8F44", "#00A1D5", "#B24745", "#79AF97",
|
|
47
|
+
"#6A6599", "#80796B",
|
|
48
|
+
],
|
|
49
|
+
# Low-saturation "muted" set, close to Nature Machine Intelligence aesthetics.
|
|
50
|
+
"muted": [
|
|
51
|
+
"#4C72B0", "#DD8452", "#55A868", "#C44E52", "#8172B3",
|
|
52
|
+
"#937860", "#DA8BC3", "#8C8C8C", "#CCB974", "#64B5CD",
|
|
53
|
+
],
|
|
54
|
+
# Semantic order: blue=proposed/hero, green=gain, red=baseline/drop,
|
|
55
|
+
# teal/violet=extra signal, grey=neutral. See SEMANTIC for role lookup.
|
|
56
|
+
"semantic": [
|
|
57
|
+
"#0F4D92", "#8BCF8B", "#B64342", "#42949E", "#9A4D8E", "#CFCECE",
|
|
58
|
+
],
|
|
59
|
+
# Nature-Machine-Intelligence pastel family: cool baselines + warm hero.
|
|
60
|
+
"nmi": [
|
|
61
|
+
"#484878", "#7884B4", "#B4C0E4", "#F0C0CC", "#E4CCD8", "#CFCECE",
|
|
62
|
+
],
|
|
63
|
+
# Many-method comparison (e.g. a bar chart of 8-10 baselines vs. yours):
|
|
64
|
+
# soft low-saturation baselines, related methods share a hue family with
|
|
65
|
+
# graduated lightness, and the proposed method gets the one saturated hero.
|
|
66
|
+
# Put your method LAST so #3775BA lands on it.
|
|
67
|
+
"comparison": [
|
|
68
|
+
"#CFCECE", "#F4EEAC", "#FBDFE2", "#D9B9D4", "#DAA87C",
|
|
69
|
+
"#DDF3DE", "#AADCA9", "#8BCF8B", "#92E3F9", "#3775BA",
|
|
70
|
+
],
|
|
71
|
+
# Imaging / microscopy: a couple of fluorescent accents meant to sit on a
|
|
72
|
+
# BLACK background, plus a grey context channel. Not for white-background plots.
|
|
73
|
+
"imaging": ["#22D7E6", "#FF2AD4", "#B8B8B8"],
|
|
74
|
+
# The original SciencePlots-style bright cycle (kept for backwards compat).
|
|
75
|
+
"bright": [
|
|
76
|
+
"#0C5DA5", "#00B945", "#FF9500", "#FF2C00", "#845B97",
|
|
77
|
+
"#474747", "#9e9e9e",
|
|
78
|
+
],
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# Role-based ("semantic") colours: assign by meaning, never remap a method to a
|
|
82
|
+
# different hue family across panels. Reduce saturation before adding categories.
|
|
83
|
+
SEMANTIC = {
|
|
84
|
+
"blue_main": "#0F4D92", # proposed / hero method
|
|
85
|
+
"blue_secondary": "#3775BA",
|
|
86
|
+
"green_light": "#DDF3DE",
|
|
87
|
+
"green_mid": "#AADCA9",
|
|
88
|
+
"green_strong": "#8BCF8B", # gain / improvement / "delta up"
|
|
89
|
+
"red_light": "#F6CFCB",
|
|
90
|
+
"red_mid": "#E9A6A1",
|
|
91
|
+
"red_strong": "#B64342", # baseline / drop / "delta down"
|
|
92
|
+
"neutral_light": "#CFCECE",
|
|
93
|
+
"neutral_mid": "#767676",
|
|
94
|
+
"neutral_dark": "#4D4D4D",
|
|
95
|
+
"neutral_black": "#272727",
|
|
96
|
+
"accent_gold": "#FFD700",
|
|
97
|
+
"accent_teal": "#42949E",
|
|
98
|
+
"accent_violet": "#9A4D8E",
|
|
99
|
+
"accent_magenta": "#EA84DD",
|
|
100
|
+
"delta_up": "#2E9E44",
|
|
101
|
+
"delta_down": "#E53935",
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# Recommended perceptually-uniform colormaps for continuous data (heatmaps,
|
|
105
|
+
# images, density). Pass these names to ``cmap=`` in imshow/pcolormesh/etc.
|
|
106
|
+
COLORMAPS = {
|
|
107
|
+
"sequential": "viridis", # single-direction magnitude; also "mako", "cividis"
|
|
108
|
+
"sequential_warm": "magma",
|
|
109
|
+
"diverging": "RdBu_r", # signed values around a midpoint; also "coolwarm"
|
|
110
|
+
"grayscale": "Greys", # print-safe context layers
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
DEFAULT_PALETTE = "nature"
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def get_palette(name: str = DEFAULT_PALETTE, n: Optional[int] = None) -> List[str]:
|
|
117
|
+
"""Return the list of hex colors for ``name``.
|
|
118
|
+
|
|
119
|
+
If ``n`` is given, the palette is truncated (or cycled) to that length.
|
|
120
|
+
"""
|
|
121
|
+
if name not in PALETTES:
|
|
122
|
+
raise ValueError(
|
|
123
|
+
f"Unknown palette {name!r}. Available: {', '.join(sorted(PALETTES))}"
|
|
124
|
+
)
|
|
125
|
+
colors = PALETTES[name]
|
|
126
|
+
if n is None:
|
|
127
|
+
return list(colors)
|
|
128
|
+
return [colors[i % len(colors)] for i in range(n)]
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def alpha_ramp(color: str, n: int, lo: float = 0.2, hi: float = 1.0) -> List[tuple]:
|
|
132
|
+
"""One hue at ``n`` graduated opacities — the standard *ablation* encoding.
|
|
133
|
+
|
|
134
|
+
Use a single color and let opacity carry the ordering (e.g. progressively
|
|
135
|
+
more complete model variants), rather than introducing more hues. Returns a
|
|
136
|
+
list of RGBA tuples ready to pass to ``color=`` in bar/line calls.
|
|
137
|
+
|
|
138
|
+
>>> alpha_ramp(epl.SEMANTIC["blue_main"], 3) # light -> solid
|
|
139
|
+
"""
|
|
140
|
+
r, g, b = mcolors.to_rgb(color)
|
|
141
|
+
if n <= 1:
|
|
142
|
+
return [(r, g, b, hi)]
|
|
143
|
+
return [(r, g, b, lo + (hi - lo) * i / (n - 1)) for i in range(n)]
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def shades(color: str, n: int, light: float = 0.82) -> List[str]:
|
|
147
|
+
"""``n`` shades of one hue from light to the base color.
|
|
148
|
+
|
|
149
|
+
Mirrors the "related methods share a hue family with graduated lightness"
|
|
150
|
+
rule (e.g. a baseline trio dark/mid/soft). The lightest shade is ``color``
|
|
151
|
+
mixed ``light`` of the way toward white; the last shade is ``color`` itself.
|
|
152
|
+
Returns hex strings.
|
|
153
|
+
"""
|
|
154
|
+
base = mcolors.to_rgb(color)
|
|
155
|
+
if n <= 1:
|
|
156
|
+
return [mcolors.to_hex(base)]
|
|
157
|
+
out = []
|
|
158
|
+
for i in range(n):
|
|
159
|
+
t = light * (1 - i / (n - 1)) # light at i=0 -> 0 at i=n-1
|
|
160
|
+
mix = (base[0] * (1 - t) + t, base[1] * (1 - t) + t, base[2] * (1 - t) + t)
|
|
161
|
+
out.append(mcolors.to_hex(mix))
|
|
162
|
+
return out
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def set_palette(name: str = DEFAULT_PALETTE, ax: Optional[Axes] = None) -> List[str]:
|
|
166
|
+
"""Set the qualitative color cycle to ``name``.
|
|
167
|
+
|
|
168
|
+
With no ``ax`` the global :data:`matplotlib.rcParams` cycle is updated so it
|
|
169
|
+
applies to every subsequent figure. Pass an ``ax`` to scope it to a single
|
|
170
|
+
Axes. Returns the colour list that was applied.
|
|
171
|
+
"""
|
|
172
|
+
colors = get_palette(name)
|
|
173
|
+
prop_cycle = cycler("color", colors)
|
|
174
|
+
if ax is None:
|
|
175
|
+
plt.rcParams["axes.prop_cycle"] = prop_cycle
|
|
176
|
+
else:
|
|
177
|
+
ax.set_prop_cycle(prop_cycle)
|
|
178
|
+
return colors
|
|
@@ -30,6 +30,10 @@ lines.linewidth : 1.
|
|
|
30
30
|
lines.markersize: 3
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
# Open frame: drop the top/right spines (Nature default)
|
|
34
|
+
axes.spines.top : False
|
|
35
|
+
axes.spines.right : False
|
|
36
|
+
|
|
33
37
|
# Remove legend frame
|
|
34
38
|
legend.frameon : False
|
|
35
39
|
|
|
@@ -37,6 +41,15 @@ legend.frameon : False
|
|
|
37
41
|
savefig.bbox : tight
|
|
38
42
|
savefig.pad_inches : 0.05
|
|
39
43
|
|
|
44
|
+
# Resolution: crisp on-screen preview, journal-grade rasters
|
|
45
|
+
figure.dpi : 200
|
|
46
|
+
savefig.dpi : 600
|
|
47
|
+
|
|
48
|
+
# Keep text editable in vector exports (Illustrator/Inkscape friendly)
|
|
49
|
+
pdf.fonttype : 42
|
|
50
|
+
ps.fonttype : 42
|
|
51
|
+
svg.fonttype : none
|
|
52
|
+
|
|
40
53
|
# Font sizes
|
|
41
54
|
axes.labelsize: 7
|
|
42
55
|
xtick.labelsize: 7
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: EasyPlotLib
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A simple plotting library
|
|
5
|
+
Home-page: https://github.com/yourusername/EasyPlotLib
|
|
6
|
+
Author: HanYuyang
|
|
7
|
+
Author-email: 17766095120@163.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.6
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Dynamic: author
|
|
13
|
+
Dynamic: author-email
|
|
14
|
+
Dynamic: classifier
|
|
15
|
+
Dynamic: description
|
|
16
|
+
Dynamic: description-content-type
|
|
17
|
+
Dynamic: home-page
|
|
18
|
+
Dynamic: requires-python
|
|
19
|
+
Dynamic: summary
|
|
20
|
+
|
|
21
|
+
# EasyPlotLib
|
|
22
|
+
|
|
23
|
+
Matplotlib styles and helpers for publication-quality SCI-journal figures
|
|
24
|
+
(Nature / Science / Cell / NEJM / Lancet / PNAS / AGU / AMS).
|
|
25
|
+
|
|
26
|
+
<p align="center">
|
|
27
|
+
<img src="examples/gallery/01_bar_comparison.png" height="150">
|
|
28
|
+
<img src="examples/gallery/03_line_trend.png" height="150">
|
|
29
|
+
<img src="examples/gallery/07_distributions.png" height="150">
|
|
30
|
+
</p>
|
|
31
|
+
<p align="center"><sub>Built with the bundled <code>nature</code> style — see the full <a href="#chart-type-gallery">gallery</a>.</sub></p>
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
pip install EasyPlotLib
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick start
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import EasyPlotLib as epl
|
|
43
|
+
import matplotlib.pyplot as plt
|
|
44
|
+
import numpy as np
|
|
45
|
+
|
|
46
|
+
# Style + journal column width + color palette in one call.
|
|
47
|
+
epl.journal_style("nat1", palette="nature", nrows=1, ncols=2)
|
|
48
|
+
|
|
49
|
+
fig, axs = plt.subplots(1, 2)
|
|
50
|
+
x = np.linspace(0, 2 * np.pi, 200)
|
|
51
|
+
for n, ax in enumerate(axs):
|
|
52
|
+
for k in range(3):
|
|
53
|
+
ax.plot(x, np.sin(x + k), label=f"series {k}")
|
|
54
|
+
ax.set_xlabel("x")
|
|
55
|
+
ax.set_ylabel("amplitude")
|
|
56
|
+
ax.annotate(**epl.subplot_labels(n, "a")) # bold panel labels a, b …
|
|
57
|
+
|
|
58
|
+
# Editable vector (PDF) + 600-dpi raster.
|
|
59
|
+
epl.save_pub(fig, "figures/fig1", formats=("pdf", "png"))
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## API
|
|
63
|
+
|
|
64
|
+
| Function | Description |
|
|
65
|
+
|----------|-------------|
|
|
66
|
+
| `journal_style(key, palette=, base_style=, nrows=, ncols=, apply=)` | Apply style + size + palette in one call. Returns the figsize dict. |
|
|
67
|
+
| `figsizes(key, ...)` | rcParams dict with a journal column figure size. Keys: `nat1/2`, `aaas1/2`, `pnas1..3`, `agu1..4`, `ams1..4`. |
|
|
68
|
+
| `subplot_labels(n, style)` | Args for `ax.annotate(**...)`. Styles: `a`, `A`, `(a)`, `a)`, `a.` |
|
|
69
|
+
| `set_palette(name, ax=None)` / `get_palette(name, n=)` | Set the color cycle / return raw hex list. |
|
|
70
|
+
| `PALETTES` | Qualitative palettes: `nature`, `science`, `nejm`, `lancet`, `jama`, `muted`, `semantic`, `nmi`, `comparison`, `imaging`, `bright`. |
|
|
71
|
+
| `SEMANTIC` | Role-based colors (`blue_main`=hero, `green_strong`=gain, `red_strong`=drop, `delta_up/down`, neutrals, accents). |
|
|
72
|
+
| `shades(color, n)` | `n` shades of one hue, light→base — related methods (e.g. a baseline trio, stacked-area family). |
|
|
73
|
+
| `alpha_ramp(color, n)` | One hue at graduated opacity — the standard ablation encoding. |
|
|
74
|
+
| `COLORMAPS` | Recommended continuous colormaps for heatmaps/images: `sequential`, `sequential_warm`, `diverging`, `grayscale`. |
|
|
75
|
+
| `save_pub(fig, path, formats=, dpi=)` | Save to multiple formats with editable vector text. |
|
|
76
|
+
| `cartopy_plot_tickmarks(ax, gl)` | Clean lon/lat tick labels for cartopy maps. |
|
|
77
|
+
|
|
78
|
+
## Color scheme guidance
|
|
79
|
+
Every recurring nature-style color pattern is abstracted into one small, shared
|
|
80
|
+
API so a whole figure stays consistent (the `examples/gallery.py` panels all
|
|
81
|
+
draw from it):
|
|
82
|
+
|
|
83
|
+
- **One restrained palette per figure.** Use a unified family across panels
|
|
84
|
+
rather than maximizing hue variety; reduce saturation before adding categories.
|
|
85
|
+
- **Assign color by meaning, not by index.** `SEMANTIC` reserves `blue_main`
|
|
86
|
+
for the proposed/hero method and green/red for gains/drops. Never remap the
|
|
87
|
+
same method to a different hue in another panel.
|
|
88
|
+
- **Many-method comparison** → `get_palette("comparison")`: soft pastel
|
|
89
|
+
baselines with related methods sharing a hue family, and one saturated hero
|
|
90
|
+
(put your method last).
|
|
91
|
+
- **Related methods / one family** → `shades(color, n)`: graduated lightness of
|
|
92
|
+
a single hue (baseline trios, stacked-area bands).
|
|
93
|
+
- **Ablations** → `alpha_ramp(color, n)`: one hue, opacity carries the ordering.
|
|
94
|
+
- **Continuous data** (heatmaps, density, images) → a perceptual colormap from
|
|
95
|
+
`COLORMAPS` — sequential for magnitude, diverging for signed values.
|
|
96
|
+
- **Microscopy** → `imaging` accents (cyan/magenta) on a black background.
|
|
97
|
+
|
|
98
|
+
## Chart-type gallery
|
|
99
|
+
`examples/gallery.py` renders one publication-styled panel per common archetype.
|
|
100
|
+
Every panel sources its colors from the abstracted API above, so a method keeps
|
|
101
|
+
its hue across the whole figure (blue = hero, grey = baseline):
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
python examples/gallery.py # writes PNGs to examples/gallery/
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
<table>
|
|
108
|
+
<tr>
|
|
109
|
+
<td align="center"><img src="examples/gallery/01_bar_comparison.png" width="260"><br><sub>Method comparison · <code>get_palette("comparison")</code></sub></td>
|
|
110
|
+
<td align="center"><img src="examples/gallery/02_bar_ablation.png" width="260"><br><sub>Ablation · <code>alpha_ramp()</code></sub></td>
|
|
111
|
+
<td align="center"><img src="examples/gallery/03_line_trend.png" width="260"><br><sub>Trend + CI · <code>SEMANTIC</code></sub></td>
|
|
112
|
+
</tr>
|
|
113
|
+
<tr>
|
|
114
|
+
<td align="center"><img src="examples/gallery/04_heatmap.png" width="260"><br><sub>Heatmap · <code>COLORMAPS["sequential"]</code></sub></td>
|
|
115
|
+
<td align="center"><img src="examples/gallery/05_scatter_bubble.png" width="260"><br><sub>Scatter / bubble · <code>SEMANTIC</code></sub></td>
|
|
116
|
+
<td align="center"><img src="examples/gallery/06_radar.png" width="260"><br><sub>Radar / polar · <code>SEMANTIC</code></sub></td>
|
|
117
|
+
</tr>
|
|
118
|
+
<tr>
|
|
119
|
+
<td align="center"><img src="examples/gallery/07_distributions.png" width="260"><br><sub>Violin + box · <code>shades()</code></sub></td>
|
|
120
|
+
<td align="center"><img src="examples/gallery/08_forest.png" width="260"><br><sub>Forest / interval · <code>SEMANTIC</code></sub></td>
|
|
121
|
+
<td align="center"><img src="examples/gallery/09_stacked_area.png" width="260"><br><sub>Stacked area · <code>shades()</code></sub></td>
|
|
122
|
+
</tr>
|
|
123
|
+
</table>
|
|
124
|
+
|
|
125
|
+
## Style notes
|
|
126
|
+
|
|
127
|
+
The bundled `nature` style uses 7 pt sans (TeX Gyre Heros / Helvetica), an open
|
|
128
|
+
frame (no top/right spines), and export settings that keep text **editable** in
|
|
129
|
+
Illustrator/Inkscape (`pdf.fonttype=42`, `svg.fonttype=none`) at 600 dpi.
|
|
130
|
+
|
|
131
|
+
## Claude Code skill
|
|
132
|
+
|
|
133
|
+
A personal `sci-figure` skill (`~/.claude/skills/sci-figure/`) walks Claude
|
|
134
|
+
through the publication workflow and is auto-invoked on plotting tasks in any
|
|
135
|
+
project where EasyPlotLib is installed.
|
|
@@ -2,7 +2,10 @@ README.md
|
|
|
2
2
|
setup.py
|
|
3
3
|
EasyPlotLib/__init__.py
|
|
4
4
|
EasyPlotLib/cartopy_helper.py
|
|
5
|
+
EasyPlotLib/export.py
|
|
5
6
|
EasyPlotLib/figsizes_set.py
|
|
7
|
+
EasyPlotLib/helpers.py
|
|
8
|
+
EasyPlotLib/palettes.py
|
|
6
9
|
EasyPlotLib.egg-info/PKG-INFO
|
|
7
10
|
EasyPlotLib.egg-info/SOURCES.txt
|
|
8
11
|
EasyPlotLib.egg-info/dependency_links.txt
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: EasyPlotLib
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A simple plotting library
|
|
5
|
+
Home-page: https://github.com/yourusername/EasyPlotLib
|
|
6
|
+
Author: HanYuyang
|
|
7
|
+
Author-email: 17766095120@163.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.6
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Dynamic: author
|
|
13
|
+
Dynamic: author-email
|
|
14
|
+
Dynamic: classifier
|
|
15
|
+
Dynamic: description
|
|
16
|
+
Dynamic: description-content-type
|
|
17
|
+
Dynamic: home-page
|
|
18
|
+
Dynamic: requires-python
|
|
19
|
+
Dynamic: summary
|
|
20
|
+
|
|
21
|
+
# EasyPlotLib
|
|
22
|
+
|
|
23
|
+
Matplotlib styles and helpers for publication-quality SCI-journal figures
|
|
24
|
+
(Nature / Science / Cell / NEJM / Lancet / PNAS / AGU / AMS).
|
|
25
|
+
|
|
26
|
+
<p align="center">
|
|
27
|
+
<img src="examples/gallery/01_bar_comparison.png" height="150">
|
|
28
|
+
<img src="examples/gallery/03_line_trend.png" height="150">
|
|
29
|
+
<img src="examples/gallery/07_distributions.png" height="150">
|
|
30
|
+
</p>
|
|
31
|
+
<p align="center"><sub>Built with the bundled <code>nature</code> style — see the full <a href="#chart-type-gallery">gallery</a>.</sub></p>
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
pip install EasyPlotLib
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick start
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import EasyPlotLib as epl
|
|
43
|
+
import matplotlib.pyplot as plt
|
|
44
|
+
import numpy as np
|
|
45
|
+
|
|
46
|
+
# Style + journal column width + color palette in one call.
|
|
47
|
+
epl.journal_style("nat1", palette="nature", nrows=1, ncols=2)
|
|
48
|
+
|
|
49
|
+
fig, axs = plt.subplots(1, 2)
|
|
50
|
+
x = np.linspace(0, 2 * np.pi, 200)
|
|
51
|
+
for n, ax in enumerate(axs):
|
|
52
|
+
for k in range(3):
|
|
53
|
+
ax.plot(x, np.sin(x + k), label=f"series {k}")
|
|
54
|
+
ax.set_xlabel("x")
|
|
55
|
+
ax.set_ylabel("amplitude")
|
|
56
|
+
ax.annotate(**epl.subplot_labels(n, "a")) # bold panel labels a, b …
|
|
57
|
+
|
|
58
|
+
# Editable vector (PDF) + 600-dpi raster.
|
|
59
|
+
epl.save_pub(fig, "figures/fig1", formats=("pdf", "png"))
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## API
|
|
63
|
+
|
|
64
|
+
| Function | Description |
|
|
65
|
+
|----------|-------------|
|
|
66
|
+
| `journal_style(key, palette=, base_style=, nrows=, ncols=, apply=)` | Apply style + size + palette in one call. Returns the figsize dict. |
|
|
67
|
+
| `figsizes(key, ...)` | rcParams dict with a journal column figure size. Keys: `nat1/2`, `aaas1/2`, `pnas1..3`, `agu1..4`, `ams1..4`. |
|
|
68
|
+
| `subplot_labels(n, style)` | Args for `ax.annotate(**...)`. Styles: `a`, `A`, `(a)`, `a)`, `a.` |
|
|
69
|
+
| `set_palette(name, ax=None)` / `get_palette(name, n=)` | Set the color cycle / return raw hex list. |
|
|
70
|
+
| `PALETTES` | Qualitative palettes: `nature`, `science`, `nejm`, `lancet`, `jama`, `muted`, `semantic`, `nmi`, `comparison`, `imaging`, `bright`. |
|
|
71
|
+
| `SEMANTIC` | Role-based colors (`blue_main`=hero, `green_strong`=gain, `red_strong`=drop, `delta_up/down`, neutrals, accents). |
|
|
72
|
+
| `shades(color, n)` | `n` shades of one hue, light→base — related methods (e.g. a baseline trio, stacked-area family). |
|
|
73
|
+
| `alpha_ramp(color, n)` | One hue at graduated opacity — the standard ablation encoding. |
|
|
74
|
+
| `COLORMAPS` | Recommended continuous colormaps for heatmaps/images: `sequential`, `sequential_warm`, `diverging`, `grayscale`. |
|
|
75
|
+
| `save_pub(fig, path, formats=, dpi=)` | Save to multiple formats with editable vector text. |
|
|
76
|
+
| `cartopy_plot_tickmarks(ax, gl)` | Clean lon/lat tick labels for cartopy maps. |
|
|
77
|
+
|
|
78
|
+
## Color scheme guidance
|
|
79
|
+
Every recurring nature-style color pattern is abstracted into one small, shared
|
|
80
|
+
API so a whole figure stays consistent (the `examples/gallery.py` panels all
|
|
81
|
+
draw from it):
|
|
82
|
+
|
|
83
|
+
- **One restrained palette per figure.** Use a unified family across panels
|
|
84
|
+
rather than maximizing hue variety; reduce saturation before adding categories.
|
|
85
|
+
- **Assign color by meaning, not by index.** `SEMANTIC` reserves `blue_main`
|
|
86
|
+
for the proposed/hero method and green/red for gains/drops. Never remap the
|
|
87
|
+
same method to a different hue in another panel.
|
|
88
|
+
- **Many-method comparison** → `get_palette("comparison")`: soft pastel
|
|
89
|
+
baselines with related methods sharing a hue family, and one saturated hero
|
|
90
|
+
(put your method last).
|
|
91
|
+
- **Related methods / one family** → `shades(color, n)`: graduated lightness of
|
|
92
|
+
a single hue (baseline trios, stacked-area bands).
|
|
93
|
+
- **Ablations** → `alpha_ramp(color, n)`: one hue, opacity carries the ordering.
|
|
94
|
+
- **Continuous data** (heatmaps, density, images) → a perceptual colormap from
|
|
95
|
+
`COLORMAPS` — sequential for magnitude, diverging for signed values.
|
|
96
|
+
- **Microscopy** → `imaging` accents (cyan/magenta) on a black background.
|
|
97
|
+
|
|
98
|
+
## Chart-type gallery
|
|
99
|
+
`examples/gallery.py` renders one publication-styled panel per common archetype.
|
|
100
|
+
Every panel sources its colors from the abstracted API above, so a method keeps
|
|
101
|
+
its hue across the whole figure (blue = hero, grey = baseline):
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
python examples/gallery.py # writes PNGs to examples/gallery/
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
<table>
|
|
108
|
+
<tr>
|
|
109
|
+
<td align="center"><img src="examples/gallery/01_bar_comparison.png" width="260"><br><sub>Method comparison · <code>get_palette("comparison")</code></sub></td>
|
|
110
|
+
<td align="center"><img src="examples/gallery/02_bar_ablation.png" width="260"><br><sub>Ablation · <code>alpha_ramp()</code></sub></td>
|
|
111
|
+
<td align="center"><img src="examples/gallery/03_line_trend.png" width="260"><br><sub>Trend + CI · <code>SEMANTIC</code></sub></td>
|
|
112
|
+
</tr>
|
|
113
|
+
<tr>
|
|
114
|
+
<td align="center"><img src="examples/gallery/04_heatmap.png" width="260"><br><sub>Heatmap · <code>COLORMAPS["sequential"]</code></sub></td>
|
|
115
|
+
<td align="center"><img src="examples/gallery/05_scatter_bubble.png" width="260"><br><sub>Scatter / bubble · <code>SEMANTIC</code></sub></td>
|
|
116
|
+
<td align="center"><img src="examples/gallery/06_radar.png" width="260"><br><sub>Radar / polar · <code>SEMANTIC</code></sub></td>
|
|
117
|
+
</tr>
|
|
118
|
+
<tr>
|
|
119
|
+
<td align="center"><img src="examples/gallery/07_distributions.png" width="260"><br><sub>Violin + box · <code>shades()</code></sub></td>
|
|
120
|
+
<td align="center"><img src="examples/gallery/08_forest.png" width="260"><br><sub>Forest / interval · <code>SEMANTIC</code></sub></td>
|
|
121
|
+
<td align="center"><img src="examples/gallery/09_stacked_area.png" width="260"><br><sub>Stacked area · <code>shades()</code></sub></td>
|
|
122
|
+
</tr>
|
|
123
|
+
</table>
|
|
124
|
+
|
|
125
|
+
## Style notes
|
|
126
|
+
|
|
127
|
+
The bundled `nature` style uses 7 pt sans (TeX Gyre Heros / Helvetica), an open
|
|
128
|
+
frame (no top/right spines), and export settings that keep text **editable** in
|
|
129
|
+
Illustrator/Inkscape (`pdf.fonttype=42`, `svg.fonttype=none`) at 600 dpi.
|
|
130
|
+
|
|
131
|
+
## Claude Code skill
|
|
132
|
+
|
|
133
|
+
A personal `sci-figure` skill (`~/.claude/skills/sci-figure/`) walks Claude
|
|
134
|
+
through the publication workflow and is auto-invoked on plotting tasks in any
|
|
135
|
+
project where EasyPlotLib is installed.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# EasyPlotLib
|
|
2
|
+
|
|
3
|
+
Matplotlib styles and helpers for publication-quality SCI-journal figures
|
|
4
|
+
(Nature / Science / Cell / NEJM / Lancet / PNAS / AGU / AMS).
|
|
5
|
+
|
|
6
|
+
<p align="center">
|
|
7
|
+
<img src="examples/gallery/01_bar_comparison.png" height="150">
|
|
8
|
+
<img src="examples/gallery/03_line_trend.png" height="150">
|
|
9
|
+
<img src="examples/gallery/07_distributions.png" height="150">
|
|
10
|
+
</p>
|
|
11
|
+
<p align="center"><sub>Built with the bundled <code>nature</code> style — see the full <a href="#chart-type-gallery">gallery</a>.</sub></p>
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
pip install EasyPlotLib
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
import EasyPlotLib as epl
|
|
23
|
+
import matplotlib.pyplot as plt
|
|
24
|
+
import numpy as np
|
|
25
|
+
|
|
26
|
+
# Style + journal column width + color palette in one call.
|
|
27
|
+
epl.journal_style("nat1", palette="nature", nrows=1, ncols=2)
|
|
28
|
+
|
|
29
|
+
fig, axs = plt.subplots(1, 2)
|
|
30
|
+
x = np.linspace(0, 2 * np.pi, 200)
|
|
31
|
+
for n, ax in enumerate(axs):
|
|
32
|
+
for k in range(3):
|
|
33
|
+
ax.plot(x, np.sin(x + k), label=f"series {k}")
|
|
34
|
+
ax.set_xlabel("x")
|
|
35
|
+
ax.set_ylabel("amplitude")
|
|
36
|
+
ax.annotate(**epl.subplot_labels(n, "a")) # bold panel labels a, b …
|
|
37
|
+
|
|
38
|
+
# Editable vector (PDF) + 600-dpi raster.
|
|
39
|
+
epl.save_pub(fig, "figures/fig1", formats=("pdf", "png"))
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## API
|
|
43
|
+
|
|
44
|
+
| Function | Description |
|
|
45
|
+
|----------|-------------|
|
|
46
|
+
| `journal_style(key, palette=, base_style=, nrows=, ncols=, apply=)` | Apply style + size + palette in one call. Returns the figsize dict. |
|
|
47
|
+
| `figsizes(key, ...)` | rcParams dict with a journal column figure size. Keys: `nat1/2`, `aaas1/2`, `pnas1..3`, `agu1..4`, `ams1..4`. |
|
|
48
|
+
| `subplot_labels(n, style)` | Args for `ax.annotate(**...)`. Styles: `a`, `A`, `(a)`, `a)`, `a.` |
|
|
49
|
+
| `set_palette(name, ax=None)` / `get_palette(name, n=)` | Set the color cycle / return raw hex list. |
|
|
50
|
+
| `PALETTES` | Qualitative palettes: `nature`, `science`, `nejm`, `lancet`, `jama`, `muted`, `semantic`, `nmi`, `comparison`, `imaging`, `bright`. |
|
|
51
|
+
| `SEMANTIC` | Role-based colors (`blue_main`=hero, `green_strong`=gain, `red_strong`=drop, `delta_up/down`, neutrals, accents). |
|
|
52
|
+
| `shades(color, n)` | `n` shades of one hue, light→base — related methods (e.g. a baseline trio, stacked-area family). |
|
|
53
|
+
| `alpha_ramp(color, n)` | One hue at graduated opacity — the standard ablation encoding. |
|
|
54
|
+
| `COLORMAPS` | Recommended continuous colormaps for heatmaps/images: `sequential`, `sequential_warm`, `diverging`, `grayscale`. |
|
|
55
|
+
| `save_pub(fig, path, formats=, dpi=)` | Save to multiple formats with editable vector text. |
|
|
56
|
+
| `cartopy_plot_tickmarks(ax, gl)` | Clean lon/lat tick labels for cartopy maps. |
|
|
57
|
+
|
|
58
|
+
## Color scheme guidance
|
|
59
|
+
Every recurring nature-style color pattern is abstracted into one small, shared
|
|
60
|
+
API so a whole figure stays consistent (the `examples/gallery.py` panels all
|
|
61
|
+
draw from it):
|
|
62
|
+
|
|
63
|
+
- **One restrained palette per figure.** Use a unified family across panels
|
|
64
|
+
rather than maximizing hue variety; reduce saturation before adding categories.
|
|
65
|
+
- **Assign color by meaning, not by index.** `SEMANTIC` reserves `blue_main`
|
|
66
|
+
for the proposed/hero method and green/red for gains/drops. Never remap the
|
|
67
|
+
same method to a different hue in another panel.
|
|
68
|
+
- **Many-method comparison** → `get_palette("comparison")`: soft pastel
|
|
69
|
+
baselines with related methods sharing a hue family, and one saturated hero
|
|
70
|
+
(put your method last).
|
|
71
|
+
- **Related methods / one family** → `shades(color, n)`: graduated lightness of
|
|
72
|
+
a single hue (baseline trios, stacked-area bands).
|
|
73
|
+
- **Ablations** → `alpha_ramp(color, n)`: one hue, opacity carries the ordering.
|
|
74
|
+
- **Continuous data** (heatmaps, density, images) → a perceptual colormap from
|
|
75
|
+
`COLORMAPS` — sequential for magnitude, diverging for signed values.
|
|
76
|
+
- **Microscopy** → `imaging` accents (cyan/magenta) on a black background.
|
|
77
|
+
|
|
78
|
+
## Chart-type gallery
|
|
79
|
+
`examples/gallery.py` renders one publication-styled panel per common archetype.
|
|
80
|
+
Every panel sources its colors from the abstracted API above, so a method keeps
|
|
81
|
+
its hue across the whole figure (blue = hero, grey = baseline):
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
python examples/gallery.py # writes PNGs to examples/gallery/
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
<table>
|
|
88
|
+
<tr>
|
|
89
|
+
<td align="center"><img src="examples/gallery/01_bar_comparison.png" width="260"><br><sub>Method comparison · <code>get_palette("comparison")</code></sub></td>
|
|
90
|
+
<td align="center"><img src="examples/gallery/02_bar_ablation.png" width="260"><br><sub>Ablation · <code>alpha_ramp()</code></sub></td>
|
|
91
|
+
<td align="center"><img src="examples/gallery/03_line_trend.png" width="260"><br><sub>Trend + CI · <code>SEMANTIC</code></sub></td>
|
|
92
|
+
</tr>
|
|
93
|
+
<tr>
|
|
94
|
+
<td align="center"><img src="examples/gallery/04_heatmap.png" width="260"><br><sub>Heatmap · <code>COLORMAPS["sequential"]</code></sub></td>
|
|
95
|
+
<td align="center"><img src="examples/gallery/05_scatter_bubble.png" width="260"><br><sub>Scatter / bubble · <code>SEMANTIC</code></sub></td>
|
|
96
|
+
<td align="center"><img src="examples/gallery/06_radar.png" width="260"><br><sub>Radar / polar · <code>SEMANTIC</code></sub></td>
|
|
97
|
+
</tr>
|
|
98
|
+
<tr>
|
|
99
|
+
<td align="center"><img src="examples/gallery/07_distributions.png" width="260"><br><sub>Violin + box · <code>shades()</code></sub></td>
|
|
100
|
+
<td align="center"><img src="examples/gallery/08_forest.png" width="260"><br><sub>Forest / interval · <code>SEMANTIC</code></sub></td>
|
|
101
|
+
<td align="center"><img src="examples/gallery/09_stacked_area.png" width="260"><br><sub>Stacked area · <code>shades()</code></sub></td>
|
|
102
|
+
</tr>
|
|
103
|
+
</table>
|
|
104
|
+
|
|
105
|
+
## Style notes
|
|
106
|
+
|
|
107
|
+
The bundled `nature` style uses 7 pt sans (TeX Gyre Heros / Helvetica), an open
|
|
108
|
+
frame (no top/right spines), and export settings that keep text **editable** in
|
|
109
|
+
Illustrator/Inkscape (`pdf.fonttype=42`, `svg.fonttype=none`) at 600 dpi.
|
|
110
|
+
|
|
111
|
+
## Claude Code skill
|
|
112
|
+
|
|
113
|
+
A personal `sci-figure` skill (`~/.claude/skills/sci-figure/`) walks Claude
|
|
114
|
+
through the publication workflow and is auto-invoked on plotting tasks in any
|
|
115
|
+
project where EasyPlotLib is installed.
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.2
|
|
2
|
-
Name: EasyPlotLib
|
|
3
|
-
Version: 0.1.1
|
|
4
|
-
Summary: A simple plotting library
|
|
5
|
-
Home-page: https://github.com/yourusername/EasyPlotLib
|
|
6
|
-
Author: HanYuyang
|
|
7
|
-
Author-email: 17766095120@163.com
|
|
8
|
-
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: Operating System :: OS Independent
|
|
10
|
-
Requires-Python: >=3.6
|
|
11
|
-
Description-Content-Type: text/markdown
|
|
12
|
-
Dynamic: author
|
|
13
|
-
Dynamic: author-email
|
|
14
|
-
Dynamic: classifier
|
|
15
|
-
Dynamic: description
|
|
16
|
-
Dynamic: description-content-type
|
|
17
|
-
Dynamic: home-page
|
|
18
|
-
Dynamic: requires-python
|
|
19
|
-
Dynamic: summary
|
|
20
|
-
|
|
21
|
-
# Install
|
|
22
|
-
pip install EasyPlotLib
|
easyplotlib-0.1.1/PKG-INFO
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.2
|
|
2
|
-
Name: EasyPlotLib
|
|
3
|
-
Version: 0.1.1
|
|
4
|
-
Summary: A simple plotting library
|
|
5
|
-
Home-page: https://github.com/yourusername/EasyPlotLib
|
|
6
|
-
Author: HanYuyang
|
|
7
|
-
Author-email: 17766095120@163.com
|
|
8
|
-
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: Operating System :: OS Independent
|
|
10
|
-
Requires-Python: >=3.6
|
|
11
|
-
Description-Content-Type: text/markdown
|
|
12
|
-
Dynamic: author
|
|
13
|
-
Dynamic: author-email
|
|
14
|
-
Dynamic: classifier
|
|
15
|
-
Dynamic: description
|
|
16
|
-
Dynamic: description-content-type
|
|
17
|
-
Dynamic: home-page
|
|
18
|
-
Dynamic: requires-python
|
|
19
|
-
Dynamic: summary
|
|
20
|
-
|
|
21
|
-
# Install
|
|
22
|
-
pip install EasyPlotLib
|
easyplotlib-0.1.1/README.md
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|