pub-ready-plots 0.1.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.
- pub_ready_plots-0.1.0/PKG-INFO +76 -0
- pub_ready_plots-0.1.0/README.md +66 -0
- pub_ready_plots-0.1.0/pyproject.toml +29 -0
- pub_ready_plots-0.1.0/src/pub_ready_plots/__init__.py +3 -0
- pub_ready_plots-0.1.0/src/pub_ready_plots/pub_ready_plots.py +167 -0
- pub_ready_plots-0.1.0/tests/__init__.py +0 -0
- pub_ready_plots-0.1.0/tests/test_core_func.py +39 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: pub-ready-plots
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Easy publication-ready matplotlib plots for ML papers and posters.
|
|
5
|
+
Author-Email: Agustinus Kristiadi <agustinus@kristia.de>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: matplotlib>=3.9.0
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# Agustinus' _Very_ Opiniated Publication-Ready Plotting Library
|
|
12
|
+
|
|
13
|
+
I love TikZ. I love [tikzplotlib](https://github.com/nschloe/tikzplotlib). I've been an
|
|
14
|
+
advocate for the latter ([proof](https://agustinus.kristia.de/techblog/2022/05/01/plotting/)). However, tikzplotlib is [as good as dead](https://github.com/nschloe/tikzplotlib/commits/main/). I need to move on.
|
|
15
|
+
|
|
16
|
+
> [!IMPORTANT]
|
|
17
|
+
> Here's what I use now for all my publication needs. This library is designed to be
|
|
18
|
+
> _*very*_ opiniated. Beauty is in the eye of the beholder. Also, it is _very_ simple,
|
|
19
|
+
> just a single file and that's it.
|
|
20
|
+
|
|
21
|
+
> [!NOTE]
|
|
22
|
+
> Of course I still use TikZ whenever possible (e.g. Fig. 1 in a paper, diagrams, etc.)
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
Still with me? Still want to use this library? Here's how:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install pub-ready-plots
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
rc_params, fig_width_in, fig_height_in = pub_ready_plots.get_mpl_rcParams(
|
|
36
|
+
width=1, # between 0 and 1
|
|
37
|
+
height=0.1, # between 0 and 1
|
|
38
|
+
layout="icml" # or "iclr", "neurips", "poster-portrait", "poster-landscape"
|
|
39
|
+
)
|
|
40
|
+
plt.rcParams.update(rc_params)
|
|
41
|
+
|
|
42
|
+
fig, axs = plt.subplots(
|
|
43
|
+
nrows,
|
|
44
|
+
ncols,
|
|
45
|
+
constrained_layout=True, # Important!
|
|
46
|
+
)
|
|
47
|
+
fig.set_size_inches(fig_width_in, fig_height_in)
|
|
48
|
+
|
|
49
|
+
# Your plot here!
|
|
50
|
+
|
|
51
|
+
plt.savefig("filename.pdf")
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Then in your LaTeX file, include the plot as follows:
|
|
55
|
+
|
|
56
|
+
```tex
|
|
57
|
+
\includegraphics[width=\linewidth]{filename.pdf}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The argument `width=\linewidth` is important!
|
|
61
|
+
|
|
62
|
+
> [!TIP]
|
|
63
|
+
> That's it!
|
|
64
|
+
|
|
65
|
+
## Using your own styles
|
|
66
|
+
|
|
67
|
+
Two options:
|
|
68
|
+
|
|
69
|
+
1. For this repo and modify things as you wish.
|
|
70
|
+
2. Use this library and update the resulting `rc_params` dict with your styles.
|
|
71
|
+
|
|
72
|
+
## Other libraries
|
|
73
|
+
|
|
74
|
+
Check out [tueplots](https://github.com/pnkraemer/tueplots) if you want a more complex
|
|
75
|
+
library. My library is designed to achieve what I want in publication-ready papers and
|
|
76
|
+
posters, with as little code as possible (minimalism FTW!).
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Agustinus' _Very_ Opiniated Publication-Ready Plotting Library
|
|
2
|
+
|
|
3
|
+
I love TikZ. I love [tikzplotlib](https://github.com/nschloe/tikzplotlib). I've been an
|
|
4
|
+
advocate for the latter ([proof](https://agustinus.kristia.de/techblog/2022/05/01/plotting/)). However, tikzplotlib is [as good as dead](https://github.com/nschloe/tikzplotlib/commits/main/). I need to move on.
|
|
5
|
+
|
|
6
|
+
> [!IMPORTANT]
|
|
7
|
+
> Here's what I use now for all my publication needs. This library is designed to be
|
|
8
|
+
> _*very*_ opiniated. Beauty is in the eye of the beholder. Also, it is _very_ simple,
|
|
9
|
+
> just a single file and that's it.
|
|
10
|
+
|
|
11
|
+
> [!NOTE]
|
|
12
|
+
> Of course I still use TikZ whenever possible (e.g. Fig. 1 in a paper, diagrams, etc.)
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
Still with me? Still want to use this library? Here's how:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install pub-ready-plots
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
rc_params, fig_width_in, fig_height_in = pub_ready_plots.get_mpl_rcParams(
|
|
26
|
+
width=1, # between 0 and 1
|
|
27
|
+
height=0.1, # between 0 and 1
|
|
28
|
+
layout="icml" # or "iclr", "neurips", "poster-portrait", "poster-landscape"
|
|
29
|
+
)
|
|
30
|
+
plt.rcParams.update(rc_params)
|
|
31
|
+
|
|
32
|
+
fig, axs = plt.subplots(
|
|
33
|
+
nrows,
|
|
34
|
+
ncols,
|
|
35
|
+
constrained_layout=True, # Important!
|
|
36
|
+
)
|
|
37
|
+
fig.set_size_inches(fig_width_in, fig_height_in)
|
|
38
|
+
|
|
39
|
+
# Your plot here!
|
|
40
|
+
|
|
41
|
+
plt.savefig("filename.pdf")
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Then in your LaTeX file, include the plot as follows:
|
|
45
|
+
|
|
46
|
+
```tex
|
|
47
|
+
\includegraphics[width=\linewidth]{filename.pdf}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The argument `width=\linewidth` is important!
|
|
51
|
+
|
|
52
|
+
> [!TIP]
|
|
53
|
+
> That's it!
|
|
54
|
+
|
|
55
|
+
## Using your own styles
|
|
56
|
+
|
|
57
|
+
Two options:
|
|
58
|
+
|
|
59
|
+
1. For this repo and modify things as you wish.
|
|
60
|
+
2. Use this library and update the resulting `rc_params` dict with your styles.
|
|
61
|
+
|
|
62
|
+
## Other libraries
|
|
63
|
+
|
|
64
|
+
Check out [tueplots](https://github.com/pnkraemer/tueplots) if you want a more complex
|
|
65
|
+
library. My library is designed to achieve what I want in publication-ready papers and
|
|
66
|
+
posters, with as little code as possible (minimalism FTW!).
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pub-ready-plots"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Easy publication-ready matplotlib plots for ML papers and posters."
|
|
5
|
+
authors = [
|
|
6
|
+
{ name = "Agustinus Kristiadi", email = "agustinus@kristia.de" },
|
|
7
|
+
]
|
|
8
|
+
dependencies = [
|
|
9
|
+
"matplotlib>=3.9.0",
|
|
10
|
+
]
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
|
|
14
|
+
[project.license]
|
|
15
|
+
text = "MIT"
|
|
16
|
+
|
|
17
|
+
[build-system]
|
|
18
|
+
requires = [
|
|
19
|
+
"pdm-backend",
|
|
20
|
+
]
|
|
21
|
+
build-backend = "pdm.backend"
|
|
22
|
+
|
|
23
|
+
[tool.pdm]
|
|
24
|
+
distribution = true
|
|
25
|
+
|
|
26
|
+
[tool.pytest.ini_options]
|
|
27
|
+
pythonpath = [
|
|
28
|
+
"src",
|
|
29
|
+
]
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
import matplotlib as mpl
|
|
4
|
+
import matplotlib.font_manager as font_manager
|
|
5
|
+
|
|
6
|
+
cmfont = font_manager.FontProperties(fname=mpl.get_data_path() + "/fonts/ttf/cmr10.ttf")
|
|
7
|
+
FONT_NAME_CM = cmfont.get_name()
|
|
8
|
+
FONT_NAME_TNR = "Times New Roman"
|
|
9
|
+
FONT_NAME_AVENIR = "Avenir Next Condensed"
|
|
10
|
+
|
|
11
|
+
PAPER_FORMATS = {
|
|
12
|
+
"icml": {
|
|
13
|
+
"text_width": 6.00117,
|
|
14
|
+
"col_width": 3.25063,
|
|
15
|
+
"text_height": 8.50166,
|
|
16
|
+
"font_name": FONT_NAME_TNR,
|
|
17
|
+
"footnote_size": 8,
|
|
18
|
+
"script_size": 7,
|
|
19
|
+
"linewidth": 1.25,
|
|
20
|
+
"tick_size": 1,
|
|
21
|
+
"tick_width": 1,
|
|
22
|
+
},
|
|
23
|
+
"neurips": {
|
|
24
|
+
"text_width": 5.50107,
|
|
25
|
+
"col_width": 5.50107,
|
|
26
|
+
"text_height": 9.00177,
|
|
27
|
+
"font_name": FONT_NAME_TNR,
|
|
28
|
+
"footnote_size": 8,
|
|
29
|
+
"script_size": 7,
|
|
30
|
+
"linewidth": 1.25,
|
|
31
|
+
"tick_size": 1,
|
|
32
|
+
"tick_width": 1,
|
|
33
|
+
},
|
|
34
|
+
"iclr": {
|
|
35
|
+
"text_width": 5.50107,
|
|
36
|
+
"col_width": 5.50107,
|
|
37
|
+
"text_height": 9.00177,
|
|
38
|
+
"font_name": FONT_NAME_TNR,
|
|
39
|
+
"footnote_size": 8,
|
|
40
|
+
"script_size": 7,
|
|
41
|
+
"linewidth": 1.25,
|
|
42
|
+
"tick_size": 1,
|
|
43
|
+
"tick_width": 1,
|
|
44
|
+
},
|
|
45
|
+
"jmlr": {
|
|
46
|
+
"text_width": 6.00117,
|
|
47
|
+
"col_width": 6.00117,
|
|
48
|
+
"text_height": 8.50166,
|
|
49
|
+
"font_name": FONT_NAME_CM,
|
|
50
|
+
"footnote_size": 8,
|
|
51
|
+
"script_size": 7,
|
|
52
|
+
"linewidth": 1.25,
|
|
53
|
+
"tick_size": 1,
|
|
54
|
+
"tick_width": 1,
|
|
55
|
+
},
|
|
56
|
+
"poster-landscape": {
|
|
57
|
+
"text_width": 6.00117,
|
|
58
|
+
"col_width": 6.00117,
|
|
59
|
+
"text_height": 8.50166,
|
|
60
|
+
"font_name": FONT_NAME_AVENIR,
|
|
61
|
+
"footnote_size": 30,
|
|
62
|
+
"script_size": 23,
|
|
63
|
+
"linewidth": 3,
|
|
64
|
+
"tick_size": 4,
|
|
65
|
+
"tick_width": 2,
|
|
66
|
+
},
|
|
67
|
+
"poster-portrait": {
|
|
68
|
+
"text_width": 6.00117,
|
|
69
|
+
"col_width": 6.00117,
|
|
70
|
+
"text_height": 8.50166,
|
|
71
|
+
"font_name": FONT_NAME_AVENIR,
|
|
72
|
+
"footnote_size": 10,
|
|
73
|
+
"script_size": 8,
|
|
74
|
+
"linewidth": 1,
|
|
75
|
+
"tick_size": 1,
|
|
76
|
+
"tick_width": 1,
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def get_mpl_rcParams(
|
|
82
|
+
width_frac: float,
|
|
83
|
+
height_frac: float,
|
|
84
|
+
layout: str = "neurips",
|
|
85
|
+
single_col: bool = False,
|
|
86
|
+
) -> tuple[dict[str, Any], float, float]:
|
|
87
|
+
"""Get matplotlib rcParams dict and fig width & height in inches, depending on the
|
|
88
|
+
chosen layout and fractional width and height. Fractional here in the sense that
|
|
89
|
+
The resulting fig width/height in inches is calculated as `width_frac\\linewidth` and
|
|
90
|
+
`height_frac\\textheight` in LaTeX. Usage:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
rc_params, fig_width_in, fig_height_in = pub_ready_plots.get_mpl_rcParams(
|
|
94
|
+
width=fig_width_frac, height=fig_height_frac, layout="icml"
|
|
95
|
+
)
|
|
96
|
+
plt.rcParams.update(rc_params)
|
|
97
|
+
|
|
98
|
+
fig, axs = plt.subplots(
|
|
99
|
+
nrows,
|
|
100
|
+
ncols,
|
|
101
|
+
constrained_layout=True, # Important!
|
|
102
|
+
)
|
|
103
|
+
fig.set_size_inches(fig_width_in, fig_height_in)
|
|
104
|
+
|
|
105
|
+
# Your plot here!
|
|
106
|
+
|
|
107
|
+
plt.savefig("filename.pdf")
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Then in your LaTeX file, include the plot as follows:
|
|
111
|
+
|
|
112
|
+
```tex
|
|
113
|
+
\\includegraphics[width=\\linewidth]{filename.pdf}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The arg. `width=\\linewidth` is important!
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
width_frac: Fraction of `\\linewidth` as the figure width. Usually set to 1.
|
|
120
|
+
height_frac: Fraction of `\\textheight` as the figure height. Try 0.175.
|
|
121
|
+
layout: The LaTeX template used. Possible values are "icml", "iclr", "neurips",
|
|
122
|
+
"jmlr", "poster-portrait" (A1, 2-column), and "poster-landscape" (A0, 3-col).
|
|
123
|
+
single_col: Whether the plot is single column in a layout that has two columns
|
|
124
|
+
(e.g. ICML). Not supported for any other layout.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
rc_params: Matplotlib key-value rc-params. Use it via
|
|
128
|
+
`plt.rcParams.update(rc_params)`. Note that you can always override/add
|
|
129
|
+
key-values to this dict before applying it.
|
|
130
|
+
fig_width_in: figure width in inches.
|
|
131
|
+
fig_height_in: figure height in inches.
|
|
132
|
+
"""
|
|
133
|
+
if (width_frac <= 0 or width_frac > 1) or (height_frac <= 0 or height_frac > 1):
|
|
134
|
+
raise ValueError("Both `width_frac` and `height_frac` must be between 0 and 1.")
|
|
135
|
+
|
|
136
|
+
if layout not in PAPER_FORMATS.keys():
|
|
137
|
+
raise ValueError(f"Layout must be in {list(PAPER_FORMATS.keys())}.")
|
|
138
|
+
|
|
139
|
+
if layout not in ["icml"] and single_col:
|
|
140
|
+
raise ValueError("Double-column is only supported for ICML.")
|
|
141
|
+
|
|
142
|
+
format = PAPER_FORMATS[layout]
|
|
143
|
+
is_poster = "poster" in layout
|
|
144
|
+
|
|
145
|
+
rc_params = {
|
|
146
|
+
"text.usetex": False,
|
|
147
|
+
"font.size": format["footnote_size"],
|
|
148
|
+
"font.family": "serif",
|
|
149
|
+
"font.serif": format["font_name"],
|
|
150
|
+
"mathtext.fontset": "stixsans" if is_poster else "cm",
|
|
151
|
+
"lines.linewidth": format["linewidth"],
|
|
152
|
+
"axes.linewidth": 1,
|
|
153
|
+
"axes.titlesize": format["footnote_size"],
|
|
154
|
+
"axes.labelsize": format["script_size"],
|
|
155
|
+
"axes.unicode_minus": False,
|
|
156
|
+
"axes.formatter.use_mathtext": True,
|
|
157
|
+
"legend.fontsize": format["script_size"],
|
|
158
|
+
"xtick.major.size": format["tick_size"],
|
|
159
|
+
"ytick.major.size": format["tick_size"],
|
|
160
|
+
"xtick.major.width": format["tick_width"],
|
|
161
|
+
"ytick.major.width": format["tick_width"],
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
w = width_frac * (format["col_width"] if single_col else format["text_width"])
|
|
165
|
+
h = height_frac * format["text_height"]
|
|
166
|
+
|
|
167
|
+
return rc_params, w, h
|
|
File without changes
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import matplotlib as plt
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from pub_ready_plots import get_mpl_rcParams
|
|
5
|
+
|
|
6
|
+
LAYOUTS = ["icml", "iclr", "neurips", "jmlr", "poster-portrait", "poster-landscape"]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.mark.parametrize("layout", LAYOUTS)
|
|
10
|
+
def test_correct_func(layout):
|
|
11
|
+
rc_params, width_in, height_in = get_mpl_rcParams(1, 0.15, layout)
|
|
12
|
+
plt.rcParams.update(rc_params)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_incorrect_width():
|
|
16
|
+
with pytest.raises(ValueError):
|
|
17
|
+
_, _, _ = get_mpl_rcParams(12, 0.1, "iclr")
|
|
18
|
+
|
|
19
|
+
with pytest.raises(ValueError):
|
|
20
|
+
_, _, _ = get_mpl_rcParams(0, 0.1, "iclr")
|
|
21
|
+
|
|
22
|
+
with pytest.raises(ValueError):
|
|
23
|
+
_, _, _ = get_mpl_rcParams(-3.2, 0.1, "iclr")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_incorrect_height():
|
|
27
|
+
with pytest.raises(ValueError):
|
|
28
|
+
_, _, _ = get_mpl_rcParams(0.15, 12, "iclr")
|
|
29
|
+
|
|
30
|
+
with pytest.raises(ValueError):
|
|
31
|
+
_, _, _ = get_mpl_rcParams(0.15, 0, "iclr")
|
|
32
|
+
|
|
33
|
+
with pytest.raises(ValueError):
|
|
34
|
+
_, _, _ = get_mpl_rcParams(0.15, -1.2, "iclr")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_incorrect_layout():
|
|
38
|
+
with pytest.raises(ValueError):
|
|
39
|
+
_, _, _ = get_mpl_rcParams(1, 1, "predatory_journal")
|