mgplot 0.1.3__tar.gz → 0.1.5__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.5/.gitignore +27 -0
- mgplot-0.1.5/CHANGELOG.md +49 -0
- {mgplot-0.1.3/src/mgplot.egg-info → mgplot-0.1.5}/PKG-INFO +6 -17
- mgplot-0.1.5/build-docs.sh +6 -0
- mgplot-0.1.5/build-test.sh +33 -0
- {mgplot-0.1.3 → mgplot-0.1.5}/pyproject.toml +11 -8
- {mgplot-0.1.3 → mgplot-0.1.5}/src/mgplot/__init__.py +9 -6
- {mgplot-0.1.3 → mgplot-0.1.5}/src/mgplot/finalisers.py +1 -10
- {mgplot-0.1.3 → mgplot-0.1.5}/src/mgplot/growth_plot.py +57 -45
- {mgplot-0.1.3 → mgplot-0.1.5}/src/mgplot/kw_type_checking.py +1 -1
- {mgplot-0.1.3 → mgplot-0.1.5}/src/mgplot/line_plot.py +8 -0
- mgplot-0.1.5/src/mgplot/lint-all.sh +19 -0
- {mgplot-0.1.3 → mgplot-0.1.5}/src/mgplot/multi_plot.py +44 -25
- {mgplot-0.1.3 → mgplot-0.1.5}/src/mgplot/utilities.py +31 -15
- mgplot-0.1.5/src/mgplot/zz-test-data/ocr_rba.csv +419 -0
- mgplot-0.1.5/src/mgplot/zz-test-data/revisions.csv +568 -0
- mgplot-0.1.5/src/mgplot/zz-test-data/summary.csv +568 -0
- mgplot-0.1.5/uv.lock +718 -0
- mgplot-0.1.3/PKG-INFO +0 -99
- mgplot-0.1.3/setup.cfg +0 -4
- mgplot-0.1.3/src/mgplot.egg-info/SOURCES.txt +0 -27
- mgplot-0.1.3/src/mgplot.egg-info/dependency_links.txt +0 -1
- mgplot-0.1.3/src/mgplot.egg-info/requires.txt +0 -18
- mgplot-0.1.3/src/mgplot.egg-info/top_level.txt +0 -1
- {mgplot-0.1.3 → mgplot-0.1.5}/LICENSE +0 -0
- {mgplot-0.1.3 → mgplot-0.1.5}/README.md +0 -0
- {mgplot-0.1.3 → mgplot-0.1.5}/src/mgplot/bar_plot.py +0 -0
- {mgplot-0.1.3 → mgplot-0.1.5}/src/mgplot/colors.py +0 -0
- {mgplot-0.1.3 → mgplot-0.1.5}/src/mgplot/date_utils.py +0 -0
- {mgplot-0.1.3 → mgplot-0.1.5}/src/mgplot/finalise_plot.py +0 -0
- {mgplot-0.1.3 → mgplot-0.1.5}/src/mgplot/postcovid_plot.py +0 -0
- {mgplot-0.1.3 → mgplot-0.1.5}/src/mgplot/py.typed +0 -0
- {mgplot-0.1.3 → mgplot-0.1.5}/src/mgplot/revision_plot.py +0 -0
- {mgplot-0.1.3 → mgplot-0.1.5}/src/mgplot/run_plot.py +0 -0
- {mgplot-0.1.3 → mgplot-0.1.5}/src/mgplot/seastrend_plot.py +0 -0
- {mgplot-0.1.3 → mgplot-0.1.5}/src/mgplot/settings.py +0 -0
- {mgplot-0.1.3 → mgplot-0.1.5}/src/mgplot/summary_plot.py +0 -0
- {mgplot-0.1.3 → mgplot-0.1.5}/src/mgplot/test.py +0 -0
mgplot-0.1.5/.gitignore
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# python stuff to be ignored
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# jupyter notebooks stuff to be ignored
|
|
7
|
+
.ipynb_checkpoints/
|
|
8
|
+
|
|
9
|
+
# vscode stuff
|
|
10
|
+
.vscode/
|
|
11
|
+
|
|
12
|
+
# MacOS stuff to be ignored
|
|
13
|
+
.DS_Store
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# build files
|
|
17
|
+
build/
|
|
18
|
+
dist/
|
|
19
|
+
wheels/
|
|
20
|
+
*.egg-info
|
|
21
|
+
|
|
22
|
+
# Test
|
|
23
|
+
src/mgplot/zz-test-charts/**
|
|
24
|
+
src/mgplot/zz-unused-code-snippets/**
|
|
25
|
+
|
|
26
|
+
# Virtual environments
|
|
27
|
+
.venv
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Version 0.1.4 - released 01-Jun-2025 (Canberra, Australia)
|
|
2
|
+
|
|
3
|
+
* minor changes
|
|
4
|
+
- changed the build-system
|
|
5
|
+
- added dynamic version numbering to __init__.py
|
|
6
|
+
- reworked annotations in the growth_plot.py module
|
|
7
|
+
and the utilities module,
|
|
8
|
+
- reworked kwargs validation in plot_then_finalise()
|
|
9
|
+
- typo in kw_type_checking.py
|
|
10
|
+
- tightened up function chaining in the multi-plot modules
|
|
11
|
+
- moved some default arguments from the finalisers module
|
|
12
|
+
to the line_plot module.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
Version 0.1.3 - released 31-May-2025 (Canberra, Australia)
|
|
17
|
+
|
|
18
|
+
* minor changes
|
|
19
|
+
- changed defaults for postcovid_plot() to annotate series
|
|
20
|
+
- changed line_plot() to bail early if nothing to plot
|
|
21
|
+
- added a test to ignore empty axes objects in finalise_plot()
|
|
22
|
+
- reduced the text size for runs in run_plot()
|
|
23
|
+
- added "legend" to line_plot() and the growth plots.
|
|
24
|
+
- if the plot function and the finalise_plot() function have
|
|
25
|
+
kwargs in common, they will be handled by plot() and not
|
|
26
|
+
sent to finalise plot (done by plot_then_finalise())
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
Version 0.1.2 - released 30-May-2025 (Canberra, Australia)
|
|
30
|
+
|
|
31
|
+
* minor changes
|
|
32
|
+
- fixed an incorrect typing-gate in run_plot()
|
|
33
|
+
- removed repeated version code in __init__.py
|
|
34
|
+
- added "numpy-typing" to pyproject.toml
|
|
35
|
+
- added a warning if ylabel set in series_growth_plot_finalise()
|
|
36
|
+
- added legend=True default argument to raw_growth_plot()
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
Version 0.1.1 - released 29-May-2025 (Canberra, Australia)
|
|
40
|
+
|
|
41
|
+
* minor changes
|
|
42
|
+
- added additional documentation
|
|
43
|
+
- disclosed additional variables in the API
|
|
44
|
+
- standardised the naming of the internal ExpectedTypeDicts
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
Version 0.1.0 - released 28-May-2025 (Canberra, Australia)
|
|
48
|
+
|
|
49
|
+
---
|
|
@@ -1,29 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mgplot
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: mgplot is a time-series/PeriodIndex frontend for matplotlib
|
|
5
|
+
Project-URL: Repository, https://github.com/bpalmer4/mgplot
|
|
5
6
|
Project-URL: Homepage, https://github.com/bpalmer4/mgplot
|
|
6
|
-
Requires-Python: >=3.11
|
|
7
|
-
Description-Content-Type: text/markdown
|
|
8
7
|
License-File: LICENSE
|
|
9
|
-
Requires-
|
|
10
|
-
Requires-Dist: pathlib
|
|
8
|
+
Requires-Python: >=3.12
|
|
11
9
|
Requires-Dist: matplotlib
|
|
12
10
|
Requires-Dist: numpy
|
|
13
11
|
Requires-Dist: pandas
|
|
12
|
+
Requires-Dist: pathlib
|
|
14
13
|
Requires-Dist: tabulate
|
|
15
|
-
Requires-Dist:
|
|
16
|
-
|
|
17
|
-
Requires-Dist: pdoc
|
|
18
|
-
Requires-Dist: pylint
|
|
19
|
-
Requires-Dist: ruff
|
|
20
|
-
Requires-Dist: pandas-stubs
|
|
21
|
-
Requires-Dist: numpy-typing
|
|
22
|
-
Requires-Dist: types-tabulate
|
|
23
|
-
Provides-Extra: build
|
|
24
|
-
Requires-Dist: setuptools; extra == "build"
|
|
25
|
-
Requires-Dist: cython; extra == "build"
|
|
26
|
-
Dynamic: license-file
|
|
14
|
+
Requires-Dist: typing
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
27
16
|
|
|
28
17
|
mgplot
|
|
29
18
|
======
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/bin/zsh
|
|
2
|
+
# make sure the version number is correct:
|
|
3
|
+
# ~/mgplot/pyproject.toml
|
|
4
|
+
|
|
5
|
+
# --- cd mgplot home and get ready
|
|
6
|
+
cd ~/mgplot
|
|
7
|
+
uv pip uninstall mgplot
|
|
8
|
+
deactivate
|
|
9
|
+
|
|
10
|
+
# --- clean out the dist folder
|
|
11
|
+
if [ ! -d ./dist ]; then
|
|
12
|
+
mkdir dist
|
|
13
|
+
fi
|
|
14
|
+
if [ -n "$(ls -A ./dist 2>/dev/null)" ]; then
|
|
15
|
+
rm ./dist/*
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# --- sync and build
|
|
19
|
+
uv lock --upgrade # --upgrade to get the latest dependencies
|
|
20
|
+
uv sync --no-dev # --no-dev to avoid installing dev dependencies
|
|
21
|
+
uv build
|
|
22
|
+
|
|
23
|
+
# --- install new mgplot locally
|
|
24
|
+
uv sync # install with the development dependencies
|
|
25
|
+
uv pip install dist/mgplot*gz # add in the new package
|
|
26
|
+
|
|
27
|
+
# --- build documentation
|
|
28
|
+
~/mgplot/build-docs.sh
|
|
29
|
+
|
|
30
|
+
# --- if everything is good publish and git
|
|
31
|
+
echo "\nAnd if everything is okay ..."
|
|
32
|
+
echo "uv publish --token MY_TOKEN_HERE"
|
|
33
|
+
echo "And don't forget to upload to github"
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mgplot"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.5"
|
|
4
4
|
description = "mgplot is a time-series/PeriodIndex frontend for matplotlib"
|
|
5
5
|
readme = "README.md"
|
|
6
|
-
requires-python = ">=3.
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
7
|
|
|
8
8
|
dependencies = [
|
|
9
9
|
# - system
|
|
@@ -15,24 +15,27 @@ dependencies = [
|
|
|
15
15
|
"numpy",
|
|
16
16
|
"pandas",
|
|
17
17
|
"tabulate",
|
|
18
|
+
]
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
[dependency-groups]
|
|
21
|
+
dev = [
|
|
22
|
+
# - tools
|
|
20
23
|
"black",
|
|
21
24
|
"mypy",
|
|
22
25
|
"pdoc",
|
|
23
26
|
"pylint",
|
|
24
27
|
"ruff",
|
|
25
|
-
|
|
28
|
+
|
|
26
29
|
# - typing
|
|
27
30
|
"pandas-stubs",
|
|
28
31
|
"numpy-typing",
|
|
29
32
|
"types-tabulate",
|
|
30
33
|
]
|
|
31
34
|
|
|
32
|
-
[project.optional-dependencies]
|
|
33
|
-
build = ["setuptools", "cython"]
|
|
34
|
-
|
|
35
35
|
[project.urls]
|
|
36
|
+
Repository = "https://github.com/bpalmer4/mgplot"
|
|
36
37
|
Homepage = "https://github.com/bpalmer4/mgplot"
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
[build-system]
|
|
40
|
+
requires = ["hatchling"]
|
|
41
|
+
build-backend = "hatchling.build"
|
|
@@ -7,14 +7,10 @@ with timeseries data that is indexed with a PeriodIndex.
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
# --- version and author
|
|
10
|
-
|
|
11
|
-
__version__ = "0.1.3"
|
|
12
|
-
__author__ = "Bryan Palmer"
|
|
13
|
-
|
|
10
|
+
import importlib.metadata
|
|
14
11
|
|
|
15
12
|
# --- local imports
|
|
16
13
|
# Do not import the utilities, test nor type-checking modules here.
|
|
17
|
-
from mgplot.kw_type_checking import ExpectedTypeDict # typing for the kwarg types
|
|
18
14
|
from mgplot.finalise_plot import finalise_plot, FINALISE_KW_TYPES
|
|
19
15
|
from mgplot.bar_plot import bar_plot, BAR_KW_TYPES
|
|
20
16
|
from mgplot.line_plot import line_plot, LINE_KW_TYPES
|
|
@@ -63,6 +59,14 @@ from mgplot.finalisers import (
|
|
|
63
59
|
)
|
|
64
60
|
|
|
65
61
|
|
|
62
|
+
# --- version and author
|
|
63
|
+
try:
|
|
64
|
+
__version__ = importlib.metadata.version(__name__)
|
|
65
|
+
except importlib.metadata.PackageNotFoundError:
|
|
66
|
+
__version__ = "0.0.0" # Fallback for development mode
|
|
67
|
+
__author__ = "Bryan Palmer"
|
|
68
|
+
|
|
69
|
+
|
|
66
70
|
# --- public API
|
|
67
71
|
__all__ = (
|
|
68
72
|
"__version__",
|
|
@@ -115,7 +119,6 @@ __all__ = (
|
|
|
115
119
|
"series_growth_plot_finalise",
|
|
116
120
|
"run_plot_finalise",
|
|
117
121
|
# --- typing information
|
|
118
|
-
"ExpectedTypeDict",
|
|
119
122
|
"FINALISE_KW_TYPES",
|
|
120
123
|
"BAR_KW_TYPES",
|
|
121
124
|
"LINE_KW_TYPES",
|
|
@@ -46,15 +46,6 @@ def line_plot_finalise(
|
|
|
46
46
|
wraps calls to line_plot() and finalise_plot().
|
|
47
47
|
"""
|
|
48
48
|
|
|
49
|
-
if isinstance(data, DataFrame):
|
|
50
|
-
if len(data.columns) > 1:
|
|
51
|
-
# default to displaying a legend
|
|
52
|
-
kwargs["legend"] = kwargs.get("legend", True)
|
|
53
|
-
if len(data.columns) > 4:
|
|
54
|
-
# default to using a style for the lines
|
|
55
|
-
kwargs["style"] = kwargs.get(
|
|
56
|
-
"style", ["solid", "dashed", "dashdot", "dotted"]
|
|
57
|
-
)
|
|
58
49
|
plot_then_finalise(
|
|
59
50
|
data,
|
|
60
51
|
function=line_plot,
|
|
@@ -249,6 +240,7 @@ if __name__ == "__main__":
|
|
|
249
240
|
data=test_frame,
|
|
250
241
|
title="Test Line Plot",
|
|
251
242
|
ylabel="Value",
|
|
243
|
+
annotate=True,
|
|
252
244
|
xlabel=None,
|
|
253
245
|
)
|
|
254
246
|
|
|
@@ -306,7 +298,6 @@ if __name__ == "__main__":
|
|
|
306
298
|
function=series_growth_plot_finalise,
|
|
307
299
|
starts=[0, -19],
|
|
308
300
|
title="Test Multi Start Series Growth Plot: ",
|
|
309
|
-
ylabel="Per cent Growth",
|
|
310
301
|
xlabel=None,
|
|
311
302
|
)
|
|
312
303
|
|
|
@@ -18,7 +18,7 @@ from mgplot.finalise_plot import make_legend
|
|
|
18
18
|
from mgplot.test import prepare_for_test
|
|
19
19
|
from mgplot.settings import get_setting, DataT
|
|
20
20
|
from mgplot.date_utils import set_labels
|
|
21
|
-
from mgplot.utilities import annotate_series, check_clean_timeseries
|
|
21
|
+
from mgplot.utilities import annotate_series, check_clean_timeseries, default_rounding
|
|
22
22
|
from mgplot.kw_type_checking import (
|
|
23
23
|
validate_kwargs,
|
|
24
24
|
report_kwargs,
|
|
@@ -31,15 +31,22 @@ from mgplot.kw_type_checking import (
|
|
|
31
31
|
ANNUAL = "annual"
|
|
32
32
|
PERIODIC = "periodic"
|
|
33
33
|
|
|
34
|
+
ANNOTATE_BAR = "annotate_bar"
|
|
35
|
+
ANNOTATE_LINE = "annotate_line"
|
|
36
|
+
BAR_ROUNDING = "bar_rounding"
|
|
37
|
+
ROUNDING = "rounding"
|
|
38
|
+
|
|
39
|
+
|
|
34
40
|
RAW_GROWTH_KW_TYPES: Final[ExpectedTypeDict] = {
|
|
35
41
|
"line_width": (float, int),
|
|
36
42
|
"line_color": str,
|
|
37
43
|
"line_style": str,
|
|
38
|
-
|
|
44
|
+
ANNOTATE_LINE: (type(None), bool, int, str), # None, True or fontsize
|
|
45
|
+
ROUNDING: (type(None), bool, int), # None, True or rounding
|
|
39
46
|
"bar_width": float,
|
|
40
47
|
"bar_color": str,
|
|
41
|
-
|
|
42
|
-
|
|
48
|
+
ANNOTATE_BAR: (type(None), bool, int, str), # None, True or fontsize
|
|
49
|
+
BAR_ROUNDING: (type(None), bool, int), # None, True or rounding
|
|
43
50
|
"plot_from": (type(None), Period, int),
|
|
44
51
|
"max_ticks": int,
|
|
45
52
|
"legend": (type(None), bool, dict, (str, object)),
|
|
@@ -108,44 +115,45 @@ def _annotations(
|
|
|
108
115
|
) -> None:
|
|
109
116
|
"""Apply annotations the annual and periodic growth series."""
|
|
110
117
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
118
|
+
# --- annotate the end of the line
|
|
119
|
+
annotate_line = kwargs.get(ANNOTATE_LINE, True) #
|
|
120
|
+
if annotate_line is not None and annotate_line is not False:
|
|
121
|
+
fontsize = kwargs.pop("fontsize", annotate_line)
|
|
122
|
+
annotate_series(annual, axes, fontsize=fontsize, **kwargs)
|
|
123
|
+
|
|
124
|
+
# --- annotate each bar
|
|
125
|
+
annotate_bar = kwargs.get(ANNOTATE_BAR, True) # really fontsize
|
|
126
|
+
if annotate_bar is None or annotate_bar is False:
|
|
127
|
+
return
|
|
128
|
+
if annotate_bar is True:
|
|
129
|
+
annotate_bar = "small"
|
|
130
|
+
max_annotations = 25
|
|
131
|
+
if len(periodic) > max_annotations:
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
rounding = kwargs.get(BAR_ROUNDING, True)
|
|
135
|
+
if rounding is None or isinstance(rounding, bool):
|
|
136
|
+
value = periodic.abs().max()
|
|
137
|
+
rounding = default_rounding(value)
|
|
138
|
+
annotate_style = {
|
|
139
|
+
"fontsize": annotate_bar,
|
|
140
|
+
"fontname": "Helvetica",
|
|
141
|
+
}
|
|
142
|
+
adjustment = (periodic.max() - periodic.min()) * 0.01
|
|
143
|
+
for i, value in enumerate(periodic):
|
|
144
|
+
va = "bottom" if value >= 0 else "top"
|
|
145
|
+
text = axes.text(
|
|
146
|
+
periodic.index[i],
|
|
147
|
+
adjustment if value >= 0 else -adjustment,
|
|
148
|
+
f"{value:.{rounding}f}",
|
|
149
|
+
ha="center",
|
|
150
|
+
va=va,
|
|
151
|
+
**annotate_style,
|
|
152
|
+
color="white",
|
|
153
|
+
)
|
|
154
|
+
text.set_path_effects(
|
|
155
|
+
[pe.withStroke(linewidth=2, foreground=kwargs.get("bar_color", "#dd0000"))]
|
|
119
156
|
)
|
|
120
|
-
|
|
121
|
-
annotate_bar = kwargs.get("annotate_bar", "small")
|
|
122
|
-
max_annotations = 30
|
|
123
|
-
if annotate_bar is not None and len(periodic) < max_annotations:
|
|
124
|
-
annotation_rounding = kwargs.get("annotation_rounding", 1)
|
|
125
|
-
annotate_style = {
|
|
126
|
-
"fontsize": annotate_bar,
|
|
127
|
-
"fontname": "Helvetica",
|
|
128
|
-
}
|
|
129
|
-
adjustment = (periodic.max() - periodic.min()) * 0.005
|
|
130
|
-
for i, value in enumerate(periodic):
|
|
131
|
-
va = "bottom" if value >= 0 else "top"
|
|
132
|
-
text = axes.text(
|
|
133
|
-
periodic.index[i],
|
|
134
|
-
adjustment if value >= 0 else -adjustment,
|
|
135
|
-
f"{value:.{annotation_rounding}f}",
|
|
136
|
-
ha="center",
|
|
137
|
-
va=va,
|
|
138
|
-
**annotate_style,
|
|
139
|
-
fontdict=None,
|
|
140
|
-
color="white",
|
|
141
|
-
)
|
|
142
|
-
text.set_path_effects(
|
|
143
|
-
[
|
|
144
|
-
pe.withStroke(
|
|
145
|
-
linewidth=2, foreground=kwargs.get("bar_color", "indianred")
|
|
146
|
-
)
|
|
147
|
-
]
|
|
148
|
-
)
|
|
149
157
|
|
|
150
158
|
|
|
151
159
|
def raw_growth_plot(
|
|
@@ -162,15 +170,17 @@ def raw_growth_plot(
|
|
|
162
170
|
- line_width: The width of the line (default is 2).
|
|
163
171
|
- line_color: The color of the line (default is "darkblue").
|
|
164
172
|
- line_style: The style of the line (default is "-").
|
|
165
|
-
- annotate_line: None | int | str - fontsize to annotate
|
|
166
|
-
(default is "small", which means the line is annotated with
|
|
173
|
+
- annotate_line: None | bool | int | str - fontsize to annotate
|
|
174
|
+
the line (default is "small", which means the line is annotated with
|
|
167
175
|
small text).
|
|
176
|
+
- rounding: None | bool | int - the number of decimal places to round
|
|
177
|
+
the line (default is 0).
|
|
168
178
|
- bar_width: The width of the bars (default is 0.8).
|
|
169
179
|
- bar_color: The color of the bars (default is "indianred").
|
|
170
180
|
- annotate_bar: None | int | str - fontsize to annotate the bars
|
|
171
181
|
(default is "small", which means the bars are annotated with
|
|
172
182
|
small text).
|
|
173
|
-
-
|
|
183
|
+
- bar_rounding: The number of decimal places to round the
|
|
174
184
|
annotations to (default is 1).
|
|
175
185
|
- plot_from: None | Period | int -- if:
|
|
176
186
|
- None: the entire series is plotted
|
|
@@ -217,8 +227,10 @@ def raw_growth_plot(
|
|
|
217
227
|
periodic.name = {"M": "Monthly", "Q": "Quarterly", "D": "Daily"}[
|
|
218
228
|
PeriodIndex(save_index).freqstr[:1]
|
|
219
229
|
] + " Growth"
|
|
230
|
+
color = kwargs.get("bar_color", "#dd0000")
|
|
231
|
+
kwargs["bar_color"] = color # for annotations
|
|
220
232
|
axes = periodic.plot.bar(
|
|
221
|
-
color=
|
|
233
|
+
color=color,
|
|
222
234
|
width=kwargs.get("bar_width}", 0.8),
|
|
223
235
|
)
|
|
224
236
|
thin_threshold = 180
|
|
@@ -379,7 +379,7 @@ def validate_kwargs(
|
|
|
379
379
|
continue
|
|
380
380
|
if key not in expected:
|
|
381
381
|
problems += (
|
|
382
|
-
f"{key}:
|
|
382
|
+
f"{key}: keyword not recognised. Has {value=} " + f"in {called_from}.\n"
|
|
383
383
|
)
|
|
384
384
|
continue
|
|
385
385
|
if not _type_check_kwargs(value, expected[key]):
|
|
@@ -130,6 +130,14 @@ def line_plot(data: DataT, **kwargs) -> plt.Axes:
|
|
|
130
130
|
df = DataFrame(data) # really we are only plotting DataFrames
|
|
131
131
|
df, kwargs = constrain_data(df, **kwargs)
|
|
132
132
|
|
|
133
|
+
# --- some special defaults
|
|
134
|
+
if len(df.columns) > 1:
|
|
135
|
+
# default to displaying a legend
|
|
136
|
+
kwargs["legend"] = kwargs.get("legend", True)
|
|
137
|
+
if len(df.columns) > 4:
|
|
138
|
+
# default to using a style for the lines
|
|
139
|
+
kwargs["style"] = kwargs.get("style", ["solid", "dashed", "dashdot", "dotted"])
|
|
140
|
+
|
|
133
141
|
# --- Let's plot
|
|
134
142
|
axes, kwargs = get_axes(**kwargs) # get the axes to plot on
|
|
135
143
|
if df.empty or df.isna().all().all():
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
echo "black"
|
|
2
|
+
black *.py
|
|
3
|
+
|
|
4
|
+
echo "pylint"
|
|
5
|
+
pylint *.py
|
|
6
|
+
|
|
7
|
+
echo "mypy"
|
|
8
|
+
mypy *.py
|
|
9
|
+
|
|
10
|
+
echo "ruff"
|
|
11
|
+
ruff check *.py
|
|
12
|
+
|
|
13
|
+
# report any lint overrides
|
|
14
|
+
echo " "
|
|
15
|
+
echo "Check linting overrides ..."
|
|
16
|
+
grep "# type" *.py
|
|
17
|
+
grep "# pylint" *.py
|
|
18
|
+
grep --regexp="from typing import .*cast" *.py
|
|
19
|
+
grep "cast(" *.py
|
|
@@ -36,7 +36,7 @@ Note: rather than pass the kwargs dict directly, we will re-pack-it
|
|
|
36
36
|
"""
|
|
37
37
|
|
|
38
38
|
# --- imports
|
|
39
|
-
from typing import
|
|
39
|
+
from typing import Callable, Final
|
|
40
40
|
from collections.abc import Iterable
|
|
41
41
|
from pandas import Period, DataFrame, Series, period_range
|
|
42
42
|
from numpy import random
|
|
@@ -85,8 +85,7 @@ EXPECTED_CALLABLES: Final[dict[Callable, ExpectedTypeDict]] = {
|
|
|
85
85
|
# --- private functions
|
|
86
86
|
def first_unchain(
|
|
87
87
|
function: Callable | list[Callable],
|
|
88
|
-
|
|
89
|
-
) -> tuple[Callable, dict[str, Any]]:
|
|
88
|
+
) -> tuple[Callable, list[Callable]]:
|
|
90
89
|
"""
|
|
91
90
|
Extract the first Callable from function (which may be
|
|
92
91
|
a stand alone Callable or a nonr-empty list of Callables).
|
|
@@ -94,12 +93,10 @@ def first_unchain(
|
|
|
94
93
|
This allows for chaining multiple functions together.
|
|
95
94
|
|
|
96
95
|
Parameters
|
|
97
|
-
-
|
|
96
|
+
- function - a Callable or a non-empty list of Callables
|
|
98
97
|
|
|
99
|
-
Returns a tuple containing the first function and the
|
|
100
|
-
|
|
101
|
-
from the the list, and the remaining functions will be stored in a
|
|
102
|
-
list under the key "function" in kwargs.
|
|
98
|
+
Returns a tuple containing the first function and a list of the remaining
|
|
99
|
+
functions (which may be empty if there are no remaining functions).
|
|
103
100
|
|
|
104
101
|
Raises ValueError if function is an empty list.
|
|
105
102
|
|
|
@@ -117,10 +114,7 @@ def first_unchain(
|
|
|
117
114
|
else:
|
|
118
115
|
raise ValueError(error_msg)
|
|
119
116
|
|
|
120
|
-
|
|
121
|
-
kwargs["function"] = rest
|
|
122
|
-
|
|
123
|
-
return first, kwargs
|
|
117
|
+
return first, rest
|
|
124
118
|
|
|
125
119
|
|
|
126
120
|
# --- public functions
|
|
@@ -151,8 +145,17 @@ def plot_then_finalise(
|
|
|
151
145
|
# data is not checked here, assume it is checked by the called
|
|
152
146
|
# plot function.
|
|
153
147
|
|
|
154
|
-
|
|
155
|
-
|
|
148
|
+
first, kwargs["function"] = first_unchain(function)
|
|
149
|
+
if not kwargs["function"]:
|
|
150
|
+
del kwargs["function"] # remove the function key if it is empty
|
|
151
|
+
|
|
152
|
+
bad_next = (multi_start, multi_column)
|
|
153
|
+
if first in bad_next:
|
|
154
|
+
# these functions should not be called by plot_then_finalise()
|
|
155
|
+
raise ValueError(
|
|
156
|
+
f"[{', '.join(k.__name__ for k in bad_next)}] should not be called by {me}. "
|
|
157
|
+
"Call them before calling {me}. "
|
|
158
|
+
)
|
|
156
159
|
|
|
157
160
|
if first in EXPECTED_CALLABLES:
|
|
158
161
|
expected = EXPECTED_CALLABLES[first]
|
|
@@ -161,10 +164,12 @@ def plot_then_finalise(
|
|
|
161
164
|
# this is an unexpected Callable, so we will give it a try
|
|
162
165
|
print(f"Unknown proposed function: {first}; nonetheless, will give it a try.")
|
|
163
166
|
expected = {}
|
|
164
|
-
plot_kwargs =
|
|
165
|
-
validate_kwargs(expected | FINALISE_KW_TYPES, me, **plot_kwargs)
|
|
167
|
+
plot_kwargs = kwargs.copy()
|
|
166
168
|
|
|
167
|
-
# ---
|
|
169
|
+
# --- validate the original kwargs (could not do before now)
|
|
170
|
+
validate_kwargs(FINALISE_KW_TYPES | expected, me, **kwargs)
|
|
171
|
+
|
|
172
|
+
# --- call the first function with the data and selected plot kwargs
|
|
168
173
|
axes = first(data, **plot_kwargs)
|
|
169
174
|
|
|
170
175
|
# --- remove potentially overlapping kwargs
|
|
@@ -215,7 +220,9 @@ def multi_start(
|
|
|
215
220
|
|
|
216
221
|
# --- check the function argument
|
|
217
222
|
original_tag: Final[str] = kwargs.get("tag", "")
|
|
218
|
-
first, kwargs = first_unchain(function
|
|
223
|
+
first, kwargs["function"] = first_unchain(function)
|
|
224
|
+
if not kwargs["function"]:
|
|
225
|
+
del kwargs["function"] # remove the function key if it is empty
|
|
219
226
|
|
|
220
227
|
# --- iterate over the starts
|
|
221
228
|
for i, start in enumerate(starts):
|
|
@@ -255,7 +262,9 @@ def multi_column(
|
|
|
255
262
|
# --- check the function argument
|
|
256
263
|
title_stem = kwargs.get("title", "")
|
|
257
264
|
tag: Final[str] = kwargs.get("tag", "")
|
|
258
|
-
first, kwargs = first_unchain(function
|
|
265
|
+
first, kwargs["function"] = first_unchain(function)
|
|
266
|
+
if not kwargs["function"]:
|
|
267
|
+
del kwargs["function"] # remove the function key if it is empty
|
|
259
268
|
|
|
260
269
|
# --- iterate over the columns
|
|
261
270
|
for i, col in enumerate(data.columns):
|
|
@@ -304,7 +313,8 @@ if __name__ == "__main__":
|
|
|
304
313
|
[multi_start, plot_then_finalise, line_plot],
|
|
305
314
|
title="Test Multi Column / Multi start: ",
|
|
306
315
|
starts=[None, 180],
|
|
307
|
-
|
|
316
|
+
garbage=True, # this is an unexpected argument - should trigger a warning
|
|
317
|
+
annotate=True, # this is an expected argument
|
|
308
318
|
)
|
|
309
319
|
|
|
310
320
|
# bar plot
|
|
@@ -327,10 +337,6 @@ if __name__ == "__main__":
|
|
|
327
337
|
ylabel="Y-axis Label",
|
|
328
338
|
)
|
|
329
339
|
|
|
330
|
-
|
|
331
|
-
# --- test
|
|
332
|
-
if __name__ == "__main__":
|
|
333
|
-
|
|
334
340
|
# --- check that this fails
|
|
335
341
|
try:
|
|
336
342
|
multi_start(
|
|
@@ -341,7 +347,7 @@ if __name__ == "__main__":
|
|
|
341
347
|
except (ValueError, TypeError) as e:
|
|
342
348
|
print(f"Expected error: {e}")
|
|
343
349
|
|
|
344
|
-
# --- check that
|
|
350
|
+
# --- check that this fails
|
|
345
351
|
try:
|
|
346
352
|
multi_column(
|
|
347
353
|
data=Series([1, 2, 3]), # type: ignore # Series is not a DataFrame
|
|
@@ -358,3 +364,16 @@ if __name__ == "__main__":
|
|
|
358
364
|
)
|
|
359
365
|
except (ValueError, TypeError) as e:
|
|
360
366
|
print(f"Expected error: {e}")
|
|
367
|
+
|
|
368
|
+
# --- check this fails
|
|
369
|
+
try:
|
|
370
|
+
plot_then_finalise(
|
|
371
|
+
data=Series([1, 2, 3]),
|
|
372
|
+
function=[multi_start, bar_plot],
|
|
373
|
+
starts=[0, 1],
|
|
374
|
+
title="Test Multi Start with Bar Plot",
|
|
375
|
+
xlabel="X-axis Label",
|
|
376
|
+
ylabel="Y-axis Label",
|
|
377
|
+
)
|
|
378
|
+
except (ValueError, TypeError) as e:
|
|
379
|
+
print(f"Expected error: {e}")
|