mgplot 0.1.1__tar.gz → 0.1.3__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.
- {mgplot-0.1.1/src/mgplot.egg-info → mgplot-0.1.3}/PKG-INFO +4 -3
- {mgplot-0.1.1 → mgplot-0.1.3}/README.md +2 -2
- {mgplot-0.1.1 → mgplot-0.1.3}/pyproject.toml +3 -2
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot/__init__.py +3 -9
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot/bar_plot.py +13 -4
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot/finalise_plot.py +34 -4
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot/finalisers.py +13 -39
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot/growth_plot.py +30 -12
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot/line_plot.py +17 -15
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot/multi_plot.py +36 -15
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot/postcovid_plot.py +24 -15
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot/revision_plot.py +7 -5
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot/run_plot.py +21 -12
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot/seastrend_plot.py +8 -5
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot/summary_plot.py +21 -6
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot/utilities.py +7 -4
- {mgplot-0.1.1 → mgplot-0.1.3/src/mgplot.egg-info}/PKG-INFO +4 -3
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot.egg-info/requires.txt +1 -0
- {mgplot-0.1.1 → mgplot-0.1.3}/LICENSE +0 -0
- {mgplot-0.1.1 → mgplot-0.1.3}/setup.cfg +0 -0
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot/colors.py +0 -0
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot/date_utils.py +0 -0
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot/kw_type_checking.py +0 -0
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot/py.typed +0 -0
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot/settings.py +0 -0
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot/test.py +0 -0
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot.egg-info/SOURCES.txt +0 -0
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot.egg-info/dependency_links.txt +0 -0
- {mgplot-0.1.1 → mgplot-0.1.3}/src/mgplot.egg-info/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mgplot
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary: mgplot is a frontend for matplotlib
|
|
3
|
+
Version: 0.1.3
|
|
4
|
+
Summary: mgplot is a time-series/PeriodIndex frontend for matplotlib
|
|
5
5
|
Project-URL: Homepage, https://github.com/bpalmer4/mgplot
|
|
6
6
|
Requires-Python: >=3.11
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
@@ -18,6 +18,7 @@ Requires-Dist: pdoc
|
|
|
18
18
|
Requires-Dist: pylint
|
|
19
19
|
Requires-Dist: ruff
|
|
20
20
|
Requires-Dist: pandas-stubs
|
|
21
|
+
Requires-Dist: numpy-typing
|
|
21
22
|
Requires-Dist: types-tabulate
|
|
22
23
|
Provides-Extra: build
|
|
23
24
|
Requires-Dist: setuptools; extra == "build"
|
|
@@ -64,7 +65,7 @@ The remaining arrguments are passed as keyword arguments:
|
|
|
64
65
|
- summary_plot() -- plots the latest data in a summary-format against
|
|
65
66
|
the range of previous data.
|
|
66
67
|
|
|
67
|
-
Once a plot has been generated and
|
|
68
|
+
Once a plot has been generated and an Axes object is available. The
|
|
68
69
|
plot can be finalised or published, with appropriate titles and
|
|
69
70
|
axis labels using
|
|
70
71
|
- finalise_plot()
|
|
@@ -38,7 +38,7 @@ The remaining arrguments are passed as keyword arguments:
|
|
|
38
38
|
- summary_plot() -- plots the latest data in a summary-format against
|
|
39
39
|
the range of previous data.
|
|
40
40
|
|
|
41
|
-
Once a plot has been generated and
|
|
41
|
+
Once a plot has been generated and an Axes object is available. The
|
|
42
42
|
plot can be finalised or published, with appropriate titles and
|
|
43
43
|
axis labels using
|
|
44
44
|
- finalise_plot()
|
|
@@ -69,4 +69,4 @@ function:
|
|
|
69
69
|
|
|
70
70
|
For more details, see the documentation folder.
|
|
71
71
|
|
|
72
|
-
---
|
|
72
|
+
---
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mgplot"
|
|
3
|
-
version = "0.1.
|
|
4
|
-
description = "mgplot is a frontend for matplotlib"
|
|
3
|
+
version = "0.1.3"
|
|
4
|
+
description = "mgplot is a time-series/PeriodIndex frontend for matplotlib"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
7
7
|
|
|
@@ -25,6 +25,7 @@ dependencies = [
|
|
|
25
25
|
|
|
26
26
|
# - typing
|
|
27
27
|
"pandas-stubs",
|
|
28
|
+
"numpy-typing",
|
|
28
29
|
"types-tabulate",
|
|
29
30
|
]
|
|
30
31
|
|
|
@@ -8,7 +8,7 @@ with timeseries data that is indexed with a PeriodIndex.
|
|
|
8
8
|
|
|
9
9
|
# --- version and author
|
|
10
10
|
# NOTE: update version number here (below) and in pyproject.toml
|
|
11
|
-
__version__ = "0.1.
|
|
11
|
+
__version__ = "0.1.3"
|
|
12
12
|
__author__ = "Bryan Palmer"
|
|
13
13
|
|
|
14
14
|
|
|
@@ -27,7 +27,8 @@ from mgplot.growth_plot import (
|
|
|
27
27
|
calc_growth,
|
|
28
28
|
raw_growth_plot,
|
|
29
29
|
series_growth_plot,
|
|
30
|
-
|
|
30
|
+
SERIES_GROWTH_KW_TYPES,
|
|
31
|
+
RAW_GROWTH_KW_TYPES,
|
|
31
32
|
)
|
|
32
33
|
from mgplot.multi_plot import (
|
|
33
34
|
multi_start,
|
|
@@ -62,13 +63,7 @@ from mgplot.finalisers import (
|
|
|
62
63
|
)
|
|
63
64
|
|
|
64
65
|
|
|
65
|
-
# --- version and author
|
|
66
|
-
__version__ = "0.0.1"
|
|
67
|
-
__author__ = "Bryan Palmer"
|
|
68
|
-
|
|
69
|
-
|
|
70
66
|
# --- public API
|
|
71
|
-
SERIES_GROWTH_KW_TYPES = RAW_GROWTH_KW_TYPES = GROWTH_KW_TYPES
|
|
72
67
|
__all__ = (
|
|
73
68
|
"__version__",
|
|
74
69
|
"__author__",
|
|
@@ -129,7 +124,6 @@ __all__ = (
|
|
|
129
124
|
"REVISION_KW_TYPES",
|
|
130
125
|
"RUN_KW_TYPES",
|
|
131
126
|
"SUMMARY_KW_TYPES",
|
|
132
|
-
"GROWTH_KW_TYPES",
|
|
133
127
|
"SERIES_GROWTH_KW_TYPES",
|
|
134
128
|
"RAW_GROWTH_KW_TYPES",
|
|
135
129
|
# --- The rest are internal use only
|
|
@@ -16,7 +16,12 @@ from matplotlib.pyplot import Axes
|
|
|
16
16
|
|
|
17
17
|
from mgplot.settings import DataT, get_setting
|
|
18
18
|
from mgplot.utilities import apply_defaults, get_color_list, get_axes, constrain_data
|
|
19
|
-
from mgplot.kw_type_checking import
|
|
19
|
+
from mgplot.kw_type_checking import (
|
|
20
|
+
ExpectedTypeDict,
|
|
21
|
+
validate_expected,
|
|
22
|
+
report_kwargs,
|
|
23
|
+
validate_kwargs,
|
|
24
|
+
)
|
|
20
25
|
from mgplot.date_utils import set_labels
|
|
21
26
|
|
|
22
27
|
|
|
@@ -61,11 +66,15 @@ def bar_plot(
|
|
|
61
66
|
- axes: Axes - The axes for the plot.
|
|
62
67
|
"""
|
|
63
68
|
|
|
64
|
-
# ---
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
# --- check the kwargs
|
|
70
|
+
me = "bar_plot"
|
|
71
|
+
report_kwargs(called_from=me, **kwargs)
|
|
72
|
+
validate_kwargs(BAR_KW_TYPES, me, **kwargs)
|
|
67
73
|
|
|
68
74
|
# --- get the data
|
|
75
|
+
# no call to check_clean_timeseries here, as bar plots are not
|
|
76
|
+
# necessarily timeseries data. If the data is a Series, it will be
|
|
77
|
+
# converted to a DataFrame with a single column.
|
|
69
78
|
df = DataFrame(data) # really we are only plotting DataFrames
|
|
70
79
|
df, kwargs = constrain_data(df, **kwargs)
|
|
71
80
|
item_count = len(df.columns)
|
|
@@ -5,7 +5,7 @@ file system. It is used to publish plots.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
# --- imports
|
|
8
|
-
from typing import Final
|
|
8
|
+
from typing import Final, Any
|
|
9
9
|
import re
|
|
10
10
|
import matplotlib as mpl
|
|
11
11
|
import matplotlib.pyplot as plt
|
|
@@ -121,6 +121,22 @@ _internal_consistency_kwargs()
|
|
|
121
121
|
# - private utility functions for finalise_plot()
|
|
122
122
|
|
|
123
123
|
|
|
124
|
+
def make_legend(axes: Axes, legend: None | bool | dict[str, Any]) -> None:
|
|
125
|
+
"""Create a legend for the plot."""
|
|
126
|
+
|
|
127
|
+
if legend is None or legend is False:
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
if legend is True: # use the global default settings
|
|
131
|
+
legend = get_setting("legend")
|
|
132
|
+
|
|
133
|
+
if isinstance(legend, dict):
|
|
134
|
+
axes.legend(**legend)
|
|
135
|
+
return
|
|
136
|
+
|
|
137
|
+
print(f"Warning: expected dict argument for legend, but got {type(legend)}.")
|
|
138
|
+
|
|
139
|
+
|
|
124
140
|
def _apply_value_kwargs(axes: Axes, settings: tuple, **kwargs) -> None:
|
|
125
141
|
"""Set matplotlib elements by name using Axes.set()."""
|
|
126
142
|
|
|
@@ -128,6 +144,9 @@ def _apply_value_kwargs(axes: Axes, settings: tuple, **kwargs) -> None:
|
|
|
128
144
|
value = kwargs.get(setting, None)
|
|
129
145
|
if value is None and setting not in _value_must_kwargs:
|
|
130
146
|
continue
|
|
147
|
+
if setting == "ylabel" and value is None and axes.get_ylabel():
|
|
148
|
+
# already set - probably in series_growth_plot() - so skip
|
|
149
|
+
continue
|
|
131
150
|
axes.set(**{setting: value})
|
|
132
151
|
|
|
133
152
|
|
|
@@ -141,6 +160,11 @@ def _apply_splat_kwargs(axes: Axes, settings: tuple, **kwargs) -> None:
|
|
|
141
160
|
for method_name in settings:
|
|
142
161
|
if method_name in kwargs:
|
|
143
162
|
|
|
163
|
+
if method_name == "legend":
|
|
164
|
+
# special case for legend
|
|
165
|
+
make_legend(axes, kwargs[method_name])
|
|
166
|
+
continue
|
|
167
|
+
|
|
144
168
|
if kwargs[method_name] is None or kwargs[method_name] is False:
|
|
145
169
|
continue
|
|
146
170
|
|
|
@@ -153,7 +177,7 @@ def _apply_splat_kwargs(axes: Axes, settings: tuple, **kwargs) -> None:
|
|
|
153
177
|
method(**kwargs[method_name])
|
|
154
178
|
else:
|
|
155
179
|
print(
|
|
156
|
-
f"Warning expected dict argument
|
|
180
|
+
f"Warning expected dict argument for {method_name} but got "
|
|
157
181
|
+ f"{type(kwargs[method_name])}."
|
|
158
182
|
)
|
|
159
183
|
|
|
@@ -297,9 +321,15 @@ def finalise_plot(axes: Axes, **kwargs) -> None:
|
|
|
297
321
|
- None
|
|
298
322
|
"""
|
|
299
323
|
|
|
324
|
+
# --- check the kwargs
|
|
325
|
+
me = "finalise_plot"
|
|
326
|
+
report_kwargs(called_from=me, **kwargs)
|
|
327
|
+
validate_kwargs(FINALISE_KW_TYPES, me, **kwargs)
|
|
328
|
+
|
|
300
329
|
# --- sanity checks
|
|
301
|
-
|
|
302
|
-
|
|
330
|
+
if len(axes.get_children()) < 1:
|
|
331
|
+
print("Warning: finalise_plot() called with empty axes, which was ignored.")
|
|
332
|
+
return
|
|
303
333
|
|
|
304
334
|
# --- remember should we need to restore the axes limits
|
|
305
335
|
xlim, ylim = axes.get_xlim(), axes.get_ylim()
|
|
@@ -149,8 +149,6 @@ def series_growth_plot_finalise(data: DataT, **kwargs) -> None:
|
|
|
149
149
|
the growth series.
|
|
150
150
|
"""
|
|
151
151
|
|
|
152
|
-
kwargs["ylabel"] = kwargs.get("ylabel", "Per cent Growth")
|
|
153
|
-
kwargs["xlabel"] = kwargs.get("xlabel", None)
|
|
154
152
|
plot_then_finalise(
|
|
155
153
|
data=data,
|
|
156
154
|
function=series_growth_plot,
|
|
@@ -165,8 +163,6 @@ def raw_growth_plot_finalise(data: DataT, **kwargs) -> None:
|
|
|
165
163
|
set the ylabel in kwargs.
|
|
166
164
|
"""
|
|
167
165
|
|
|
168
|
-
kwargs["ylabel"] = kwargs.get("ylabel", "Growth Units unspecified")
|
|
169
|
-
kwargs["xlabel"] = kwargs.get("xlabel", None)
|
|
170
166
|
plot_then_finalise(
|
|
171
167
|
data=data,
|
|
172
168
|
function=raw_growth_plot,
|
|
@@ -192,46 +188,24 @@ def summary_plot_finalise(
|
|
|
192
188
|
defaults to "zscores".
|
|
193
189
|
"""
|
|
194
190
|
|
|
195
|
-
# --- sanity checks
|
|
196
|
-
if not isinstance(data.index, PeriodIndex):
|
|
197
|
-
raise ValueError("data must have a PeriodIndex")
|
|
198
|
-
|
|
199
191
|
# --- standard arguments
|
|
200
|
-
kwargs["
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
192
|
+
kwargs["title"] = kwargs.get("title", f"Summary at {data.index[-1]}")
|
|
193
|
+
kwargs["preserve_lims"] = kwargs.get(
|
|
194
|
+
"preserve_lims", True
|
|
195
|
+
) # preserve the x-axis limits
|
|
196
|
+
|
|
197
|
+
start: None | int | Period = kwargs.get("plot_from", None)
|
|
198
|
+
if start is None:
|
|
199
|
+
start = data.index[0]
|
|
200
|
+
if isinstance(start, int):
|
|
201
|
+
start = data.index[start]
|
|
202
|
+
kwargs["plot_from"] = start
|
|
211
203
|
|
|
212
204
|
for plot_type in (ZSCORES, ZSCALED):
|
|
213
205
|
# some sorting of kwargs for plot production
|
|
214
206
|
kwargs["plot_type"] = plot_type
|
|
215
|
-
kwargs["
|
|
216
|
-
|
|
217
|
-
kwargs["preserve_lims"] = kwargs.get(
|
|
218
|
-
"preserve_lims", True
|
|
219
|
-
) # preserve the x-axis limits
|
|
220
|
-
|
|
221
|
-
# get the start date for the plot
|
|
222
|
-
set_default = start is None
|
|
223
|
-
if isinstance(start, int):
|
|
224
|
-
start = data.index[start]
|
|
225
|
-
if set_default:
|
|
226
|
-
freq = data.index.freqstr[0]
|
|
227
|
-
if freq not in ("D", "M", "Q"):
|
|
228
|
-
raise ValueError(f"Unknown frequency {freq} for data index")
|
|
229
|
-
start = Period("1995-01-01", freq=data.index.freqstr)
|
|
230
|
-
kwargs["plot_from"] = start
|
|
231
|
-
|
|
232
|
-
if plot_type not in (ZSCORES, ZSCALED):
|
|
233
|
-
print(f"Unknown plot type {plot_type}, defaulting to {ZSCORES}")
|
|
234
|
-
plot_type = ZSCORES
|
|
207
|
+
kwargs["pre_tag"] = plot_type # necessary because the title is the same
|
|
208
|
+
|
|
235
209
|
if plot_type == "zscores":
|
|
236
210
|
kwargs["xlabel"] = f"Z-scores for prints since {start}"
|
|
237
211
|
kwargs["x0"] = True
|
|
@@ -14,6 +14,7 @@ from matplotlib.pyplot import Axes
|
|
|
14
14
|
import matplotlib.patheffects as pe
|
|
15
15
|
from tabulate import tabulate
|
|
16
16
|
|
|
17
|
+
from mgplot.finalise_plot import make_legend
|
|
17
18
|
from mgplot.test import prepare_for_test
|
|
18
19
|
from mgplot.settings import get_setting, DataT
|
|
19
20
|
from mgplot.date_utils import set_labels
|
|
@@ -30,7 +31,7 @@ from mgplot.kw_type_checking import (
|
|
|
30
31
|
ANNUAL = "annual"
|
|
31
32
|
PERIODIC = "periodic"
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
RAW_GROWTH_KW_TYPES: Final[ExpectedTypeDict] = {
|
|
34
35
|
"line_width": (float, int),
|
|
35
36
|
"line_color": str,
|
|
36
37
|
"line_style": str,
|
|
@@ -41,9 +42,13 @@ GROWTH_KW_TYPES: Final[ExpectedTypeDict] = {
|
|
|
41
42
|
"annotation_rounding": int,
|
|
42
43
|
"plot_from": (type(None), Period, int),
|
|
43
44
|
"max_ticks": int,
|
|
45
|
+
"legend": (type(None), bool, dict, (str, object)),
|
|
44
46
|
}
|
|
45
|
-
validate_expected(
|
|
46
|
-
|
|
47
|
+
validate_expected(RAW_GROWTH_KW_TYPES, "growth_plot")
|
|
48
|
+
SERIES_GROWTH_KW_TYPES: Final[ExpectedTypeDict] = {
|
|
49
|
+
"ylabel": (str, type(None)),
|
|
50
|
+
} | RAW_GROWTH_KW_TYPES
|
|
51
|
+
validate_expected(SERIES_GROWTH_KW_TYPES, "growth_plot")
|
|
47
52
|
|
|
48
53
|
|
|
49
54
|
# --- functions
|
|
@@ -183,10 +188,13 @@ def raw_growth_plot(
|
|
|
183
188
|
- ValueError if the annual and periodic series do not have the same index.
|
|
184
189
|
"""
|
|
185
190
|
|
|
186
|
-
# ---
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
191
|
+
# --- check the kwargs
|
|
192
|
+
me = "raw_growth_plot"
|
|
193
|
+
report_kwargs(called_from=me, **kwargs)
|
|
194
|
+
validate_kwargs(RAW_GROWTH_KW_TYPES, me, **kwargs)
|
|
195
|
+
|
|
196
|
+
# --- data checks
|
|
197
|
+
data = check_clean_timeseries(data, me)
|
|
190
198
|
if len(data.columns) != 2:
|
|
191
199
|
raise TypeError("The data argument must be a pandas DataFrame with two columns")
|
|
192
200
|
|
|
@@ -228,7 +236,10 @@ def raw_growth_plot(
|
|
|
228
236
|
linestyle=kwargs.get("line_style", "-"),
|
|
229
237
|
)
|
|
230
238
|
_annotations(annual, periodic, axes, **kwargs)
|
|
231
|
-
|
|
239
|
+
|
|
240
|
+
# --- expose the legend by default
|
|
241
|
+
legend = kwargs.get("legend", True)
|
|
242
|
+
make_legend(axes, legend)
|
|
232
243
|
|
|
233
244
|
# --- fix the x-axis labels
|
|
234
245
|
set_labels(axes, save_index, kwargs.get("max_ticks", 10))
|
|
@@ -251,18 +262,25 @@ def series_growth_plot(
|
|
|
251
262
|
- takes the same kwargs as for growth_plot()
|
|
252
263
|
"""
|
|
253
264
|
|
|
265
|
+
# --- check the kwargs
|
|
266
|
+
me = "series_growth_plot"
|
|
267
|
+
report_kwargs(called_from=me, **kwargs)
|
|
268
|
+
validate_kwargs(SERIES_GROWTH_KW_TYPES, me, **kwargs)
|
|
269
|
+
|
|
254
270
|
# --- sanity checks
|
|
255
|
-
report_kwargs(called_from="series_growth_plot", **kwargs)
|
|
256
|
-
data = check_clean_timeseries(data)
|
|
257
|
-
# we will validate kwargs in raw_growth_plot()
|
|
258
271
|
if not isinstance(data, Series):
|
|
259
272
|
raise TypeError(
|
|
260
273
|
"The data argument to series_growth_plot() must be a pandas Series"
|
|
261
274
|
)
|
|
262
275
|
|
|
263
|
-
# --- calculate growth and plot
|
|
276
|
+
# --- calculate growth and plot - add ylabel
|
|
277
|
+
ylabel: str | None = kwargs.pop("ylabel", None)
|
|
278
|
+
if ylabel is not None:
|
|
279
|
+
print(f"Did you intend to specify a value for the 'ylabel' in {me}()?")
|
|
280
|
+
ylabel = "Growth (%)" if ylabel is None else ylabel
|
|
264
281
|
growth = calc_growth(data)
|
|
265
282
|
ax = raw_growth_plot(growth, **kwargs)
|
|
283
|
+
ax.set_ylabel(ylabel)
|
|
266
284
|
return ax
|
|
267
285
|
|
|
268
286
|
|
|
@@ -10,6 +10,7 @@ import matplotlib.pyplot as plt
|
|
|
10
10
|
from pandas import DataFrame, Period
|
|
11
11
|
|
|
12
12
|
from mgplot.settings import DataT, get_setting
|
|
13
|
+
from mgplot.finalise_plot import make_legend
|
|
13
14
|
from mgplot.kw_type_checking import (
|
|
14
15
|
report_kwargs,
|
|
15
16
|
validate_kwargs,
|
|
@@ -119,25 +120,28 @@ def line_plot(data: DataT, **kwargs) -> plt.Axes:
|
|
|
119
120
|
- axes: plt.Axes - the axes object for the plot
|
|
120
121
|
"""
|
|
121
122
|
|
|
122
|
-
#
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
validate_kwargs(LINE_KW_TYPES,
|
|
123
|
+
# --- check the kwargs
|
|
124
|
+
me = "line_plot"
|
|
125
|
+
report_kwargs(called_from=me, **kwargs)
|
|
126
|
+
validate_kwargs(LINE_KW_TYPES, me, **kwargs)
|
|
126
127
|
|
|
127
|
-
# the data
|
|
128
|
+
# --- check the data
|
|
129
|
+
data = check_clean_timeseries(data, me)
|
|
128
130
|
df = DataFrame(data) # really we are only plotting DataFrames
|
|
129
131
|
df, kwargs = constrain_data(df, **kwargs)
|
|
130
|
-
|
|
132
|
+
|
|
133
|
+
# --- Let's plot
|
|
134
|
+
axes, kwargs = get_axes(**kwargs) # get the axes to plot on
|
|
135
|
+
if df.empty or df.isna().all().all():
|
|
136
|
+
# Note: finalise plot will ignore an empty axes object
|
|
131
137
|
print("Warning: No data to plot.")
|
|
138
|
+
return axes
|
|
132
139
|
|
|
133
|
-
# get the arguments for each line we will plot ...
|
|
140
|
+
# --- get the arguments for each line we will plot ...
|
|
134
141
|
item_count = len(df.columns)
|
|
135
142
|
num_data_points = len(df)
|
|
136
143
|
swce, kwargs = _get_style_width_color_etc(item_count, num_data_points, **kwargs)
|
|
137
144
|
|
|
138
|
-
# Let's plot
|
|
139
|
-
axes, kwargs = get_axes(**kwargs) # get the axes to plot on
|
|
140
|
-
|
|
141
145
|
for i, column in enumerate(df.columns):
|
|
142
146
|
series = df[column]
|
|
143
147
|
series = series.dropna() if DROPNA in swce and swce[DROPNA][i] else series
|
|
@@ -169,10 +173,8 @@ def line_plot(data: DataT, **kwargs) -> plt.Axes:
|
|
|
169
173
|
# add a legend if requested
|
|
170
174
|
if len(df.columns) > 1:
|
|
171
175
|
kwargs[LEGEND] = kwargs.get(LEGEND, get_setting("legend"))
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
legend = get_setting("legend")
|
|
176
|
-
axes.legend(**legend)
|
|
176
|
+
|
|
177
|
+
if LEGEND in kwargs:
|
|
178
|
+
make_legend(axes, kwargs[LEGEND])
|
|
177
179
|
|
|
178
180
|
return axes
|
|
@@ -45,11 +45,11 @@ from mgplot.kw_type_checking import (
|
|
|
45
45
|
limit_kwargs,
|
|
46
46
|
ExpectedTypeDict,
|
|
47
47
|
report_kwargs,
|
|
48
|
+
validate_kwargs,
|
|
48
49
|
)
|
|
49
50
|
from mgplot.finalise_plot import finalise_plot, FINALISE_KW_TYPES
|
|
50
51
|
from mgplot.settings import DataT
|
|
51
52
|
from mgplot.test import prepare_for_test
|
|
52
|
-
from mgplot.utilities import check_clean_timeseries
|
|
53
53
|
|
|
54
54
|
from mgplot.line_plot import line_plot, LINE_KW_TYPES
|
|
55
55
|
from mgplot.bar_plot import bar_plot, BAR_KW_TYPES
|
|
@@ -58,8 +58,12 @@ from mgplot.postcovid_plot import postcovid_plot, POSTCOVID_KW_TYPES
|
|
|
58
58
|
from mgplot.revision_plot import revision_plot, REVISION_KW_TYPES
|
|
59
59
|
from mgplot.run_plot import run_plot, RUN_KW_TYPES
|
|
60
60
|
from mgplot.summary_plot import summary_plot, SUMMARY_KW_TYPES
|
|
61
|
-
from mgplot.growth_plot import
|
|
62
|
-
|
|
61
|
+
from mgplot.growth_plot import (
|
|
62
|
+
series_growth_plot,
|
|
63
|
+
raw_growth_plot,
|
|
64
|
+
RAW_GROWTH_KW_TYPES,
|
|
65
|
+
SERIES_GROWTH_KW_TYPES,
|
|
66
|
+
)
|
|
63
67
|
|
|
64
68
|
# --- constants
|
|
65
69
|
EXPECTED_CALLABLES: Final[dict[Callable, ExpectedTypeDict]] = {
|
|
@@ -73,8 +77,8 @@ EXPECTED_CALLABLES: Final[dict[Callable, ExpectedTypeDict]] = {
|
|
|
73
77
|
revision_plot: REVISION_KW_TYPES,
|
|
74
78
|
run_plot: RUN_KW_TYPES,
|
|
75
79
|
summary_plot: SUMMARY_KW_TYPES,
|
|
76
|
-
series_growth_plot:
|
|
77
|
-
raw_growth_plot:
|
|
80
|
+
series_growth_plot: SERIES_GROWTH_KW_TYPES,
|
|
81
|
+
raw_growth_plot: RAW_GROWTH_KW_TYPES,
|
|
78
82
|
}
|
|
79
83
|
|
|
80
84
|
|
|
@@ -139,9 +143,13 @@ def plot_then_finalise(
|
|
|
139
143
|
Returns None.
|
|
140
144
|
"""
|
|
141
145
|
|
|
142
|
-
# ---
|
|
143
|
-
|
|
144
|
-
|
|
146
|
+
# --- checks
|
|
147
|
+
me = "plot_then_finalise"
|
|
148
|
+
report_kwargs(called_from=me, **kwargs)
|
|
149
|
+
# validate once we have established the first function
|
|
150
|
+
|
|
151
|
+
# data is not checked here, assume it is checked by the called
|
|
152
|
+
# plot function.
|
|
145
153
|
|
|
146
154
|
# --- check the function argument
|
|
147
155
|
first, kwargs_ = first_unchain(function, **kwargs)
|
|
@@ -152,14 +160,21 @@ def plot_then_finalise(
|
|
|
152
160
|
else:
|
|
153
161
|
# this is an unexpected Callable, so we will give it a try
|
|
154
162
|
print(f"Unknown proposed function: {first}; nonetheless, will give it a try.")
|
|
163
|
+
expected = {}
|
|
155
164
|
plot_kwargs = kwargs_
|
|
165
|
+
validate_kwargs(expected | FINALISE_KW_TYPES, me, **plot_kwargs)
|
|
156
166
|
|
|
157
167
|
# --- call the first function with the data and kwargs
|
|
158
|
-
|
|
159
168
|
axes = first(data, **plot_kwargs)
|
|
160
169
|
|
|
161
|
-
# ---
|
|
170
|
+
# --- remove potentially overlapping kwargs
|
|
162
171
|
fp_kwargs = limit_kwargs(FINALISE_KW_TYPES, **kwargs)
|
|
172
|
+
overlapping = expected.keys() & FINALISE_KW_TYPES.keys()
|
|
173
|
+
if overlapping:
|
|
174
|
+
for key in overlapping:
|
|
175
|
+
fp_kwargs.pop(key, None) # remove overlapping keys from kwargs
|
|
176
|
+
|
|
177
|
+
# --- finalise the plot
|
|
163
178
|
finalise_plot(axes, **fp_kwargs)
|
|
164
179
|
|
|
165
180
|
|
|
@@ -191,10 +206,12 @@ def multi_start(
|
|
|
191
206
|
"""
|
|
192
207
|
|
|
193
208
|
# --- sanity checks
|
|
194
|
-
|
|
195
|
-
|
|
209
|
+
me = "multi_start"
|
|
210
|
+
report_kwargs(called_from=me, **kwargs)
|
|
196
211
|
if not isinstance(starts, Iterable):
|
|
197
212
|
raise ValueError("starts must be an iterable of None, Period or int")
|
|
213
|
+
# data not checked here, assume it is checked by the called
|
|
214
|
+
# plot function.
|
|
198
215
|
|
|
199
216
|
# --- check the function argument
|
|
200
217
|
original_tag: Final[str] = kwargs.get("tag", "")
|
|
@@ -219,7 +236,7 @@ def multi_column(
|
|
|
219
236
|
The plot title will be the column name.
|
|
220
237
|
|
|
221
238
|
Parameters
|
|
222
|
-
- data: DataFrame - The data to be plotted
|
|
239
|
+
- data: DataFrame - The data to be plotted
|
|
223
240
|
- function: Callable - The plotting function to be used.
|
|
224
241
|
- **kwargs: Additional keyword arguments to be passed to
|
|
225
242
|
the plotting function.
|
|
@@ -228,8 +245,12 @@ def multi_column(
|
|
|
228
245
|
"""
|
|
229
246
|
|
|
230
247
|
# --- sanity checks
|
|
231
|
-
|
|
232
|
-
|
|
248
|
+
me = "multi_column"
|
|
249
|
+
report_kwargs(called_from=me, **kwargs)
|
|
250
|
+
if not isinstance(data, DataFrame):
|
|
251
|
+
raise TypeError("data must be a pandas DataFrame for multi_column()")
|
|
252
|
+
# Otherwise, the data is assumed to be checked by the called
|
|
253
|
+
# plot function, so we do not check it here.
|
|
233
254
|
|
|
234
255
|
# --- check the function argument
|
|
235
256
|
title_stem = kwargs.get("title", "")
|
|
@@ -4,29 +4,31 @@ Plot the pre-COVID trajectory against the current trend.
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
# --- imports
|
|
7
|
-
from collections.abc import Sequence
|
|
8
7
|
from pandas import DataFrame, Series, Period, PeriodIndex
|
|
9
8
|
from matplotlib.pyplot import Axes
|
|
10
9
|
from numpy import arange, polyfit
|
|
11
10
|
|
|
12
11
|
from mgplot.settings import DataT, get_setting
|
|
13
|
-
from mgplot.line_plot import line_plot
|
|
12
|
+
from mgplot.line_plot import line_plot, LINE_KW_TYPES
|
|
14
13
|
from mgplot.utilities import check_clean_timeseries
|
|
15
|
-
from mgplot.kw_type_checking import
|
|
14
|
+
from mgplot.kw_type_checking import (
|
|
15
|
+
ExpectedTypeDict,
|
|
16
|
+
validate_kwargs,
|
|
17
|
+
validate_expected,
|
|
18
|
+
report_kwargs,
|
|
19
|
+
)
|
|
16
20
|
|
|
17
21
|
|
|
18
22
|
# --- constants
|
|
19
|
-
WIDTH = "width"
|
|
20
|
-
STYLE = "style"
|
|
21
23
|
START_R = "start_r"
|
|
22
24
|
END_R = "end_r"
|
|
25
|
+
WIDTH = "width"
|
|
26
|
+
STYLE = "style"
|
|
23
27
|
|
|
24
28
|
POSTCOVID_KW_TYPES: ExpectedTypeDict = {
|
|
25
|
-
WIDTH: (Sequence, (int, float), int, float),
|
|
26
|
-
STYLE: (Sequence, (str,), str),
|
|
27
29
|
START_R: Period,
|
|
28
30
|
END_R: Period,
|
|
29
|
-
}
|
|
31
|
+
} | LINE_KW_TYPES
|
|
30
32
|
validate_expected(POSTCOVID_KW_TYPES, "postcovid_plot")
|
|
31
33
|
|
|
32
34
|
|
|
@@ -64,9 +66,13 @@ def postcovid_plot(data: DataT, **kwargs) -> Axes:
|
|
|
64
66
|
- ValueError if regression start is after regression end
|
|
65
67
|
"""
|
|
66
68
|
|
|
67
|
-
# ---
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
# --- check the kwargs
|
|
70
|
+
me = "postcovid_plot"
|
|
71
|
+
report_kwargs(called_from=me, **kwargs)
|
|
72
|
+
validate_kwargs(POSTCOVID_KW_TYPES, me, **kwargs)
|
|
73
|
+
|
|
74
|
+
# --- check the data
|
|
75
|
+
data = check_clean_timeseries(data, me)
|
|
70
76
|
if not isinstance(data, Series):
|
|
71
77
|
raise TypeError("The series argument must be a pandas Series")
|
|
72
78
|
series: Series = data
|
|
@@ -103,11 +109,14 @@ def postcovid_plot(data: DataT, **kwargs) -> Axes:
|
|
|
103
109
|
projection.name = "Pre-COVID projection"
|
|
104
110
|
data_set = DataFrame([projection, recent]).T
|
|
105
111
|
|
|
112
|
+
# --- activate plot settings
|
|
106
113
|
kwargs[WIDTH] = kwargs.pop(
|
|
107
|
-
WIDTH,
|
|
108
|
-
)
|
|
109
|
-
kwargs[STYLE] = kwargs.pop(STYLE,
|
|
110
|
-
kwargs["legend"] = kwargs.pop("legend", True)
|
|
114
|
+
WIDTH, (get_setting("line_normal"), get_setting("line_wide"))
|
|
115
|
+
) # series line is thicker than projection
|
|
116
|
+
kwargs[STYLE] = kwargs.pop(STYLE, ("--", "-")) # dashed regression line
|
|
117
|
+
kwargs["legend"] = kwargs.pop("legend", True) # show legend by default
|
|
118
|
+
kwargs["annotate"] = kwargs.pop("annotate", (False, True)) # annotate series only
|
|
119
|
+
kwargs["color"] = kwargs.pop("color", ("darkblue", "#dd0000"))
|
|
111
120
|
|
|
112
121
|
return line_plot(
|
|
113
122
|
data_set,
|
|
@@ -40,13 +40,15 @@ def revision_plot(data: DataT, **kwargs) -> Axes:
|
|
|
40
40
|
apply int rounding.
|
|
41
41
|
"""
|
|
42
42
|
|
|
43
|
-
# ---
|
|
44
|
-
|
|
45
|
-
report_kwargs(called_from=
|
|
46
|
-
validate_kwargs(REVISION_KW_TYPES,
|
|
43
|
+
# --- check the kwargs and data
|
|
44
|
+
me = "revision_plot"
|
|
45
|
+
report_kwargs(called_from=me, **kwargs)
|
|
46
|
+
validate_kwargs(REVISION_KW_TYPES, me, **kwargs)
|
|
47
|
+
|
|
48
|
+
data = check_clean_timeseries(data, me)
|
|
47
49
|
|
|
48
50
|
# --- critical defaults
|
|
49
|
-
kwargs["plot_from"] = kwargs.get("plot_from", -
|
|
51
|
+
kwargs["plot_from"] = kwargs.get("plot_from", -19)
|
|
50
52
|
|
|
51
53
|
# --- plot
|
|
52
54
|
axes = line_plot(data, **kwargs)
|
|
@@ -6,7 +6,7 @@ the 'runs' in a series.
|
|
|
6
6
|
|
|
7
7
|
# --- imports
|
|
8
8
|
from collections.abc import Sequence
|
|
9
|
-
from pandas import Series, concat
|
|
9
|
+
from pandas import Series, concat, period_range
|
|
10
10
|
from matplotlib.pyplot import Axes
|
|
11
11
|
from matplotlib import patheffects as pe
|
|
12
12
|
|
|
@@ -105,17 +105,17 @@ def _plot_runs(
|
|
|
105
105
|
),
|
|
106
106
|
va=vert_align,
|
|
107
107
|
ha="left",
|
|
108
|
-
fontsize="small",
|
|
108
|
+
fontsize="x-small",
|
|
109
109
|
rotation=90,
|
|
110
110
|
)
|
|
111
111
|
text.set_path_effects([pe.withStroke(linewidth=5, foreground="w")])
|
|
112
112
|
|
|
113
113
|
|
|
114
|
-
def run_plot(
|
|
114
|
+
def run_plot(data: DataT, **kwargs) -> Axes:
|
|
115
115
|
"""Plot a series of percentage rates, highlighting the increasing runs.
|
|
116
116
|
|
|
117
117
|
Arguments
|
|
118
|
-
-
|
|
118
|
+
- data - ordered pandas Series of percentages, with PeriodIndex
|
|
119
119
|
- **kwargs
|
|
120
120
|
- threshold - float - used to ignore micro noise near zero
|
|
121
121
|
(for example, threshhold=0.01)
|
|
@@ -129,17 +129,17 @@ def run_plot(series: DataT, **kwargs) -> Axes:
|
|
|
129
129
|
Return
|
|
130
130
|
- matplotlib Axes object"""
|
|
131
131
|
|
|
132
|
-
# ---
|
|
133
|
-
|
|
132
|
+
# --- check the kwargs
|
|
133
|
+
me = "run_plot"
|
|
134
|
+
report_kwargs(called_from=me, **kwargs)
|
|
135
|
+
validate_kwargs(RUN_KW_TYPES, me, **kwargs)
|
|
136
|
+
|
|
137
|
+
# --- check the data
|
|
138
|
+
series = check_clean_timeseries(data, me)
|
|
134
139
|
if not isinstance(series, Series):
|
|
135
140
|
raise TypeError("series must be a pandas Series for run_plot()")
|
|
136
141
|
series, kwargs = constrain_data(series, **kwargs)
|
|
137
142
|
|
|
138
|
-
# --- check the kwargs
|
|
139
|
-
report_kwargs(called_from="run_plot", **kwargs)
|
|
140
|
-
expected = RUN_KW_TYPES
|
|
141
|
-
validate_kwargs(expected, "run_plot", **kwargs)
|
|
142
|
-
|
|
143
143
|
# --- default arguments - in **kwargs
|
|
144
144
|
kwargs[THRESHOLD] = kwargs.get(THRESHOLD, 0.1)
|
|
145
145
|
kwargs[ROUND] = kwargs.get(ROUND, 2)
|
|
@@ -162,7 +162,7 @@ def run_plot(series: DataT, **kwargs) -> Axes:
|
|
|
162
162
|
|
|
163
163
|
# plot the line
|
|
164
164
|
kwargs["drawstyle"] = kwargs.get("drawstyle", "steps-post")
|
|
165
|
-
lp_kwargs = limit_kwargs(
|
|
165
|
+
lp_kwargs = limit_kwargs(LINE_KW_TYPES, **kwargs)
|
|
166
166
|
axes = line_plot(series, **lp_kwargs)
|
|
167
167
|
|
|
168
168
|
# plot the runs
|
|
@@ -180,3 +180,12 @@ def run_plot(series: DataT, **kwargs) -> Axes:
|
|
|
180
180
|
"Expected 'up', 'down', or 'both'."
|
|
181
181
|
)
|
|
182
182
|
return axes
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# test ---
|
|
186
|
+
if __name__ == "__main__":
|
|
187
|
+
N_PERIODS = 25
|
|
188
|
+
periods = period_range(start="2020Q1", periods=N_PERIODS, freq="Q")
|
|
189
|
+
dataset = Series([1] * N_PERIODS, index=periods).cumsum()
|
|
190
|
+
|
|
191
|
+
ax = run_plot(data=dataset, junk="should generate a warning")
|
|
@@ -9,7 +9,7 @@ from matplotlib.pyplot import Axes
|
|
|
9
9
|
from mgplot.settings import DataT
|
|
10
10
|
from mgplot.line_plot import line_plot, LINE_KW_TYPES
|
|
11
11
|
from mgplot.utilities import get_color_list, get_setting, check_clean_timeseries
|
|
12
|
-
from mgplot.kw_type_checking import report_kwargs
|
|
12
|
+
from mgplot.kw_type_checking import report_kwargs, validate_kwargs
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
# --- constants
|
|
@@ -44,14 +44,17 @@ def seastrend_plot(data: DataT, **kwargs) -> Axes:
|
|
|
44
44
|
# Note: we will rely on the line_plot() function to do most of the work.
|
|
45
45
|
# including constraining the data to the plot_from keyword argument.
|
|
46
46
|
|
|
47
|
-
# ---
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
# --- check the kwargs
|
|
48
|
+
me = "seastrend_plot"
|
|
49
|
+
report_kwargs(called_from=me, **kwargs)
|
|
50
|
+
validate_kwargs(SEASTREND_KW_TYPES, me, **kwargs)
|
|
51
|
+
|
|
52
|
+
# --- check the data
|
|
53
|
+
data = check_clean_timeseries(data, me)
|
|
50
54
|
if len(data.columns) < 2:
|
|
51
55
|
raise ValueError(
|
|
52
56
|
"seas_trend_plot() expects a DataFrame data item with at least 2 columns."
|
|
53
57
|
)
|
|
54
|
-
# let line_plot() handle validate_kwargs()
|
|
55
58
|
|
|
56
59
|
# --- defaults if not in kwargs
|
|
57
60
|
colors = kwargs.pop(COLOR, get_color_list(2))
|
|
@@ -17,6 +17,7 @@ from pandas import DataFrame, Period
|
|
|
17
17
|
|
|
18
18
|
# local imports
|
|
19
19
|
from mgplot.settings import DataT
|
|
20
|
+
from mgplot.finalise_plot import make_legend
|
|
20
21
|
from mgplot.utilities import constrain_data, check_clean_timeseries
|
|
21
22
|
from mgplot.kw_type_checking import (
|
|
22
23
|
report_kwargs,
|
|
@@ -35,6 +36,7 @@ SUMMARY_KW_TYPES: ExpectedTypeDict = {
|
|
|
35
36
|
"middle": float,
|
|
36
37
|
"plot_type": str,
|
|
37
38
|
"plot_from": (int, Period, type(None)),
|
|
39
|
+
"legend": (type(None), bool, dict, (str, object)),
|
|
38
40
|
}
|
|
39
41
|
validate_expected(SUMMARY_KW_TYPES, "summary_plot")
|
|
40
42
|
|
|
@@ -213,20 +215,31 @@ def summary_plot(
|
|
|
213
215
|
Returns Axes.
|
|
214
216
|
"""
|
|
215
217
|
|
|
216
|
-
# ---
|
|
217
|
-
|
|
218
|
+
# --- check the kwargs
|
|
219
|
+
me = "summary_plot"
|
|
220
|
+
report_kwargs(called_from=me, **kwargs)
|
|
221
|
+
validate_kwargs(SUMMARY_KW_TYPES, me, **kwargs)
|
|
222
|
+
|
|
223
|
+
# --- check the data
|
|
224
|
+
data = check_clean_timeseries(data, me)
|
|
218
225
|
if not isinstance(data, DataFrame):
|
|
219
226
|
raise TypeError("data must be a pandas DataFrame for summary_plot()")
|
|
220
227
|
df = DataFrame(data) # syntactic sugar for type hinting
|
|
221
228
|
|
|
222
|
-
# --- check the arguments
|
|
223
|
-
report_kwargs("summary_plot", **kwargs)
|
|
224
|
-
validate_kwargs(SUMMARY_KW_TYPES, "summary_plot", **kwargs)
|
|
225
|
-
|
|
226
229
|
# --- optional arguments
|
|
227
230
|
verbose = kwargs.pop("verbose", False)
|
|
228
231
|
middle = float(kwargs.pop("middle", 0.8))
|
|
229
232
|
plot_type = kwargs.pop("plot_type", ZSCORES)
|
|
233
|
+
kwargs["legend"] = kwargs.get(
|
|
234
|
+
"legend",
|
|
235
|
+
{
|
|
236
|
+
# put the legend below the x-axis label
|
|
237
|
+
"loc": "upper center",
|
|
238
|
+
"fontsize": "xx-small",
|
|
239
|
+
"bbox_to_anchor": (0.5, -0.125),
|
|
240
|
+
"ncol": 4,
|
|
241
|
+
},
|
|
242
|
+
)
|
|
230
243
|
|
|
231
244
|
# get the data, calculate z-scores and scaled scores based on the start period
|
|
232
245
|
subset, kwargs = constrain_data(df, **kwargs)
|
|
@@ -236,5 +249,7 @@ def summary_plot(
|
|
|
236
249
|
adjusted = z_scores if plot_type == ZSCORES else z_scaled
|
|
237
250
|
ax = _horizontal_bar_plot(subset, adjusted, middle, plot_type, kwargs)
|
|
238
251
|
ax.tick_params(axis="y", labelsize="small")
|
|
252
|
+
make_legend(ax, kwargs["legend"])
|
|
239
253
|
ax.set_xlim(kwargs.get("xlim", None)) # provide space for the labels
|
|
254
|
+
|
|
240
255
|
return ax
|
|
@@ -25,7 +25,7 @@ from mgplot.settings import DataT
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
# --- functions
|
|
28
|
-
def check_clean_timeseries(data: DataT) -> DataT:
|
|
28
|
+
def check_clean_timeseries(data: DataT, called_by: str) -> DataT:
|
|
29
29
|
"""
|
|
30
30
|
Check timeseries data for the following:
|
|
31
31
|
- That the data is a Series or DataFrame.
|
|
@@ -71,7 +71,10 @@ def check_clean_timeseries(data: DataT) -> DataT:
|
|
|
71
71
|
missing = complete.difference(data_index)
|
|
72
72
|
if not missing.empty:
|
|
73
73
|
plural = "s" if len(missing) > 1 else ""
|
|
74
|
-
print(
|
|
74
|
+
print(
|
|
75
|
+
f"Warning: {len(missing)} period{plural} missing from data index. "
|
|
76
|
+
+ f"Found by {called_by}."
|
|
77
|
+
)
|
|
75
78
|
|
|
76
79
|
# --- return the final data
|
|
77
80
|
return data
|
|
@@ -79,7 +82,7 @@ def check_clean_timeseries(data: DataT) -> DataT:
|
|
|
79
82
|
|
|
80
83
|
def constrain_data(data: DataT, **kwargs) -> tuple[DataT, dict[str, Any]]:
|
|
81
84
|
"""
|
|
82
|
-
Constrain the data to start after a certain point.
|
|
85
|
+
Constrain the data to start after a certain point - kwargs["plot_from"].
|
|
83
86
|
|
|
84
87
|
Args:
|
|
85
88
|
data: the data to be constrained
|
|
@@ -234,7 +237,7 @@ if __name__ == "__main__":
|
|
|
234
237
|
my_list = [np.nan, np.nan, 1.12345, 2.12345, 3.12345, 4.12345, 5.12345]
|
|
235
238
|
_ = Series(my_list, period_range(start="2023-01", periods=len(my_list), freq="M"))
|
|
236
239
|
_ = _.drop(index=[_.index[3]])
|
|
237
|
-
clean = check_clean_timeseries(_)
|
|
240
|
+
clean = check_clean_timeseries(_, "test")
|
|
238
241
|
print(f"Cleaned data:\n{clean}")
|
|
239
242
|
|
|
240
243
|
# --- test annotate_series()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mgplot
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary: mgplot is a frontend for matplotlib
|
|
3
|
+
Version: 0.1.3
|
|
4
|
+
Summary: mgplot is a time-series/PeriodIndex frontend for matplotlib
|
|
5
5
|
Project-URL: Homepage, https://github.com/bpalmer4/mgplot
|
|
6
6
|
Requires-Python: >=3.11
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
@@ -18,6 +18,7 @@ Requires-Dist: pdoc
|
|
|
18
18
|
Requires-Dist: pylint
|
|
19
19
|
Requires-Dist: ruff
|
|
20
20
|
Requires-Dist: pandas-stubs
|
|
21
|
+
Requires-Dist: numpy-typing
|
|
21
22
|
Requires-Dist: types-tabulate
|
|
22
23
|
Provides-Extra: build
|
|
23
24
|
Requires-Dist: setuptools; extra == "build"
|
|
@@ -64,7 +65,7 @@ The remaining arrguments are passed as keyword arguments:
|
|
|
64
65
|
- summary_plot() -- plots the latest data in a summary-format against
|
|
65
66
|
the range of previous data.
|
|
66
67
|
|
|
67
|
-
Once a plot has been generated and
|
|
68
|
+
Once a plot has been generated and an Axes object is available. The
|
|
68
69
|
plot can be finalised or published, with appropriate titles and
|
|
69
70
|
axis labels using
|
|
70
71
|
- finalise_plot()
|
|
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
|