mgplot 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
mgplot-0.1.0/LICENSE ADDED
@@ -0,0 +1,8 @@
1
+ Copyright 2025 Bryan Palmer (Canberra Australia)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8
+
mgplot-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.4
2
+ Name: mgplot
3
+ Version: 0.1.0
4
+ Summary: mgplot is a frontend for matplotlib
5
+ Project-URL: Homepage, https://github.com/bpalmer4/mgplot
6
+ Requires-Python: >=3.11
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: typing
10
+ Requires-Dist: numpy
11
+ Requires-Dist: matplotlib
12
+ Requires-Dist: numpy
13
+ Requires-Dist: pandas
14
+ Requires-Dist: pathlib
15
+ Requires-Dist: tabulate
16
+ Requires-Dist: black
17
+ Requires-Dist: mypy
18
+ Requires-Dist: ruff
19
+ Requires-Dist: pylint
20
+ Requires-Dist: pdoc
21
+ Requires-Dist: twine
22
+ Requires-Dist: pandas-stubs
23
+ Requires-Dist: types-tabulate
24
+ Provides-Extra: build
25
+ Requires-Dist: setuptools; extra == "build"
26
+ Requires-Dist: cython; extra == "build"
27
+ Dynamic: license-file
28
+
29
+ mgplot
30
+ ======
31
+
32
+ Description
33
+ -----------
34
+ mgplot is an open-source python frontend for the matplotlib
35
+ package to:
36
+ 1. produce time-series charts that can be a little difficult or
37
+ tricky to produce directly,
38
+ 2. finalise (or publish) charts with titles, xlabels, ylabels,
39
+ etc., all while
40
+ 3. minimising code duplication, and maintaining a common plot
41
+ style or look-and-feel.
42
+
43
+ Import
44
+ ------
45
+ ```
46
+ import mgplot as mg
47
+ ```
48
+
49
+ For more information
50
+ --------------------
51
+ - See the dicumentation folder
52
+
53
+ ---
mgplot-0.1.0/README.md ADDED
@@ -0,0 +1,25 @@
1
+ mgplot
2
+ ======
3
+
4
+ Description
5
+ -----------
6
+ mgplot is an open-source python frontend for the matplotlib
7
+ package to:
8
+ 1. produce time-series charts that can be a little difficult or
9
+ tricky to produce directly,
10
+ 2. finalise (or publish) charts with titles, xlabels, ylabels,
11
+ etc., all while
12
+ 3. minimising code duplication, and maintaining a common plot
13
+ style or look-and-feel.
14
+
15
+ Import
16
+ ------
17
+ ```
18
+ import mgplot as mg
19
+ ```
20
+
21
+ For more information
22
+ --------------------
23
+ - See the dicumentation folder
24
+
25
+ ---
@@ -0,0 +1,39 @@
1
+ [project]
2
+ name = "mgplot"
3
+ version = "0.1.0"
4
+ description = "mgplot is a frontend for matplotlib"
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+
8
+ dependencies = [
9
+ # - system
10
+ "typing",
11
+
12
+ # - data science
13
+ "numpy",
14
+ "matplotlib",
15
+ "numpy",
16
+ "pandas",
17
+ "pathlib",
18
+ "tabulate",
19
+
20
+ # - code
21
+ "black",
22
+ "mypy",
23
+ "ruff",
24
+ "pylint",
25
+ "pdoc",
26
+ "twine",
27
+
28
+ # - typing
29
+ "pandas-stubs",
30
+ "types-tabulate",
31
+ ]
32
+
33
+ [project.optional-dependencies]
34
+ build = ["setuptools", "cython"]
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/bpalmer4/mgplot"
38
+
39
+
mgplot-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,121 @@
1
+ """
2
+ mgplot
3
+ ------
4
+
5
+ Package to provide a frontend to matplotlib for working
6
+ with timeseries data that is indexed with a PeriodIndex.
7
+ """
8
+
9
+ # --- version and author
10
+ # NOTE: update version number here (below) and in pyproject.toml
11
+ __version__ = "0.1.0"
12
+ __author__ = "Bryan Palmer"
13
+
14
+
15
+ # --- local imports
16
+ # Do not import the utilities, test nor type-checking modules here.
17
+ from mgplot.finalise_plot import finalise_plot
18
+ from mgplot.bar_plot import bar_plot
19
+ from mgplot.line_plot import line_plot
20
+ from mgplot.seastrend_plot import seastrend_plot
21
+ from mgplot.postcovid_plot import postcovid_plot
22
+ from mgplot.revision_plot import revision_plot
23
+ from mgplot.run_plot import run_plot
24
+ from mgplot.summary_plot import summary_plot
25
+ from mgplot.growth_plot import (
26
+ calc_growth,
27
+ raw_growth_plot,
28
+ series_growth_plot,
29
+ )
30
+ from mgplot.multi_plot import (
31
+ multi_start,
32
+ multi_column,
33
+ plot_then_finalise,
34
+ )
35
+ from mgplot.colors import (
36
+ get_color,
37
+ get_party_palette,
38
+ colorise_list,
39
+ contrast,
40
+ abbreviate_state,
41
+ state_names,
42
+ state_abbrs,
43
+ )
44
+ from mgplot.settings import (
45
+ get_setting,
46
+ set_setting,
47
+ set_chart_dir,
48
+ clear_chart_dir,
49
+ )
50
+ from mgplot.finalisers import (
51
+ line_plot_finalise,
52
+ bar_plot_finalise,
53
+ seastrend_plot_finalise,
54
+ postcovid_plot_finalise,
55
+ revision_plot_finalise,
56
+ summary_plot_finalise,
57
+ raw_growth_plot_finalise,
58
+ series_growth_plot_finalise,
59
+ run_plot_finalise,
60
+ )
61
+
62
+
63
+ # --- version and author
64
+ __version__ = "0.0.1"
65
+ __author__ = "Bryan Palmer"
66
+
67
+
68
+ # --- public API
69
+ __all__ = (
70
+ "__version__",
71
+ "__author__",
72
+ # --- settings
73
+ "get_setting",
74
+ "set_setting",
75
+ "set_chart_dir",
76
+ "clear_chart_dir",
77
+ # --- colors
78
+ "get_color",
79
+ "get_party_palette",
80
+ "colorise_list",
81
+ "contrast",
82
+ "abbreviate_state",
83
+ "state_names",
84
+ "state_abbrs",
85
+ # --- finalise_plot
86
+ "finalise_plot",
87
+ # --- line_plot
88
+ "line_plot",
89
+ # --- bar plot
90
+ "bar_plot",
91
+ # --- seastrend_plot
92
+ "seastrend_plot",
93
+ # --- postcovid_plot
94
+ "postcovid_plot",
95
+ # --- revision_plot
96
+ "revision_plot",
97
+ # --- run_plot
98
+ "run_plot",
99
+ # --- summary_plot
100
+ "summary_plot",
101
+ # --- growth_plot
102
+ "calc_growth",
103
+ "raw_growth_plot",
104
+ "series_growth_plot",
105
+ # --- multi_plot
106
+ "multi_start",
107
+ "multi_column",
108
+ "plot_then_finalise",
109
+ # --- finaliser functions
110
+ "line_plot_finalise",
111
+ "bar_plot_finalise",
112
+ "seastrend_plot_finalise",
113
+ "postcovid_plot_finalise",
114
+ "revision_plot_finalise",
115
+ "summary_plot_finalise",
116
+ "raw_growth_plot_finalise",
117
+ "series_growth_plot_finalise",
118
+ "run_plot_finalise",
119
+ # --- The rest are internal use only
120
+ )
121
+ # __pdoc__: dict[str, Any] = {"test": False} # hide submodules from documentation
@@ -0,0 +1,107 @@
1
+ """
2
+ bar_plot.py
3
+ This module contains functions to create bar plots using Matplotlib.
4
+ Note: bar plots in Matplotlib are not the same as bar charts in other
5
+ libraries. Bar plots are used to represent categorical data with
6
+ rectangular bars. As a result, bar plots and line plots typically
7
+ cannot be plotted on the same axes.
8
+ """
9
+
10
+ # --- imports
11
+ from typing import Any
12
+ from collections.abc import Sequence
13
+ from pandas import DataFrame, period_range, PeriodIndex
14
+ import matplotlib.pyplot as plt
15
+ from matplotlib.pyplot import Axes
16
+
17
+ from mgplot.settings import DataT, get_setting
18
+ from mgplot.utilities import apply_defaults, get_color_list, get_axes, constrain_data
19
+ from mgplot.kw_type_checking import validate_kwargs, validate_expected, ExpectedTypeDict
20
+ from mgplot.date_utils import set_labels
21
+
22
+
23
+ # --- constants
24
+ BAR_PLOT_KW_TYPES: ExpectedTypeDict = {
25
+ "color": (str, Sequence, (str,)),
26
+ "width": float,
27
+ "stacked": bool,
28
+ "rotation": (int, float),
29
+ "bar_legend": bool,
30
+ "max_ticks": int,
31
+ "plot_from": (int, PeriodIndex, type(None)),
32
+ }
33
+ validate_expected(BAR_PLOT_KW_TYPES, "bar_plot")
34
+
35
+
36
+ # --- functions
37
+ def bar_plot(
38
+ data: DataT,
39
+ **kwargs,
40
+ ) -> Axes:
41
+ """
42
+ Create a bar plot from the given data. Each column in the DataFrame
43
+ will be stacked on top of each other, with positive values above
44
+ zero and negative values below zero.
45
+
46
+ Parameters
47
+ - data: Series - The data to plot. Can be a DataFrame or a Series.
48
+ - **kwargs: dict Additional keyword arguments for customization.
49
+ - color: list - A list of colors for the each series (column) in the DataFrame.
50
+ - width: float - The width of the bars.
51
+ - stacked: bool - If True, the bars will be stacked.
52
+ - rotation: int - The rotation angle in degrees for the x-axis labels.
53
+ - bar_legend: bool - If True, show the legend. Defaults to True
54
+ if more than one bar being plotted for each category.
55
+ - "max_ticks": int - The maximum number of ticks on the x-axis,
56
+ (this option only applies to PeriodIndex data.).
57
+
58
+ Note: This function does not assume all data is timeseries with a PeriodIndex,
59
+
60
+ Returns
61
+ - axes: Axes - The axes for the plot.
62
+ """
63
+
64
+ # --- validate the kwargs
65
+ validate_kwargs(BAR_PLOT_KW_TYPES, "bar_plot", **kwargs)
66
+ # note data may not be time-series or have a period index.
67
+
68
+ # --- get the data
69
+ df = DataFrame(data) # really we are only plotting DataFrames
70
+ df, kwargs = constrain_data(df, **kwargs)
71
+ item_count = len(df.columns)
72
+
73
+ defaults: dict[str, Any] = {
74
+ "color": get_color_list(item_count),
75
+ "width": get_setting("bar_width"),
76
+ "stacked": False,
77
+ "rotation": 90,
78
+ "bar_legend": (item_count > 1),
79
+ "max_ticks": 10,
80
+ }
81
+ bar_args, remaining_kwargs = apply_defaults(item_count, defaults, kwargs)
82
+
83
+ # --- plot the data
84
+ axes, _rkwargs = get_axes(**remaining_kwargs)
85
+
86
+ df.plot.bar(
87
+ ax=axes,
88
+ color=bar_args["color"],
89
+ stacked=bar_args["stacked"][0],
90
+ width=bar_args["width"][0],
91
+ legend=bar_args["bar_legend"][0],
92
+ )
93
+
94
+ rotate_labels = True
95
+ if isinstance(df.index, PeriodIndex):
96
+ complete = period_range(
97
+ start=df.index.min(), end=df.index.max(), freq=df.index.freqstr
98
+ )
99
+ if complete.equals(df.index):
100
+ # if the index is complete, we can set the labels
101
+ set_labels(axes, df.index, bar_args["max_ticks"][0])
102
+ rotate_labels = False
103
+
104
+ if rotate_labels:
105
+ plt.xticks(rotation=bar_args["rotation"][0])
106
+
107
+ return axes
@@ -0,0 +1,199 @@
1
+ """
2
+ colors.py
3
+ This module provides a set of color palettes and functions to generate colors
4
+ for Australian states and territories and major political parties.
5
+ It also provides Australian state names and their abbreviations.
6
+ """
7
+
8
+ # --- Imports
9
+ from typing import Iterable
10
+
11
+
12
+ # --- Functions
13
+ def get_party_palette(party_text: str) -> str:
14
+ """
15
+ Return a matplotlib color-map name based on party_text.
16
+ Works for Australian major political parties.
17
+ """
18
+
19
+ # Note: light to dark maps work best
20
+ match party_text.lower():
21
+ case "alp" | "labor":
22
+ return "Reds"
23
+ case "l/np" | "coalition":
24
+ return "Blues"
25
+ case "grn" | "green" | "greens":
26
+ return "Greens"
27
+ case "oth" | "other":
28
+ return "YlOrBr"
29
+ case "onp" | "one nation":
30
+ return "YlGnBu"
31
+ return "Purples"
32
+
33
+
34
+ def get_color(s: str) -> str:
35
+ """
36
+ Return a matplotlib color for a party label
37
+ or an Australian state/territory.
38
+ """
39
+
40
+ color_map = {
41
+ # --- Australian states and territories
42
+ ("wa", "western australia"): "gold",
43
+ ("sa", "south australia"): "red",
44
+ ("nt", "northern territory"): "#CC7722", # ochre
45
+ ("nsw", "new south wales"): "deepskyblue",
46
+ ("act", "australian capital territory"): "blue",
47
+ ("vic", "victoria"): "navy",
48
+ ("tas", "tasmania"): "seagreen", # bottle green #006A4E?
49
+ ("qld", "queensland"): "#c32148", # a lighter maroon
50
+ ("australia", "aus"): "grey",
51
+ # --- political parties
52
+ ("dissatisfied",): "darkorange", # must be before satisfied
53
+ ("satisfied",): "mediumblue",
54
+ (
55
+ "lnp",
56
+ "l/np",
57
+ "coalition",
58
+ "dutton",
59
+ "ley",
60
+ ): "royalblue",
61
+ (
62
+ "alp",
63
+ "labor",
64
+ "albanese",
65
+ ): "indianred",
66
+ (
67
+ "grn",
68
+ "green",
69
+ "greens",
70
+ ): "mediumseagreen",
71
+ (
72
+ "other",
73
+ "oth",
74
+ ): "darkorange",
75
+ }
76
+
77
+ for find_me, return_me in color_map.items():
78
+ if any(x == s.lower() for x in find_me):
79
+ return return_me
80
+
81
+ return "darkgrey"
82
+
83
+
84
+ def colorise_list(party_list: Iterable) -> list[str]:
85
+ """
86
+ Return a list of party/state colors for a party_list.
87
+ """
88
+
89
+ return [get_color(x) for x in party_list]
90
+
91
+
92
+ def contrast(orig_color: str) -> str:
93
+ """
94
+ Provide a constrasting color to any party color
95
+ generated by get_color() above.
96
+ """
97
+
98
+ new_color = "black"
99
+ match orig_color:
100
+ case "royalblue":
101
+ new_color = "indianred"
102
+ case "indianred":
103
+ new_color = "mediumblue"
104
+
105
+ case "darkorange":
106
+ new_color = "mediumblue"
107
+ case "mediumblue":
108
+ new_color = "darkorange"
109
+
110
+ case "mediumseagreen":
111
+ new_color = "darkblue"
112
+
113
+ case "darkgrey":
114
+ new_color = "hotpink"
115
+
116
+ return new_color
117
+
118
+
119
+ # --- Australian state names
120
+ _state_names: dict[str, str] = {
121
+ "New South Wales": "NSW",
122
+ "Victoria": "Vic",
123
+ "Queensland": "Qld",
124
+ "South Australia": "SA",
125
+ "Western Australia": "WA",
126
+ "Tasmania": "Tas",
127
+ "Northern Territory": "NT",
128
+ "Australian Capital Territory": "ACT",
129
+ }
130
+
131
+ # a tuple of standard state names
132
+ state_names = tuple(_state_names.keys())
133
+
134
+ # a tuple of standard state abbreviations
135
+ state_abbrs = tuple(_state_names.values())
136
+
137
+ # a map of state name to their abbreviation
138
+ # including upper and lower case mappings
139
+ _state_names_multi: dict[str, str] = {}
140
+ for k, v in _state_names.items():
141
+ # allow for fast different case matches
142
+ _state_names_multi[k.lower()] = v
143
+ _state_names_multi[k.lower()] = v
144
+ _state_names_multi[v.lower()] = v
145
+
146
+
147
+ def abbreviate_state(state: str) -> str:
148
+ """
149
+ A function to abbreviate long-form state
150
+ names.
151
+
152
+ Arguments
153
+ - state: the long-form state name.
154
+
155
+ Return the abbreviation for a state name.
156
+ """
157
+
158
+ return _state_names_multi.get(state.lower(), state)
159
+
160
+
161
+ # --- test code
162
+ if __name__ == "__main__":
163
+ # Test the color functions
164
+ check = ["Western Australia", "L/NP", "ALP", "Greens", "Dissatisfied", "Ley"]
165
+ for party in check:
166
+ print(f"{party} -> {get_color(party)}")
167
+
168
+ # Test the party palette function
169
+ print()
170
+ check = ["ALP", "L/NP", "Greens"]
171
+ for party in check:
172
+ print(f"{party} -> {get_party_palette(party)}")
173
+
174
+ # Test the abbreviate_state function
175
+ print()
176
+ check = [
177
+ "New South Wales",
178
+ "nsw",
179
+ "Victoria",
180
+ "victoria",
181
+ "VICTORIA",
182
+ "VIC",
183
+ "rubbish",
184
+ "Queensland",
185
+ "South Australia",
186
+ "Western Australia",
187
+ "Tasmania",
188
+ "Northern Territory",
189
+ "Australian Capital Territory",
190
+ "ACT",
191
+ ]
192
+
193
+ for state_ in check:
194
+ print(f"{state_} -> {abbreviate_state(state_)}")
195
+
196
+ # Test the get_color function for states
197
+ print()
198
+ for state_ in check:
199
+ print(f"{state_} -> {get_color(state_)}")