figrecipe 0.5.0__py3-none-any.whl → 0.7.4__py3-none-any.whl
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.
- figrecipe/__init__.py +220 -819
- figrecipe/_api/__init__.py +48 -0
- figrecipe/_api/_extract.py +108 -0
- figrecipe/_api/_notebook.py +61 -0
- figrecipe/_api/_panel.py +46 -0
- figrecipe/_api/_save.py +191 -0
- figrecipe/_api/_seaborn_proxy.py +34 -0
- figrecipe/_api/_style_manager.py +153 -0
- figrecipe/_api/_subplots.py +333 -0
- figrecipe/_api/_validate.py +82 -0
- figrecipe/_dev/__init__.py +29 -0
- figrecipe/_dev/_plotters.py +76 -0
- figrecipe/_dev/_run_demos.py +56 -0
- figrecipe/_dev/demo_plotters/__init__.py +64 -0
- figrecipe/_dev/demo_plotters/_categories.py +81 -0
- figrecipe/_dev/demo_plotters/_figure_creators.py +119 -0
- figrecipe/_dev/demo_plotters/_helpers.py +31 -0
- figrecipe/_dev/demo_plotters/_registry.py +50 -0
- figrecipe/_dev/demo_plotters/bar_categorical/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/bar_categorical/plot_bar.py +25 -0
- figrecipe/_dev/demo_plotters/bar_categorical/plot_barh.py +25 -0
- figrecipe/_dev/demo_plotters/contour_surface/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/contour_surface/plot_contour.py +30 -0
- figrecipe/_dev/demo_plotters/contour_surface/plot_contourf.py +29 -0
- figrecipe/_dev/demo_plotters/contour_surface/plot_tricontour.py +28 -0
- figrecipe/_dev/demo_plotters/contour_surface/plot_tricontourf.py +28 -0
- figrecipe/_dev/demo_plotters/contour_surface/plot_tripcolor.py +29 -0
- figrecipe/_dev/demo_plotters/contour_surface/plot_triplot.py +25 -0
- figrecipe/_dev/demo_plotters/distribution/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/distribution/plot_boxplot.py +24 -0
- figrecipe/_dev/demo_plotters/distribution/plot_ecdf.py +24 -0
- figrecipe/_dev/demo_plotters/distribution/plot_hist.py +24 -0
- figrecipe/_dev/demo_plotters/distribution/plot_hist2d.py +25 -0
- figrecipe/_dev/demo_plotters/distribution/plot_violinplot.py +25 -0
- figrecipe/_dev/demo_plotters/image_matrix/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/image_matrix/plot_hexbin.py +25 -0
- figrecipe/_dev/demo_plotters/image_matrix/plot_imshow.py +23 -0
- figrecipe/_dev/demo_plotters/image_matrix/plot_matshow.py +23 -0
- figrecipe/_dev/demo_plotters/image_matrix/plot_pcolor.py +29 -0
- figrecipe/_dev/demo_plotters/image_matrix/plot_pcolormesh.py +29 -0
- figrecipe/_dev/demo_plotters/image_matrix/plot_spy.py +29 -0
- figrecipe/_dev/demo_plotters/line_curve/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/line_curve/plot_errorbar.py +28 -0
- figrecipe/_dev/demo_plotters/line_curve/plot_fill.py +29 -0
- figrecipe/_dev/demo_plotters/line_curve/plot_fill_between.py +30 -0
- figrecipe/_dev/demo_plotters/line_curve/plot_fill_betweenx.py +28 -0
- figrecipe/_dev/demo_plotters/line_curve/plot_plot.py +28 -0
- figrecipe/_dev/demo_plotters/line_curve/plot_stackplot.py +29 -0
- figrecipe/_dev/demo_plotters/line_curve/plot_stairs.py +27 -0
- figrecipe/_dev/demo_plotters/line_curve/plot_step.py +27 -0
- figrecipe/_dev/demo_plotters/scatter_points/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/scatter_points/plot_scatter.py +24 -0
- figrecipe/_dev/demo_plotters/special/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/special/plot_eventplot.py +25 -0
- figrecipe/_dev/demo_plotters/special/plot_loglog.py +27 -0
- figrecipe/_dev/demo_plotters/special/plot_pie.py +27 -0
- figrecipe/_dev/demo_plotters/special/plot_semilogx.py +27 -0
- figrecipe/_dev/demo_plotters/special/plot_semilogy.py +27 -0
- figrecipe/_dev/demo_plotters/special/plot_stem.py +27 -0
- figrecipe/_dev/demo_plotters/spectral_signal/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/spectral_signal/plot_acorr.py +24 -0
- figrecipe/_dev/demo_plotters/spectral_signal/plot_angle_spectrum.py +28 -0
- figrecipe/_dev/demo_plotters/spectral_signal/plot_cohere.py +29 -0
- figrecipe/_dev/demo_plotters/spectral_signal/plot_csd.py +29 -0
- figrecipe/_dev/demo_plotters/spectral_signal/plot_magnitude_spectrum.py +28 -0
- figrecipe/_dev/demo_plotters/spectral_signal/plot_phase_spectrum.py +28 -0
- figrecipe/_dev/demo_plotters/spectral_signal/plot_psd.py +29 -0
- figrecipe/_dev/demo_plotters/spectral_signal/plot_specgram.py +30 -0
- figrecipe/_dev/demo_plotters/spectral_signal/plot_xcorr.py +25 -0
- figrecipe/_dev/demo_plotters/vector_flow/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/vector_flow/plot_barbs.py +30 -0
- figrecipe/_dev/demo_plotters/vector_flow/plot_quiver.py +30 -0
- figrecipe/_dev/demo_plotters/vector_flow/plot_streamplot.py +30 -0
- figrecipe/_editor/__init__.py +278 -0
- figrecipe/_editor/_bbox/__init__.py +43 -0
- figrecipe/_editor/_bbox/_collections.py +177 -0
- figrecipe/_editor/_bbox/_elements.py +159 -0
- figrecipe/_editor/_bbox/_extract.py +256 -0
- figrecipe/_editor/_bbox/_extract_axes.py +370 -0
- figrecipe/_editor/_bbox/_extract_text.py +342 -0
- figrecipe/_editor/_bbox/_lines.py +173 -0
- figrecipe/_editor/_bbox/_transforms.py +146 -0
- figrecipe/_editor/_flask_app.py +258 -0
- figrecipe/_editor/_helpers.py +242 -0
- figrecipe/_editor/_hitmap/__init__.py +76 -0
- figrecipe/_editor/_hitmap/_artists/__init__.py +21 -0
- figrecipe/_editor/_hitmap/_artists/_collections.py +345 -0
- figrecipe/_editor/_hitmap/_artists/_images.py +68 -0
- figrecipe/_editor/_hitmap/_artists/_lines.py +107 -0
- figrecipe/_editor/_hitmap/_artists/_patches.py +163 -0
- figrecipe/_editor/_hitmap/_artists/_text.py +190 -0
- figrecipe/_editor/_hitmap/_colors.py +181 -0
- figrecipe/_editor/_hitmap/_detect.py +137 -0
- figrecipe/_editor/_hitmap/_restore.py +154 -0
- figrecipe/_editor/_hitmap_main.py +182 -0
- figrecipe/_editor/_overrides.py +318 -0
- figrecipe/_editor/_preferences.py +135 -0
- figrecipe/_editor/_render_overrides.py +480 -0
- figrecipe/_editor/_renderer.py +199 -0
- figrecipe/_editor/_routes_axis.py +453 -0
- figrecipe/_editor/_routes_core.py +284 -0
- figrecipe/_editor/_routes_element.py +317 -0
- figrecipe/_editor/_routes_style.py +223 -0
- figrecipe/_editor/_templates/__init__.py +152 -0
- figrecipe/_editor/_templates/_html.py +502 -0
- figrecipe/_editor/_templates/_scripts/__init__.py +120 -0
- figrecipe/_editor/_templates/_scripts/_api.py +228 -0
- figrecipe/_editor/_templates/_scripts/_colors.py +485 -0
- figrecipe/_editor/_templates/_scripts/_core.py +436 -0
- figrecipe/_editor/_templates/_scripts/_debug_snapshot.py +186 -0
- figrecipe/_editor/_templates/_scripts/_element_editor.py +310 -0
- figrecipe/_editor/_templates/_scripts/_files.py +195 -0
- figrecipe/_editor/_templates/_scripts/_hitmap.py +509 -0
- figrecipe/_editor/_templates/_scripts/_inspector.py +315 -0
- figrecipe/_editor/_templates/_scripts/_labels.py +464 -0
- figrecipe/_editor/_templates/_scripts/_legend_drag.py +265 -0
- figrecipe/_editor/_templates/_scripts/_modals.py +226 -0
- figrecipe/_editor/_templates/_scripts/_overlays.py +292 -0
- figrecipe/_editor/_templates/_scripts/_panel_drag.py +334 -0
- figrecipe/_editor/_templates/_scripts/_panel_position.py +279 -0
- figrecipe/_editor/_templates/_scripts/_selection.py +237 -0
- figrecipe/_editor/_templates/_scripts/_tabs.py +89 -0
- figrecipe/_editor/_templates/_scripts/_view_mode.py +107 -0
- figrecipe/_editor/_templates/_scripts/_zoom.py +179 -0
- figrecipe/_editor/_templates/_styles/__init__.py +69 -0
- figrecipe/_editor/_templates/_styles/_base.py +64 -0
- figrecipe/_editor/_templates/_styles/_buttons.py +206 -0
- figrecipe/_editor/_templates/_styles/_color_input.py +123 -0
- figrecipe/_editor/_templates/_styles/_controls.py +265 -0
- figrecipe/_editor/_templates/_styles/_dynamic_props.py +144 -0
- figrecipe/_editor/_templates/_styles/_forms.py +126 -0
- figrecipe/_editor/_templates/_styles/_hitmap.py +184 -0
- figrecipe/_editor/_templates/_styles/_inspector.py +90 -0
- figrecipe/_editor/_templates/_styles/_labels.py +118 -0
- figrecipe/_editor/_templates/_styles/_modals.py +98 -0
- figrecipe/_editor/_templates/_styles/_overlays.py +130 -0
- figrecipe/_editor/_templates/_styles/_preview.py +225 -0
- figrecipe/_editor/_templates/_styles/_selection.py +73 -0
- figrecipe/_params/_DECORATION_METHODS.py +33 -0
- figrecipe/_params/_PLOTTING_METHODS.py +58 -0
- figrecipe/_params/__init__.py +9 -0
- figrecipe/_recorder.py +92 -110
- figrecipe/_recorder_utils.py +124 -0
- figrecipe/_reproducer/__init__.py +18 -0
- figrecipe/_reproducer/_core.py +498 -0
- figrecipe/_reproducer/_custom_plots.py +279 -0
- figrecipe/_reproducer/_seaborn.py +100 -0
- figrecipe/_reproducer/_violin.py +186 -0
- figrecipe/_seaborn.py +14 -9
- figrecipe/_serializer.py +2 -2
- figrecipe/_signatures/README.md +68 -0
- figrecipe/_signatures/__init__.py +12 -2
- figrecipe/_signatures/_kwargs.py +273 -0
- figrecipe/_signatures/_loader.py +114 -57
- figrecipe/_signatures/_parsing.py +147 -0
- figrecipe/_utils/__init__.py +6 -4
- figrecipe/_utils/_crop.py +10 -4
- figrecipe/_utils/_image_diff.py +37 -33
- figrecipe/_utils/_numpy_io.py +0 -1
- figrecipe/_utils/_units.py +11 -3
- figrecipe/_validator.py +12 -3
- figrecipe/_wrappers/_axes.py +193 -170
- figrecipe/_wrappers/_axes_helpers.py +136 -0
- figrecipe/_wrappers/_axes_plots.py +418 -0
- figrecipe/_wrappers/_axes_seaborn.py +157 -0
- figrecipe/_wrappers/_figure.py +277 -18
- figrecipe/_wrappers/_panel_labels.py +127 -0
- figrecipe/_wrappers/_plot_helpers.py +143 -0
- figrecipe/_wrappers/_violin_helpers.py +180 -0
- figrecipe/plt.py +0 -1
- figrecipe/pyplot.py +2 -1
- figrecipe/styles/__init__.py +12 -11
- figrecipe/styles/_dotdict.py +72 -0
- figrecipe/styles/_finalize.py +134 -0
- figrecipe/styles/_fonts.py +77 -0
- figrecipe/styles/_kwargs_converter.py +178 -0
- figrecipe/styles/_plot_styles.py +209 -0
- figrecipe/styles/_style_applier.py +60 -202
- figrecipe/styles/_style_loader.py +73 -121
- figrecipe/styles/_themes.py +151 -0
- figrecipe/styles/presets/MATPLOTLIB.yaml +95 -0
- figrecipe/styles/presets/SCITEX.yaml +181 -0
- figrecipe-0.7.4.dist-info/METADATA +429 -0
- figrecipe-0.7.4.dist-info/RECORD +188 -0
- figrecipe/_reproducer.py +0 -358
- figrecipe-0.5.0.dist-info/METADATA +0 -336
- figrecipe-0.5.0.dist-info/RECORD +0 -26
- {figrecipe-0.5.0.dist-info → figrecipe-0.7.4.dist-info}/WHEEL +0 -0
- {figrecipe-0.5.0.dist-info → figrecipe-0.7.4.dist-info}/licenses/LICENSE +0 -0
figrecipe/_reproducer.py
DELETED
|
@@ -1,358 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""Reproduce figures from recipe files."""
|
|
4
|
-
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
7
|
-
|
|
8
|
-
import matplotlib.pyplot as plt
|
|
9
|
-
import numpy as np
|
|
10
|
-
from matplotlib.axes import Axes
|
|
11
|
-
from matplotlib.figure import Figure
|
|
12
|
-
|
|
13
|
-
from ._recorder import FigureRecord, CallRecord
|
|
14
|
-
from ._serializer import load_recipe
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def reproduce(
|
|
18
|
-
path: Union[str, Path],
|
|
19
|
-
calls: Optional[List[str]] = None,
|
|
20
|
-
skip_decorations: bool = False,
|
|
21
|
-
) -> Tuple[Figure, Union[Axes, List[Axes]]]:
|
|
22
|
-
"""Reproduce a figure from a recipe file.
|
|
23
|
-
|
|
24
|
-
Parameters
|
|
25
|
-
----------
|
|
26
|
-
path : str or Path
|
|
27
|
-
Path to .yaml recipe file.
|
|
28
|
-
calls : list of str, optional
|
|
29
|
-
If provided, only reproduce these specific call IDs.
|
|
30
|
-
skip_decorations : bool
|
|
31
|
-
If True, skip decoration calls (labels, legends, etc.).
|
|
32
|
-
|
|
33
|
-
Returns
|
|
34
|
-
-------
|
|
35
|
-
fig : matplotlib.figure.Figure
|
|
36
|
-
Reproduced figure.
|
|
37
|
-
axes : Axes or list of Axes
|
|
38
|
-
Reproduced axes (single if 1x1, otherwise list).
|
|
39
|
-
|
|
40
|
-
Examples
|
|
41
|
-
--------
|
|
42
|
-
>>> import figrecipe as ps
|
|
43
|
-
>>> fig, ax = ps.reproduce("experiment_001.yaml")
|
|
44
|
-
>>> plt.show()
|
|
45
|
-
"""
|
|
46
|
-
record = load_recipe(path)
|
|
47
|
-
return reproduce_from_record(
|
|
48
|
-
record,
|
|
49
|
-
calls=calls,
|
|
50
|
-
skip_decorations=skip_decorations,
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def reproduce_from_record(
|
|
55
|
-
record: FigureRecord,
|
|
56
|
-
calls: Optional[List[str]] = None,
|
|
57
|
-
skip_decorations: bool = False,
|
|
58
|
-
) -> Tuple[Figure, Union[Axes, List[Axes]]]:
|
|
59
|
-
"""Reproduce a figure from a FigureRecord.
|
|
60
|
-
|
|
61
|
-
Parameters
|
|
62
|
-
----------
|
|
63
|
-
record : FigureRecord
|
|
64
|
-
The figure record to reproduce.
|
|
65
|
-
calls : list of str, optional
|
|
66
|
-
If provided, only reproduce these specific call IDs.
|
|
67
|
-
skip_decorations : bool
|
|
68
|
-
If True, skip decoration calls.
|
|
69
|
-
|
|
70
|
-
Returns
|
|
71
|
-
-------
|
|
72
|
-
fig : matplotlib.figure.Figure
|
|
73
|
-
Reproduced figure.
|
|
74
|
-
axes : Axes or list of Axes
|
|
75
|
-
Reproduced axes.
|
|
76
|
-
"""
|
|
77
|
-
# Determine grid size from axes positions
|
|
78
|
-
max_row = 0
|
|
79
|
-
max_col = 0
|
|
80
|
-
for ax_key in record.axes.keys():
|
|
81
|
-
parts = ax_key.split("_")
|
|
82
|
-
if len(parts) >= 3:
|
|
83
|
-
max_row = max(max_row, int(parts[1]))
|
|
84
|
-
max_col = max(max_col, int(parts[2]))
|
|
85
|
-
|
|
86
|
-
nrows = max_row + 1
|
|
87
|
-
ncols = max_col + 1
|
|
88
|
-
|
|
89
|
-
# Create figure
|
|
90
|
-
fig, mpl_axes = plt.subplots(
|
|
91
|
-
nrows,
|
|
92
|
-
ncols,
|
|
93
|
-
figsize=record.figsize,
|
|
94
|
-
dpi=record.dpi,
|
|
95
|
-
constrained_layout=record.constrained_layout,
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
# Apply layout if recorded
|
|
99
|
-
if record.layout is not None:
|
|
100
|
-
fig.subplots_adjust(**record.layout)
|
|
101
|
-
|
|
102
|
-
# Ensure axes is 2D array
|
|
103
|
-
if nrows == 1 and ncols == 1:
|
|
104
|
-
axes_2d = np.array([[mpl_axes]])
|
|
105
|
-
else:
|
|
106
|
-
axes_2d = np.atleast_2d(mpl_axes)
|
|
107
|
-
if nrows == 1:
|
|
108
|
-
axes_2d = axes_2d.reshape(1, -1)
|
|
109
|
-
elif ncols == 1:
|
|
110
|
-
axes_2d = axes_2d.reshape(-1, 1)
|
|
111
|
-
|
|
112
|
-
# Apply style BEFORE replaying calls (to match original order:
|
|
113
|
-
# style is applied during subplots(), then user creates plots/decorations)
|
|
114
|
-
if record.style is not None:
|
|
115
|
-
from .styles import apply_style_mm
|
|
116
|
-
for row in range(nrows):
|
|
117
|
-
for col in range(ncols):
|
|
118
|
-
apply_style_mm(axes_2d[row, col], record.style)
|
|
119
|
-
|
|
120
|
-
# Replay calls on each axes
|
|
121
|
-
for ax_key, ax_record in record.axes.items():
|
|
122
|
-
parts = ax_key.split("_")
|
|
123
|
-
if len(parts) >= 3:
|
|
124
|
-
row, col = int(parts[1]), int(parts[2])
|
|
125
|
-
else:
|
|
126
|
-
row, col = 0, 0
|
|
127
|
-
|
|
128
|
-
ax = axes_2d[row, col]
|
|
129
|
-
|
|
130
|
-
# Replay plotting calls
|
|
131
|
-
for call in ax_record.calls:
|
|
132
|
-
if calls is not None and call.id not in calls:
|
|
133
|
-
continue
|
|
134
|
-
_replay_call(ax, call)
|
|
135
|
-
|
|
136
|
-
# Replay decorations
|
|
137
|
-
if not skip_decorations:
|
|
138
|
-
for call in ax_record.decorations:
|
|
139
|
-
if calls is not None and call.id not in calls:
|
|
140
|
-
continue
|
|
141
|
-
_replay_call(ax, call)
|
|
142
|
-
|
|
143
|
-
# Return in appropriate format
|
|
144
|
-
if nrows == 1 and ncols == 1:
|
|
145
|
-
return fig, axes_2d[0, 0]
|
|
146
|
-
elif nrows == 1:
|
|
147
|
-
return fig, list(axes_2d[0])
|
|
148
|
-
elif ncols == 1:
|
|
149
|
-
return fig, list(axes_2d[:, 0])
|
|
150
|
-
else:
|
|
151
|
-
return fig, axes_2d.tolist()
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
def _replay_call(ax: Axes, call: CallRecord) -> Any:
|
|
155
|
-
"""Replay a single call on an axes.
|
|
156
|
-
|
|
157
|
-
Parameters
|
|
158
|
-
----------
|
|
159
|
-
ax : Axes
|
|
160
|
-
The matplotlib axes.
|
|
161
|
-
call : CallRecord
|
|
162
|
-
The call to replay.
|
|
163
|
-
|
|
164
|
-
Returns
|
|
165
|
-
-------
|
|
166
|
-
Any
|
|
167
|
-
Result of the matplotlib call.
|
|
168
|
-
"""
|
|
169
|
-
method_name = call.function
|
|
170
|
-
|
|
171
|
-
# Check if it's a seaborn call
|
|
172
|
-
if method_name.startswith("sns."):
|
|
173
|
-
return _replay_seaborn_call(ax, call)
|
|
174
|
-
|
|
175
|
-
method = getattr(ax, method_name, None)
|
|
176
|
-
|
|
177
|
-
if method is None:
|
|
178
|
-
# Method not found, skip
|
|
179
|
-
return None
|
|
180
|
-
|
|
181
|
-
# Reconstruct args
|
|
182
|
-
args = []
|
|
183
|
-
for arg_data in call.args:
|
|
184
|
-
value = _reconstruct_value(arg_data)
|
|
185
|
-
args.append(value)
|
|
186
|
-
|
|
187
|
-
# Get kwargs
|
|
188
|
-
kwargs = call.kwargs.copy()
|
|
189
|
-
|
|
190
|
-
# Call the method
|
|
191
|
-
try:
|
|
192
|
-
return method(*args, **kwargs)
|
|
193
|
-
except Exception as e:
|
|
194
|
-
# Log warning but continue
|
|
195
|
-
import warnings
|
|
196
|
-
warnings.warn(f"Failed to replay {method_name}: {e}")
|
|
197
|
-
return None
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
def _replay_seaborn_call(ax: Axes, call: CallRecord) -> Any:
|
|
201
|
-
"""Replay a seaborn call on an axes.
|
|
202
|
-
|
|
203
|
-
Parameters
|
|
204
|
-
----------
|
|
205
|
-
ax : Axes
|
|
206
|
-
The matplotlib axes.
|
|
207
|
-
call : CallRecord
|
|
208
|
-
The seaborn call to replay.
|
|
209
|
-
|
|
210
|
-
Returns
|
|
211
|
-
-------
|
|
212
|
-
Any
|
|
213
|
-
Result of the seaborn call.
|
|
214
|
-
"""
|
|
215
|
-
try:
|
|
216
|
-
import seaborn as sns
|
|
217
|
-
import pandas as pd
|
|
218
|
-
except ImportError:
|
|
219
|
-
import warnings
|
|
220
|
-
warnings.warn("seaborn/pandas required to replay seaborn calls")
|
|
221
|
-
return None
|
|
222
|
-
|
|
223
|
-
# Get the seaborn function name (remove "sns." prefix)
|
|
224
|
-
func_name = call.function[4:] # Remove "sns."
|
|
225
|
-
func = getattr(sns, func_name, None)
|
|
226
|
-
|
|
227
|
-
if func is None:
|
|
228
|
-
import warnings
|
|
229
|
-
warnings.warn(f"Seaborn function {func_name} not found")
|
|
230
|
-
return None
|
|
231
|
-
|
|
232
|
-
# Reconstruct data from args
|
|
233
|
-
# Args contain column data with "param" field indicating the parameter name
|
|
234
|
-
data_dict = {}
|
|
235
|
-
param_mapping = {} # Maps param name to column name
|
|
236
|
-
|
|
237
|
-
for arg_data in call.args:
|
|
238
|
-
param = arg_data.get("param")
|
|
239
|
-
name = arg_data.get("name")
|
|
240
|
-
value = _reconstruct_value(arg_data)
|
|
241
|
-
|
|
242
|
-
if param is not None:
|
|
243
|
-
# This is a DataFrame column
|
|
244
|
-
col_name = name if name else param
|
|
245
|
-
data_dict[col_name] = value
|
|
246
|
-
param_mapping[param] = col_name
|
|
247
|
-
|
|
248
|
-
# Build kwargs
|
|
249
|
-
kwargs = call.kwargs.copy()
|
|
250
|
-
|
|
251
|
-
# Remove internal keys
|
|
252
|
-
internal_keys = [k for k in kwargs.keys() if k.startswith("_")]
|
|
253
|
-
for key in internal_keys:
|
|
254
|
-
kwargs.pop(key, None)
|
|
255
|
-
|
|
256
|
-
# If we have data columns, create a DataFrame
|
|
257
|
-
if data_dict:
|
|
258
|
-
df = pd.DataFrame(data_dict)
|
|
259
|
-
kwargs["data"] = df
|
|
260
|
-
|
|
261
|
-
# Update column name references in kwargs
|
|
262
|
-
for param, col_name in param_mapping.items():
|
|
263
|
-
if param in ["x", "y", "hue", "size", "style", "row", "col"]:
|
|
264
|
-
kwargs[param] = col_name
|
|
265
|
-
|
|
266
|
-
# Add the axes
|
|
267
|
-
kwargs["ax"] = ax
|
|
268
|
-
|
|
269
|
-
# Convert certain list parameters back to tuples (YAML serializes tuples as lists)
|
|
270
|
-
# 'sizes' in seaborn expects a tuple (min, max) for range, not a list
|
|
271
|
-
if "sizes" in kwargs and isinstance(kwargs["sizes"], list):
|
|
272
|
-
kwargs["sizes"] = tuple(kwargs["sizes"])
|
|
273
|
-
|
|
274
|
-
# Call the seaborn function
|
|
275
|
-
try:
|
|
276
|
-
return func(**kwargs)
|
|
277
|
-
except Exception as e:
|
|
278
|
-
import warnings
|
|
279
|
-
warnings.warn(f"Failed to replay sns.{func_name}: {e}")
|
|
280
|
-
return None
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
def _reconstruct_value(arg_data: Dict[str, Any]) -> Any:
|
|
284
|
-
"""Reconstruct a value from serialized arg data.
|
|
285
|
-
|
|
286
|
-
Parameters
|
|
287
|
-
----------
|
|
288
|
-
arg_data : dict
|
|
289
|
-
Serialized argument data.
|
|
290
|
-
|
|
291
|
-
Returns
|
|
292
|
-
-------
|
|
293
|
-
Any
|
|
294
|
-
Reconstructed value.
|
|
295
|
-
"""
|
|
296
|
-
# Check if we have a pre-loaded array
|
|
297
|
-
if "_loaded_array" in arg_data:
|
|
298
|
-
return arg_data["_loaded_array"]
|
|
299
|
-
|
|
300
|
-
data = arg_data.get("data")
|
|
301
|
-
|
|
302
|
-
# If data is a list, convert to numpy array
|
|
303
|
-
if isinstance(data, list):
|
|
304
|
-
dtype = arg_data.get("dtype")
|
|
305
|
-
try:
|
|
306
|
-
return np.array(data, dtype=dtype if dtype else None)
|
|
307
|
-
except (TypeError, ValueError):
|
|
308
|
-
return np.array(data)
|
|
309
|
-
|
|
310
|
-
return data
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
def get_recipe_info(path: Union[str, Path]) -> Dict[str, Any]:
|
|
314
|
-
"""Get information about a recipe without reproducing.
|
|
315
|
-
|
|
316
|
-
Parameters
|
|
317
|
-
----------
|
|
318
|
-
path : str or Path
|
|
319
|
-
Path to .yaml recipe file.
|
|
320
|
-
|
|
321
|
-
Returns
|
|
322
|
-
-------
|
|
323
|
-
dict
|
|
324
|
-
Recipe information including:
|
|
325
|
-
- id: Figure ID
|
|
326
|
-
- created: Creation timestamp
|
|
327
|
-
- matplotlib_version: Version used
|
|
328
|
-
- figsize: Figure size
|
|
329
|
-
- n_axes: Number of axes
|
|
330
|
-
- calls: List of call IDs
|
|
331
|
-
"""
|
|
332
|
-
record = load_recipe(path)
|
|
333
|
-
|
|
334
|
-
all_calls = []
|
|
335
|
-
for ax_record in record.axes.values():
|
|
336
|
-
for call in ax_record.calls:
|
|
337
|
-
all_calls.append({
|
|
338
|
-
"id": call.id,
|
|
339
|
-
"function": call.function,
|
|
340
|
-
"n_args": len(call.args),
|
|
341
|
-
"kwargs": list(call.kwargs.keys()),
|
|
342
|
-
})
|
|
343
|
-
for call in ax_record.decorations:
|
|
344
|
-
all_calls.append({
|
|
345
|
-
"id": call.id,
|
|
346
|
-
"function": call.function,
|
|
347
|
-
"type": "decoration",
|
|
348
|
-
})
|
|
349
|
-
|
|
350
|
-
return {
|
|
351
|
-
"id": record.id,
|
|
352
|
-
"created": record.created,
|
|
353
|
-
"matplotlib_version": record.matplotlib_version,
|
|
354
|
-
"figsize": record.figsize,
|
|
355
|
-
"dpi": record.dpi,
|
|
356
|
-
"n_axes": len(record.axes),
|
|
357
|
-
"calls": all_calls,
|
|
358
|
-
}
|
|
@@ -1,336 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: figrecipe
|
|
3
|
-
Version: 0.5.0
|
|
4
|
-
Summary: Reproducible matplotlib wrapper with mm-precision layouts
|
|
5
|
-
Project-URL: Homepage, https://github.com/ywatanabe1989/figrecipe
|
|
6
|
-
Project-URL: Documentation, https://github.com/ywatanabe1989/figrecipe#readme
|
|
7
|
-
Project-URL: Repository, https://github.com/ywatanabe1989/figrecipe.git
|
|
8
|
-
Project-URL: Issues, https://github.com/ywatanabe1989/figrecipe/issues
|
|
9
|
-
Author-email: Yusuke Watanabe <ywatanabe@scitex.ai>
|
|
10
|
-
License-Expression: AGPL-3.0
|
|
11
|
-
License-File: LICENSE
|
|
12
|
-
Keywords: matplotlib,millimeter,plotting,publication,recipe,reproducibility,scientific,visualization,yaml
|
|
13
|
-
Classifier: Development Status :: 4 - Beta
|
|
14
|
-
Classifier: Intended Audience :: Science/Research
|
|
15
|
-
Classifier: License :: OSI Approved :: GNU Affero General Public License v3
|
|
16
|
-
Classifier: Operating System :: OS Independent
|
|
17
|
-
Classifier: Programming Language :: Python :: 3
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
-
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
23
|
-
Requires-Python: >=3.9
|
|
24
|
-
Requires-Dist: matplotlib>=3.5.0
|
|
25
|
-
Requires-Dist: numpy>=1.20.0
|
|
26
|
-
Requires-Dist: ruamel-yaml>=0.17.0
|
|
27
|
-
Provides-Extra: all
|
|
28
|
-
Requires-Dist: pandas>=1.3.0; extra == 'all'
|
|
29
|
-
Requires-Dist: pillow>=9.0.0; extra == 'all'
|
|
30
|
-
Requires-Dist: pytest-cov>=4.0.0; extra == 'all'
|
|
31
|
-
Requires-Dist: pytest>=7.0.0; extra == 'all'
|
|
32
|
-
Requires-Dist: seaborn>=0.12.0; extra == 'all'
|
|
33
|
-
Provides-Extra: dev
|
|
34
|
-
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
35
|
-
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
36
|
-
Provides-Extra: imaging
|
|
37
|
-
Requires-Dist: pillow>=9.0.0; extra == 'imaging'
|
|
38
|
-
Provides-Extra: seaborn
|
|
39
|
-
Requires-Dist: pandas>=1.3.0; extra == 'seaborn'
|
|
40
|
-
Requires-Dist: seaborn>=0.12.0; extra == 'seaborn'
|
|
41
|
-
Description-Content-Type: text/markdown
|
|
42
|
-
|
|
43
|
-
<!-- ---
|
|
44
|
-
!-- Timestamp: 2025-12-22 13:01:11
|
|
45
|
-
!-- Author: ywatanabe
|
|
46
|
-
!-- File: /home/ywatanabe/proj/figrecipe/README.md
|
|
47
|
-
!-- --- -->
|
|
48
|
-
|
|
49
|
-
<p align="center">
|
|
50
|
-
<img src="docs/figrecipe_logo.png" alt="figrecipe logo" width="200"/>
|
|
51
|
-
</p>
|
|
52
|
-
|
|
53
|
-
# FigRecipe
|
|
54
|
-
|
|
55
|
-
**Reproducible matplotlib figures with mm-precision layouts.**
|
|
56
|
-
FigRecipe is a lightweight recording & reproduction layer for matplotlib,
|
|
57
|
-
designed for scientific figures that must remain **editable, inspectable,
|
|
58
|
-
and reproducible**.
|
|
59
|
-
|
|
60
|
-
Part of **SciTeX™ – Research OS for reproducible science**
|
|
61
|
-
https://scitex.ai
|
|
62
|
-
|
|
63
|
-
[](https://badge.fury.io/py/figrecipe)
|
|
64
|
-
[](https://www.gnu.org/licenses/agpl-3.0)
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
## Why FigRecipe?
|
|
69
|
-
|
|
70
|
-
In scientific workflows, figures are often:
|
|
71
|
-
- hard to reproduce once scripts change,
|
|
72
|
-
- resized manually in pixels or inches,
|
|
73
|
-
- impossible to partially reuse or inspect later.
|
|
74
|
-
|
|
75
|
-
**FigRecipe solves this by recording plotting calls as a structured “recipe”**,
|
|
76
|
-
allowing figures to be:
|
|
77
|
-
- faithfully reproduced,
|
|
78
|
-
- partially re-rendered,
|
|
79
|
-
- inspected for underlying data,
|
|
80
|
-
- laid out in **exact millimeters** for publication.
|
|
81
|
-
|
|
82
|
-
---
|
|
83
|
-
|
|
84
|
-
## Key Features
|
|
85
|
-
|
|
86
|
-
- ✅ Drop-in replacement for `matplotlib.pyplot` (use `fr.subplots()` to enable recording)
|
|
87
|
-
- ✅ Automatic recording of plotting calls
|
|
88
|
-
- ✅ Reproduce figures from a YAML recipe
|
|
89
|
-
- ✅ Extract plotted data programmatically
|
|
90
|
-
- ✅ Selective reproduction of specific plots
|
|
91
|
-
- ✅ Millimeter-based layout (journal-ready)
|
|
92
|
-
- ✅ Publication-quality style presets
|
|
93
|
-
- ✅ Dark theme support (data colors preserved)
|
|
94
|
-
- ✅ Seamless seaborn integration
|
|
95
|
-
- ✅ Crop figures to content with mm margins
|
|
96
|
-
|
|
97
|
-
---
|
|
98
|
-
|
|
99
|
-
## Installation
|
|
100
|
-
|
|
101
|
-
```bash
|
|
102
|
-
pip install figrecipe
|
|
103
|
-
|
|
104
|
-
# Optional extras
|
|
105
|
-
pip install figrecipe[seaborn] # seaborn + pandas support
|
|
106
|
-
pip install figrecipe[imaging] # image cropping (Pillow)
|
|
107
|
-
pip install figrecipe[all] # all extras
|
|
108
|
-
|
|
109
|
-
# Optional: for PDF export from notebooks (SVG → PDF)
|
|
110
|
-
sudo apt install inkscape # Linux
|
|
111
|
-
brew install inkscape # macOS
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
**Requirements:** Python >= 3.9
|
|
115
|
-
|
|
116
|
-
## Basic Usage
|
|
117
|
-
|
|
118
|
-
### Recording & Saving
|
|
119
|
-
|
|
120
|
-
``` python
|
|
121
|
-
import figrecipe as fr
|
|
122
|
-
import numpy as np
|
|
123
|
-
|
|
124
|
-
x = np.linspace(0, 10, 100)
|
|
125
|
-
y = np.sin(x)
|
|
126
|
-
|
|
127
|
-
fig, ax = fr.subplots()
|
|
128
|
-
ax.plot(x, y, color='red', linewidth=2, id='sine_wave')
|
|
129
|
-
ax.set_xlabel('Time (s)')
|
|
130
|
-
ax.set_ylabel('Amplitude')
|
|
131
|
-
|
|
132
|
-
# Save image + recipe
|
|
133
|
-
img_path, yaml_path, result = fr.save(fig, 'figure.png')
|
|
134
|
-
# → creates: figure.png + figure.yaml
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### Reproducing a Figure
|
|
138
|
-
|
|
139
|
-
``` python
|
|
140
|
-
import figrecipe as fr
|
|
141
|
-
|
|
142
|
-
fig, ax = fr.reproduce('figure.yaml')
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
### Extracting Plotted Data
|
|
146
|
-
|
|
147
|
-
``` python
|
|
148
|
-
import figrecipe as fr
|
|
149
|
-
|
|
150
|
-
data = fr.extract_data('figure.yaml')
|
|
151
|
-
# {'sine_wave': {'x': array([...]), 'y': array([...])}}
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
### Millimeter-Based Layout (Publication-Ready)
|
|
155
|
-
|
|
156
|
-
``` python
|
|
157
|
-
fig, ax = fr.subplots(
|
|
158
|
-
axes_width_mm=60,
|
|
159
|
-
axes_height_mm=40,
|
|
160
|
-
margin_left_mm=15,
|
|
161
|
-
margin_bottom_mm=12,
|
|
162
|
-
)
|
|
163
|
-
```
|
|
164
|
-
This guarantees consistent sizing across editors, exports, and journals.
|
|
165
|
-
|
|
166
|
-
### Style Presets
|
|
167
|
-
|
|
168
|
-
``` python
|
|
169
|
-
fr.list_presets()
|
|
170
|
-
# ['MATPLOTLIB', 'SCITEX']
|
|
171
|
-
|
|
172
|
-
# Publication-quality preset (applied globally)
|
|
173
|
-
fr.load_style('SCITEX')
|
|
174
|
-
fig, ax = fr.subplots()
|
|
175
|
-
|
|
176
|
-
# Dark theme (UI-only, data colors preserved)
|
|
177
|
-
fr.load_style('SCITEX_DARK')
|
|
178
|
-
# or: fr.load_style('SCITEX', dark=True)
|
|
179
|
-
|
|
180
|
-
# Custom style
|
|
181
|
-
fr.load_style('/path/to/my_style.yaml')
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
See docs/EXAMPLE_RECIPE.yaml for a full style template.
|
|
185
|
-
|
|
186
|
-
### Recipe Format (YAML)
|
|
187
|
-
|
|
188
|
-
``` yaml
|
|
189
|
-
# Timestamp: "2025-12-22 11:53:14 (ywatanabe)"
|
|
190
|
-
# File: ./docs/EXAMPLE_RECIPE.yaml
|
|
191
|
-
# MATPLOTLIB Style Preset
|
|
192
|
-
# =======================
|
|
193
|
-
# Vanilla matplotlib defaults - minimal customization.
|
|
194
|
-
# Use this to reset to standard matplotlib behavior.
|
|
195
|
-
|
|
196
|
-
axes:
|
|
197
|
-
width_mm: null # Use matplotlib default (auto)
|
|
198
|
-
height_mm: null # Use matplotlib default (auto)
|
|
199
|
-
thickness_mm: null # Use matplotlib default
|
|
200
|
-
|
|
201
|
-
margins:
|
|
202
|
-
left_mm: null
|
|
203
|
-
right_mm: null
|
|
204
|
-
bottom_mm: null
|
|
205
|
-
top_mm: null
|
|
206
|
-
|
|
207
|
-
spacing:
|
|
208
|
-
horizontal_mm: null
|
|
209
|
-
vertical_mm: null
|
|
210
|
-
|
|
211
|
-
fonts:
|
|
212
|
-
family: null # matplotlib default (DejaVu Sans)
|
|
213
|
-
axis_label_pt: null
|
|
214
|
-
tick_label_pt: null
|
|
215
|
-
title_pt: null
|
|
216
|
-
suptitle_pt: null
|
|
217
|
-
legend_pt: null
|
|
218
|
-
annotation_pt: null
|
|
219
|
-
|
|
220
|
-
padding:
|
|
221
|
-
label_pt: null
|
|
222
|
-
tick_pt: null
|
|
223
|
-
title_pt: null
|
|
224
|
-
|
|
225
|
-
lines:
|
|
226
|
-
trace_mm: null
|
|
227
|
-
errorbar_mm: null
|
|
228
|
-
errorbar_cap_mm: null
|
|
229
|
-
|
|
230
|
-
ticks:
|
|
231
|
-
length_mm: null
|
|
232
|
-
thickness_mm: null
|
|
233
|
-
direction: null
|
|
234
|
-
n_ticks: null
|
|
235
|
-
|
|
236
|
-
markers:
|
|
237
|
-
size_mm: null
|
|
238
|
-
scatter_mm: null
|
|
239
|
-
edge_width_mm: null
|
|
240
|
-
|
|
241
|
-
legend:
|
|
242
|
-
frameon: null # matplotlib default (True)
|
|
243
|
-
bg: null # matplotlib default
|
|
244
|
-
edgecolor: null # matplotlib default
|
|
245
|
-
alpha: null # matplotlib default
|
|
246
|
-
loc: null # matplotlib default
|
|
247
|
-
|
|
248
|
-
output:
|
|
249
|
-
dpi: 300
|
|
250
|
-
transparent: false
|
|
251
|
-
format: "png"
|
|
252
|
-
|
|
253
|
-
behavior:
|
|
254
|
-
auto_scale_axes: false
|
|
255
|
-
hide_top_spine: false
|
|
256
|
-
hide_right_spine: false
|
|
257
|
-
grid: false
|
|
258
|
-
|
|
259
|
-
theme:
|
|
260
|
-
mode: "light"
|
|
261
|
-
dark:
|
|
262
|
-
figure_bg: "#1e1e1e"
|
|
263
|
-
axes_bg: "#1e1e1e"
|
|
264
|
-
legend_bg: "#1e1e1e"
|
|
265
|
-
text: "#d4d4d4"
|
|
266
|
-
spine: "#3c3c3c"
|
|
267
|
-
tick: "#d4d4d4"
|
|
268
|
-
grid: "#3a3a3a"
|
|
269
|
-
light:
|
|
270
|
-
figure_bg: "white"
|
|
271
|
-
axes_bg: "white"
|
|
272
|
-
legend_bg: "white"
|
|
273
|
-
text: "black"
|
|
274
|
-
spine: "black"
|
|
275
|
-
tick: "black"
|
|
276
|
-
grid: "#cccccc"
|
|
277
|
-
|
|
278
|
-
# Standard matplotlib color cycle (tab10)
|
|
279
|
-
colors:
|
|
280
|
-
palette:
|
|
281
|
-
- [31, 119, 180] # tab:blue
|
|
282
|
-
- [255, 127, 14] # tab:orange
|
|
283
|
-
- [44, 160, 44] # tab:green
|
|
284
|
-
- [214, 39, 40] # tab:red
|
|
285
|
-
- [148, 103, 189] # tab:purple
|
|
286
|
-
- [140, 86, 75] # tab:brown
|
|
287
|
-
- [227, 119, 194] # tab:pink
|
|
288
|
-
- [127, 127, 127] # tab:gray
|
|
289
|
-
- [188, 189, 34] # tab:olive
|
|
290
|
-
- [23, 190, 207] # tab:cyan
|
|
291
|
-
|
|
292
|
-
# EOF
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
The recipe is human-readable, version-controllable, and inspectable.
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
### API Overview
|
|
299
|
-
|
|
300
|
-
| Function | Description |
|
|
301
|
-
| ----------------------------- | --------------------------------- |
|
|
302
|
-
| `fr.subplots()` | Create a recording-enabled figure |
|
|
303
|
-
| `fr.save(fig, 'fig.png')` | Save image + recipe |
|
|
304
|
-
| `fr.reproduce('fig.yaml')` | Reproduce figure from recipe |
|
|
305
|
-
| `fr.extract_data('fig.yaml')` | Extract plotted data |
|
|
306
|
-
| `fr.info('fig.yaml')` | Inspect recipe metadata |
|
|
307
|
-
| `fr.load_style()` | Load style preset (global) |
|
|
308
|
-
| `fr.list_presets()` | List available presets |
|
|
309
|
-
| `fr.crop('fig.png')` | Crop to content with mm margin |
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
### Examples
|
|
313
|
-
- 📓 Interactive demo notebook:
|
|
314
|
-
examples/figrecipe_demo.ipynb
|
|
315
|
-
|
|
316
|
-
- 🌐 View on nbviewer:
|
|
317
|
-
https://nbviewer.org/github/ywatanabe1989/figrecipe/blob/main/examples/figrecipe_demo.ipynb
|
|
318
|
-
|
|
319
|
-
The notebook includes side-by-side comparisons of original and reproduced figures.
|
|
320
|
-
|
|
321
|
-
## Positioning
|
|
322
|
-
|
|
323
|
-
FigRecipe is intentionally minimal.
|
|
324
|
-
It focuses on recording, reproduction, and layout fidelity.
|
|
325
|
-
|
|
326
|
-
Higher-level workflows (figures, tables, statistics, GUIs) are handled in:
|
|
327
|
-
|
|
328
|
-
FTS (Figure-Table-Statistics Bundle) in SciTeX ecosystem (https://scitex.ai/vis/)
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
## License
|
|
332
|
-
|
|
333
|
-
AGPL-3.0 See [LICENSE](LICENSE)
|
|
334
|
-
.
|
|
335
|
-
|
|
336
|
-
<!-- EOF -->
|
figrecipe-0.5.0.dist-info/RECORD
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
figrecipe/__init__.py,sha256=X9RnSndUi5ZC_JW6ReBjvYD84kjK8D4FNvB5QjekFg4,35296
|
|
2
|
-
figrecipe/_recorder.py,sha256=mPjhc6ZaUWrzW3lBxeB_epf-ESHLj05sKs_zeHkZ9_Y,14290
|
|
3
|
-
figrecipe/_reproducer.py,sha256=IQNYJ8OcvGRKNRLe561yqtmWgIJtzkx6okCes1xIHsk,9683
|
|
4
|
-
figrecipe/_seaborn.py,sha256=dRQgIbJG9Xeh1DYCDNrgaXgQe4MsnukEXFeEolWnfeo,8348
|
|
5
|
-
figrecipe/_serializer.py,sha256=OpWbCRSlhMuA2KInRXvze-g9V_d5ToLUIEmTj-jquYs,6267
|
|
6
|
-
figrecipe/_validator.py,sha256=dFZez0J4f8V9wUnrMaz_CV-TlndBDcHHbZO4mRDC6Po,5567
|
|
7
|
-
figrecipe/plt.py,sha256=GkURHdcupAVvpWKgTCC23jn3OjmKQmTY09A9wGMUdDQ,308
|
|
8
|
-
figrecipe/pyplot.py,sha256=xw-wFa6N7wJ-A3ThLbQvhafwySNIqD05u9UupksY7S4,5685
|
|
9
|
-
figrecipe/_signatures/__init__.py,sha256=twHKZ8o_9Ho4k0SD-cff72_aaWevK0noGlWav5fauHg,244
|
|
10
|
-
figrecipe/_signatures/_loader.py,sha256=HCrVzQNWqqt6qSr13G_A1461-v94g65usfIRJ4_zmJk,4744
|
|
11
|
-
figrecipe/_utils/__init__.py,sha256=usLkRQJ6oZJSUSzZWkb1x-lkAdHO9I1g7VinLf2kz24,815
|
|
12
|
-
figrecipe/_utils/_crop.py,sha256=YQgaYGI3gAFRF0zkQeUOlTRs9vsBmAK_24vooPPcPPc,8385
|
|
13
|
-
figrecipe/_utils/_diff.py,sha256=cjRXjMrfXFKz6uZQOZ-XYifc0mzmHZyRXn_LY13K1SY,2354
|
|
14
|
-
figrecipe/_utils/_image_diff.py,sha256=9648vx2f_K628YjCB-bhCOsRws4qqWuUk2l4uJlpdrc,5406
|
|
15
|
-
figrecipe/_utils/_numpy_io.py,sha256=leckngCnZKTvceM3RfUq1P9TA6z1SibfzWqlhKtOaxY,4533
|
|
16
|
-
figrecipe/_utils/_units.py,sha256=94e0MaJ27WkF7zDqNL-P7adbve3qWitPzWrCYOuDBe8,4628
|
|
17
|
-
figrecipe/_wrappers/__init__.py,sha256=CcBUID5G6Mo_bNGpCaz5vbfB8CG7XtF-9lOm7kShQPw,214
|
|
18
|
-
figrecipe/_wrappers/_axes.py,sha256=gLymbk5WYBdaJ8pVnSJH4X8SMs_foNJIDRN4pDeP6To,10659
|
|
19
|
-
figrecipe/_wrappers/_figure.py,sha256=ZSCH5jVgZbb9kgXyj_LumV52v5vspmRQ3N1KzY37CaQ,6491
|
|
20
|
-
figrecipe/styles/__init__.py,sha256=vGKtlJkUi06i0QC4qEw3ebUwpxTgablFUlyABSYr1Co,976
|
|
21
|
-
figrecipe/styles/_style_applier.py,sha256=f0iPzV5IF2yy94yirVYD9hd7op1EDYQqo8xcL2qBZVU,14238
|
|
22
|
-
figrecipe/styles/_style_loader.py,sha256=4LazXQzZRmRsWj3TvtDgCXP_Mp5nJca9bXmMH7LnsHw,13342
|
|
23
|
-
figrecipe-0.5.0.dist-info/METADATA,sha256=WbJp4fSSIse1uB_1c1ar2KL3ktYBVznKA_78fYFusFk,8924
|
|
24
|
-
figrecipe-0.5.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
25
|
-
figrecipe-0.5.0.dist-info/licenses/LICENSE,sha256=TfPDBt3ar0uv_f9cqCDMZ5rIzW3CY8anRRd4PkL6ejs,34522
|
|
26
|
-
figrecipe-0.5.0.dist-info/RECORD,,
|