mgplot 0.2.2__tar.gz → 0.2.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.
Files changed (46) hide show
  1. {mgplot-0.2.2 → mgplot-0.2.3}/CHANGELOG.md +7 -0
  2. {mgplot-0.2.2 → mgplot-0.2.3}/PKG-INFO +1 -1
  3. {mgplot-0.2.2 → mgplot-0.2.3}/docs/mgplot.html +1 -1
  4. {mgplot-0.2.2 → mgplot-0.2.3}/pyproject.toml +1 -1
  5. {mgplot-0.2.2 → mgplot-0.2.3}/src/mgplot/__init__.py +27 -29
  6. {mgplot-0.2.2 → mgplot-0.2.3}/src/mgplot/axis_utils.py +25 -35
  7. {mgplot-0.2.2 → mgplot-0.2.3}/src/mgplot/bar_plot.py +21 -28
  8. {mgplot-0.2.2 → mgplot-0.2.3}/src/mgplot/colors.py +9 -20
  9. {mgplot-0.2.2 → mgplot-0.2.3}/src/mgplot/finalise_plot.py +15 -24
  10. {mgplot-0.2.2 → mgplot-0.2.3}/src/mgplot/finalisers.py +27 -44
  11. {mgplot-0.2.2 → mgplot-0.2.3}/src/mgplot/growth_plot.py +21 -25
  12. {mgplot-0.2.2 → mgplot-0.2.3}/src/mgplot/keyword_checking.py +46 -56
  13. {mgplot-0.2.2 → mgplot-0.2.3}/src/mgplot/line_plot.py +17 -20
  14. {mgplot-0.2.2 → mgplot-0.2.3}/src/mgplot/lint-all.sh +4 -1
  15. {mgplot-0.2.2 → mgplot-0.2.3}/src/mgplot/multi_plot.py +35 -36
  16. {mgplot-0.2.2 → mgplot-0.2.3}/src/mgplot/postcovid_plot.py +16 -21
  17. {mgplot-0.2.2 → mgplot-0.2.3}/src/mgplot/revision_plot.py +8 -12
  18. {mgplot-0.2.2 → mgplot-0.2.3}/src/mgplot/run_plot.py +18 -17
  19. {mgplot-0.2.2 → mgplot-0.2.3}/src/mgplot/seastrend_plot.py +8 -11
  20. {mgplot-0.2.2 → mgplot-0.2.3}/src/mgplot/settings.py +11 -18
  21. {mgplot-0.2.2 → mgplot-0.2.3}/src/mgplot/summary_plot.py +22 -23
  22. {mgplot-0.2.2 → mgplot-0.2.3}/src/mgplot/utilities.py +27 -36
  23. {mgplot-0.2.2 → mgplot-0.2.3}/uv.lock +1 -1
  24. {mgplot-0.2.2 → mgplot-0.2.3}/.gitignore +0 -0
  25. {mgplot-0.2.2 → mgplot-0.2.3}/.pylintrc +0 -0
  26. {mgplot-0.2.2 → mgplot-0.2.3}/LICENSE +0 -0
  27. {mgplot-0.2.2 → mgplot-0.2.3}/README.md +0 -0
  28. {mgplot-0.2.2 → mgplot-0.2.3}/build-docs.sh +0 -0
  29. {mgplot-0.2.2 → mgplot-0.2.3}/build-test.sh +0 -0
  30. {mgplot-0.2.2 → mgplot-0.2.3}/docs/index.html +0 -0
  31. {mgplot-0.2.2 → mgplot-0.2.3}/docs/mgplot/bar_plot.html +0 -0
  32. {mgplot-0.2.2 → mgplot-0.2.3}/docs/mgplot/finalise_plot.html +0 -0
  33. {mgplot-0.2.2 → mgplot-0.2.3}/docs/mgplot/growth_plot.html +0 -0
  34. {mgplot-0.2.2 → mgplot-0.2.3}/docs/mgplot/line_plot.html +0 -0
  35. {mgplot-0.2.2 → mgplot-0.2.3}/docs/mgplot/postcovid_plot.html +0 -0
  36. {mgplot-0.2.2 → mgplot-0.2.3}/docs/mgplot/revision_plot.html +0 -0
  37. {mgplot-0.2.2 → mgplot-0.2.3}/docs/mgplot/run_plot.html +0 -0
  38. {mgplot-0.2.2 → mgplot-0.2.3}/docs/mgplot/seastrend_plot.html +0 -0
  39. {mgplot-0.2.2 → mgplot-0.2.3}/docs/mgplot/summary_plot.html +0 -0
  40. {mgplot-0.2.2 → mgplot-0.2.3}/docs/search.js +0 -0
  41. {mgplot-0.2.2 → mgplot-0.2.3}/src/mgplot/py.typed +0 -0
  42. {mgplot-0.2.2 → mgplot-0.2.3}/test/test.ipynb +0 -0
  43. {mgplot-0.2.2 → mgplot-0.2.3}/test/zz-test-data/ocr_rba.csv +0 -0
  44. {mgplot-0.2.2 → mgplot-0.2.3}/test/zz-test-data/revisions.csv +0 -0
  45. {mgplot-0.2.2 → mgplot-0.2.3}/test/zz-test-data/summary.csv +0 -0
  46. {mgplot-0.2.2 → mgplot-0.2.3}/uv-upgrade.sh +0 -0
@@ -1,3 +1,10 @@
1
+ Version 0.2.3 - released 20-Jun-2025 (Canberra, Australia)
2
+
3
+ * minor changes
4
+ - Implemented more aggressive lining in ruff.
5
+
6
+ ---
7
+
1
8
  Version 0.2.1 - released 19-Jun-2025 (Canberra Australia)
2
9
 
3
10
  * minor changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mgplot
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: mgplot is a time-series/PeriodIndex frontend for matplotlib
5
5
  Project-URL: Repository, https://github.com/bpalmer4/mgplot
6
6
  Project-URL: Homepage, https://github.com/bpalmer4/mgplot
@@ -649,7 +649,7 @@ with timeseries data that is indexed with a PeriodIndex.</p>
649
649
  <section id="__version__">
650
650
  <div class="attr variable">
651
651
  <span class="name">__version__</span> =
652
- <span class="default_value">&#39;0.2.2a1&#39;</span>
652
+ <span class="default_value">&#39;0.2.2&#39;</span>
653
653
 
654
654
 
655
655
  </div>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mgplot"
3
- version = "0.2.2"
3
+ version = "0.2.3"
4
4
  description = "mgplot is a time-series/PeriodIndex frontend for matplotlib"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -1,5 +1,4 @@
1
- """
2
- mgplot
1
+ """mgplot
3
2
  ------
4
3
 
5
4
  Package to provide a frontend to matplotlib for working
@@ -11,49 +10,48 @@ import importlib.metadata
11
10
 
12
11
  # --- local imports
13
12
  # Do not import the utilities, axis_utils nor keyword_checking modules here.
14
- from mgplot.bar_plot import bar_plot, BarKwargs
15
- from mgplot.line_plot import line_plot, LineKwargs
16
- from mgplot.seastrend_plot import seastrend_plot
17
- from mgplot.postcovid_plot import postcovid_plot, PostcovidKwargs
18
- from mgplot.run_plot import run_plot, RunKwargs
19
- from mgplot.revision_plot import revision_plot
20
- from mgplot.growth_plot import (
21
- growth_plot,
22
- GrowthKwargs,
23
- series_growth_plot,
24
- SeriesGrowthKwargs,
25
- calc_growth,
13
+ from mgplot.bar_plot import BarKwargs, bar_plot
14
+ from mgplot.colors import (
15
+ abbreviate_state,
16
+ colorise_list,
17
+ contrast,
18
+ get_color,
19
+ get_party_palette,
20
+ state_abbrs,
21
+ state_names,
26
22
  )
27
- from mgplot.summary_plot import summary_plot, SummaryKwargs
28
- from mgplot.multi_plot import plot_then_finalise, multi_start, multi_column
23
+ from mgplot.finalise_plot import FinaliseKwargs, finalise_plot
29
24
  from mgplot.finalisers import (
30
25
  bar_plot_finalise,
26
+ growth_plot_finalise,
31
27
  line_plot_finalise,
32
28
  postcovid_plot_finalise,
33
- growth_plot_finalise,
34
29
  revision_plot_finalise,
35
30
  run_plot_finalise,
36
31
  seastrend_plot_finalise,
37
32
  series_growth_plot_finalise,
38
33
  summary_plot_finalise,
39
34
  )
40
- from mgplot.finalise_plot import finalise_plot, FinaliseKwargs
41
- from mgplot.colors import (
42
- get_color,
43
- get_party_palette,
44
- colorise_list,
45
- contrast,
46
- abbreviate_state,
47
- state_names,
48
- state_abbrs,
35
+ from mgplot.growth_plot import (
36
+ GrowthKwargs,
37
+ SeriesGrowthKwargs,
38
+ calc_growth,
39
+ growth_plot,
40
+ series_growth_plot,
49
41
  )
42
+ from mgplot.line_plot import LineKwargs, line_plot
43
+ from mgplot.multi_plot import multi_column, multi_start, plot_then_finalise
44
+ from mgplot.postcovid_plot import PostcovidKwargs, postcovid_plot
45
+ from mgplot.revision_plot import revision_plot
46
+ from mgplot.run_plot import RunKwargs, run_plot
47
+ from mgplot.seastrend_plot import seastrend_plot
50
48
  from mgplot.settings import (
49
+ clear_chart_dir,
51
50
  get_setting,
52
- set_setting,
53
51
  set_chart_dir,
54
- clear_chart_dir,
52
+ set_setting,
55
53
  )
56
-
54
+ from mgplot.summary_plot import SummaryKwargs, summary_plot
57
55
 
58
56
  # --- version and author
59
57
  try:
@@ -1,5 +1,4 @@
1
- """
2
- axis_utils.py
1
+ """axis_utils.py
3
2
 
4
3
  This module contains functions to work with categorical
5
4
  axis in Matplotlib, specifically:
@@ -10,21 +9,20 @@ axis in Matplotlib, specifically:
10
9
 
11
10
  import calendar
12
11
  from enum import Enum
13
- from pandas import Period, PeriodIndex, period_range, RangeIndex
14
- from pandas.api.types import is_integer_dtype, is_string_dtype
12
+
15
13
  from matplotlib.pyplot import Axes
14
+ from pandas import Period, PeriodIndex, RangeIndex, period_range
15
+ from pandas.api.types import is_integer_dtype, is_string_dtype
16
16
 
17
17
  from mgplot.settings import DataT
18
18
 
19
19
 
20
20
  def is_categorical(data: DataT) -> bool:
21
- """
22
- Check if the data.index is usefully categorical
21
+ """Check if the data.index is usefully categorical
23
22
  (index needs to be complete, and unique).
24
23
 
25
24
  Note: we plot categoricals using bar plots.
26
25
  """
27
-
28
26
  if data.index.has_duplicates or data.index.hasnans or data.index.empty:
29
27
  return False
30
28
  if is_string_dtype(data.index.dtype):
@@ -44,9 +42,7 @@ def is_categorical(data: DataT) -> bool:
44
42
 
45
43
 
46
44
  def map_periodindex(data: DataT) -> None | tuple[DataT, PeriodIndex]:
47
- """
48
- Map a PeriodIndex to an integer index.
49
- """
45
+ """Map a PeriodIndex to an integer index."""
50
46
  if not is_categorical(data):
51
47
  return None
52
48
  if not isinstance(data.index, PeriodIndex):
@@ -56,7 +52,8 @@ def map_periodindex(data: DataT) -> None | tuple[DataT, PeriodIndex]:
56
52
  start=og_index[0].ordinal,
57
53
  stop=og_index[-1].ordinal + (1 if og_index[0] < og_index[-1] else -1),
58
54
  )
59
- assert len(data.index) == len(og_index), "Mapped PeriodIndex to RangeIndex, but the lengths do not match."
55
+ if len(data.index) != len(og_index):
56
+ raise ValueError("Mapped PeriodIndex to RangeIndex, but the lengths do not match.")
60
57
  return data, og_index
61
58
 
62
59
 
@@ -89,11 +86,11 @@ intervals = {
89
86
 
90
87
 
91
88
  def get_count(p: PeriodIndex, max_ticks: int) -> tuple[int, DateLike, int]:
92
- """
93
- Work out the label frequency and interval for a date-like
89
+ """Work out the label frequency and interval for a date-like
94
90
  PeriodIndex.
95
91
 
96
92
  Parameters
93
+ ----------
97
94
  - p: PeriodIndex - the PeriodIndex
98
95
  - max_ticks - the maximum number of ticks [suggestive]
99
96
 
@@ -101,8 +98,8 @@ def get_count(p: PeriodIndex, max_ticks: int) -> tuple[int, DateLike, int]:
101
98
  - the roughly anticipated number of ticks to highlight: int
102
99
  - the type of ticks to highlight (eg. days/months/quarters/years): str
103
100
  - the tick interval (ie. number of days/months/quarters/years): int
104
- """
105
101
 
102
+ """
106
103
  # --- sanity checks
107
104
  error = (0, DateLike.BAD, 0)
108
105
  if p.empty:
@@ -132,8 +129,7 @@ def day_labeller(labels: dict[Period, str]) -> dict[Period, str]:
132
129
 
133
130
  def add_year(label: str, year: str) -> str:
134
131
  label = label.replace("\n", " ") if len(label) > 2 else f"{label} {month}"
135
- label = f"{label}\n{year}"
136
- return label
132
+ return f"{label}\n{year}"
137
133
 
138
134
  if not labels:
139
135
  return labels
@@ -168,19 +164,17 @@ def day_labeller(labels: dict[Period, str]) -> dict[Period, str]:
168
164
 
169
165
  def month_locator(p: PeriodIndex, interval: int) -> dict[Period, str]:
170
166
  """Select the months to label."""
171
-
172
167
  subset = PeriodIndex([c for c in p if c.day == 1]) if p.freqstr[0] == "D" else p
173
168
 
174
169
  start = 0
175
170
  if interval > 1:
176
171
  mod_months = [(c.month - 1) % interval for c in subset]
177
172
  start = 0 if 0 not in mod_months else mod_months.index(0)
178
- return {k: "" for k in subset[start::interval]}
173
+ return dict.fromkeys(subset[start::interval], "")
179
174
 
180
175
 
181
176
  def month_labeller(labels: dict[Period, str]) -> dict[Period, str]:
182
177
  """Label the selected months."""
183
-
184
178
  if not labels:
185
179
  return labels
186
180
 
@@ -211,17 +205,15 @@ def month_labeller(labels: dict[Period, str]) -> dict[Period, str]:
211
205
 
212
206
  def qtr_locator(p: PeriodIndex, interval: int) -> dict[Period, str]:
213
207
  """Select the quarters to label."""
214
-
215
208
  start = 0
216
209
  if interval > 1:
217
210
  mod_qtrs = [(c.quarter - 1) % interval for c in p]
218
211
  start = 0 if 0 not in mod_qtrs else mod_qtrs.index(0)
219
- return {k: "" for k in p[start::interval]}
212
+ return dict.fromkeys(p[start::interval], "")
220
213
 
221
214
 
222
215
  def qtr_labeller(labels: dict[Period, str]) -> dict[Period, str]:
223
216
  """Label the selected quarters."""
224
-
225
217
  if not labels:
226
218
  return labels
227
219
 
@@ -246,7 +238,6 @@ def qtr_labeller(labels: dict[Period, str]) -> dict[Period, str]:
246
238
 
247
239
  def year_locator(p: PeriodIndex, interval: int) -> dict[Period, str]:
248
240
  """Select the years to label."""
249
-
250
241
  match p.freqstr[0]:
251
242
  case "D":
252
243
  subset = PeriodIndex([c for c in p if c.month == 1 and c.day == 1])
@@ -261,12 +252,11 @@ def year_locator(p: PeriodIndex, interval: int) -> dict[Period, str]:
261
252
  if interval > 1:
262
253
  mod_years = [(c.year) % interval for c in subset]
263
254
  start = 0 if 0 not in mod_years else mod_years.index(0)
264
- return {k: "" for k in subset[start::interval]}
255
+ return dict.fromkeys(subset[start::interval], "")
265
256
 
266
257
 
267
258
  def year_labeller(labels: dict[Period, str]) -> dict[Period, str]:
268
259
  """Label the selected years."""
269
-
270
260
  if not labels:
271
261
  return labels
272
262
 
@@ -277,18 +267,18 @@ def year_labeller(labels: dict[Period, str]) -> dict[Period, str]:
277
267
 
278
268
 
279
269
  def make_labels(p: PeriodIndex, max_ticks: int) -> dict[Period, str]:
280
- """
281
- Provide a dictionary of labels for the date-like PeriodIndex.
270
+ """Provide a dictionary of labels for the date-like PeriodIndex.
282
271
 
283
272
  Parameters
273
+ ----------
284
274
  - p: PeriodIndex - the PeriodIndex
285
275
  - max_ticks - the maximum number of ticks [suggestive]
286
276
 
287
277
  Returns a dictionary:
288
278
  - keys are the Periods to label
289
279
  - values are the labels to apply
290
- """
291
280
 
281
+ """
292
282
  labels: dict[Period, str] = {}
293
283
  max_ticks = max(max_ticks, 4)
294
284
  count, date_like, interval = get_count(p, max_ticks)
@@ -301,7 +291,7 @@ def make_labels(p: PeriodIndex, max_ticks: int) -> dict[Period, str]:
301
291
  match target_freq:
302
292
  case "D":
303
293
  start = 0 if interval == 2 and count % 2 else interval // 2
304
- labels = {k: "" for k in complete[start::interval]}
294
+ labels = dict.fromkeys(complete[start::interval], "")
305
295
  labels = day_labeller(labels)
306
296
 
307
297
  case "M":
@@ -320,18 +310,18 @@ def make_labels(p: PeriodIndex, max_ticks: int) -> dict[Period, str]:
320
310
 
321
311
 
322
312
  def make_ilabels(p: PeriodIndex, max_ticks: int) -> tuple[list[int], list[str]]:
323
- """
324
- From a PeriodIndex, create a list of integer ticks and ticklabels
313
+ """From a PeriodIndex, create a list of integer ticks and ticklabels
325
314
 
326
315
  Parameters
316
+ ----------
327
317
  - p: PeriodIndex - the PeriodIndex
328
318
  - max_ticks - the maximum number of ticks [suggestive]
329
319
 
330
320
  Returns a tuple:
331
321
  - list of integer ticks
332
322
  - list of tick label strings
333
- """
334
323
 
324
+ """
335
325
  labels = make_labels(p, max_ticks)
336
326
  ticks = [x.ordinal for x in sorted(labels.keys())]
337
327
  ticklabels = [labels[x] for x in sorted(labels.keys())]
@@ -340,15 +330,15 @@ def make_ilabels(p: PeriodIndex, max_ticks: int) -> tuple[list[int], list[str]]:
340
330
 
341
331
 
342
332
  def set_labels(axes: Axes, p: PeriodIndex, max_ticks: int = 10) -> None:
343
- """
344
- Set the x-axis labels for a date-like PeriodIndex.
333
+ """Set the x-axis labels for a date-like PeriodIndex.
345
334
 
346
335
  Parameters
336
+ ----------
347
337
  - axes: Axes - the axes to set the labels on
348
338
  - p: PeriodIndex - the PeriodIndex
349
339
  - max_ticks: int - the maximum number of ticks [suggestive]
350
- """
351
340
 
341
+ """
352
342
  ticks, ticklabels = make_ilabels(p, max_ticks)
353
343
  axes.set_xticks(ticks)
354
344
  axes.set_xticklabels(ticklabels, rotation=0, ha="center")
@@ -1,5 +1,4 @@
1
- """
2
- bar_plot.py
1
+ """bar_plot.py
3
2
  This module contains functions to create bar plots using Matplotlib.
4
3
  Note: bar plots in Matplotlib are not the same as bar charts in other
5
4
  libraries. Bar plots are used to represent categorical data with
@@ -8,27 +7,25 @@ cannot be plotted on the same axes.
8
7
  """
9
8
 
10
9
  # --- imports
11
- from typing import Any, Final, Unpack, NotRequired
12
10
  from collections.abc import Sequence
11
+ from typing import Any, Final, NotRequired, Unpack
13
12
 
14
- import numpy as np
15
- from pandas import Series, DataFrame, Period
13
+ import matplotlib.patheffects as pe
16
14
  import matplotlib.pyplot as plt
15
+ import numpy as np
17
16
  from matplotlib.pyplot import Axes
18
- import matplotlib.patheffects as pe
19
-
17
+ from pandas import DataFrame, Period, Series
20
18
 
19
+ from mgplot.axis_utils import is_categorical, map_periodindex, set_labels
20
+ from mgplot.keyword_checking import BaseKwargs, report_kwargs, validate_kwargs
21
21
  from mgplot.settings import DataT, get_setting
22
22
  from mgplot.utilities import (
23
23
  apply_defaults,
24
- get_color_list,
25
- get_axes,
26
24
  constrain_data,
27
25
  default_rounding,
26
+ get_axes,
27
+ get_color_list,
28
28
  )
29
- from mgplot.keyword_checking import validate_kwargs, report_kwargs, BaseKwargs
30
- from mgplot.axis_utils import set_labels, map_periodindex, is_categorical
31
-
32
29
 
33
30
  # --- constants
34
31
  ME: Final[str] = "bar_plot"
@@ -68,7 +65,6 @@ def annotate_bars(
68
65
 
69
66
  Note: "annotate", "fontsize", "fontname", "color", and "rotation" are expected in anno_kwargs.
70
67
  """
71
-
72
68
  # --- only annotate in limited circumstances
73
69
  if "annotate" not in anno_kwargs or not anno_kwargs["annotate"]:
74
70
  return
@@ -89,12 +85,12 @@ def annotate_bars(
89
85
  "color": anno_kwargs.get("color"),
90
86
  "rotation": anno_kwargs.get("rotation"),
91
87
  }
92
- rounding = default_rounding(series=series, provided=anno_kwargs.get("rounding", None))
88
+ rounding = default_rounding(series=series, provided=anno_kwargs.get("rounding"))
93
89
  adjustment = (series.max() - series.min()) * 0.02
94
90
  zero_correction = series.index.min()
95
91
 
96
92
  # --- annotate each bar
97
- for index, value in zip(series.index.astype(int), series): # mypy syntactic sugar
93
+ for index, value in zip(series.index.astype(int), series, strict=False): # mypy syntactic sugar
98
94
  position = base[index - zero_correction] + (adjustment if value >= 0 else -adjustment)
99
95
  if above:
100
96
  position += value
@@ -113,10 +109,7 @@ def annotate_bars(
113
109
 
114
110
 
115
111
  def grouped(axes, df: DataFrame, anno_args, **kwargs) -> None:
116
- """
117
- plot a grouped bar plot
118
- """
119
-
112
+ """Plot a grouped bar plot"""
120
113
  series_count = len(df.columns)
121
114
 
122
115
  for i, col in enumerate(df.columns):
@@ -149,16 +142,15 @@ def grouped(axes, df: DataFrame, anno_args, **kwargs) -> None:
149
142
 
150
143
 
151
144
  def stacked(axes, df: DataFrame, anno_args, **kwargs) -> None:
152
- """
153
- plot a stacked bar plot
154
- """
155
-
145
+ """Plot a stacked bar plot"""
156
146
  series_count = len(df)
157
147
  base_plus: np.ndarray[tuple[int, ...], np.dtype[np.float64]] = np.zeros(
158
- shape=series_count, dtype=np.float64
148
+ shape=series_count,
149
+ dtype=np.float64,
159
150
  )
160
151
  base_minus: np.ndarray[tuple[int, ...], np.dtype[np.float64]] = np.zeros(
161
- shape=series_count, dtype=np.float64
152
+ shape=series_count,
153
+ dtype=np.float64,
162
154
  )
163
155
  for i, col in enumerate(df.columns):
164
156
  series = df[col]
@@ -185,12 +177,12 @@ def stacked(axes, df: DataFrame, anno_args, **kwargs) -> None:
185
177
 
186
178
 
187
179
  def bar_plot(data: DataT, **kwargs: Unpack[BarKwargs]) -> Axes:
188
- """
189
- Create a bar plot from the given data. Each column in the DataFrame
180
+ """Create a bar plot from the given data. Each column in the DataFrame
190
181
  will be stacked on top of each other, with positive values above
191
182
  zero and negative values below zero.
192
183
 
193
184
  Parameters
185
+ ----------
194
186
  - data: Series - The data to plot. Can be a DataFrame or a Series.
195
187
  - **kwargs: BarKwargs - Additional keyword arguments for customization.
196
188
  (see BarKwargs for details)
@@ -198,9 +190,10 @@ def bar_plot(data: DataT, **kwargs: Unpack[BarKwargs]) -> Axes:
198
190
  Note: This function does not assume all data is timeseries with a PeriodIndex,
199
191
 
200
192
  Returns
193
+ -------
201
194
  - axes: Axes - The axes for the plot.
202
- """
203
195
 
196
+ """
204
197
  # --- check the kwargs
205
198
  report_kwargs(caller=ME, **kwargs)
206
199
  validate_kwargs(schema=BarKwargs, caller=ME, **kwargs)
@@ -1,21 +1,18 @@
1
- """
2
- colors.py
1
+ """colors.py
3
2
  This module provides a set of color palettes and functions to generate colors
4
3
  for Australian states and territories and major political parties.
5
4
  It also provides Australian state names and their abbreviations.
6
5
  """
7
6
 
8
7
  # --- Imports
9
- from typing import Iterable
8
+ from collections.abc import Iterable
10
9
 
11
10
 
12
11
  # --- Functions
13
12
  def get_party_palette(party_text: str) -> str:
14
- """
15
- Return a matplotlib color-map name based on party_text.
13
+ """Return a matplotlib color-map name based on party_text.
16
14
  Works for Australian major political parties.
17
15
  """
18
-
19
16
  # Note: light to dark maps work best
20
17
  match party_text.lower():
21
18
  case "alp" | "labor":
@@ -32,11 +29,9 @@ def get_party_palette(party_text: str) -> str:
32
29
 
33
30
 
34
31
  def get_color(s: str) -> str:
35
- """
36
- Return a matplotlib color for a party label
32
+ """Return a matplotlib color for a party label
37
33
  or an Australian state/territory.
38
34
  """
39
-
40
35
  color_map = {
41
36
  # --- Australian states and territories
42
37
  ("wa", "western australia"): "gold",
@@ -91,19 +86,14 @@ def get_color(s: str) -> str:
91
86
 
92
87
 
93
88
  def colorise_list(party_list: Iterable) -> list[str]:
94
- """
95
- Return a list of party/state colors for a party_list.
96
- """
97
-
89
+ """Return a list of party/state colors for a party_list."""
98
90
  return [get_color(x) for x in party_list]
99
91
 
100
92
 
101
93
  def contrast(orig_color: str) -> str:
102
- """
103
- Provide a constrasting color to any party color
94
+ """Provide a constrasting color to any party color
104
95
  generated by get_color() above.
105
96
  """
106
-
107
97
  new_color = "black"
108
98
  match orig_color:
109
99
  case "royalblue":
@@ -154,14 +144,13 @@ for k, v in _state_names.items():
154
144
 
155
145
 
156
146
  def abbreviate_state(state: str) -> str:
157
- """
158
- A function to abbreviate long-form state
147
+ """A function to abbreviate long-form state
159
148
  names.
160
149
 
161
- Arguments
150
+ Arguments:
162
151
  - state: the long-form state name.
163
152
 
164
153
  Return the abbreviation for a state name.
165
- """
166
154
 
155
+ """
167
156
  return _state_names_multi.get(state.lower(), state)
@@ -1,20 +1,19 @@
1
- """
2
- finalise_plot.py:
1
+ """finalise_plot.py:
3
2
  This module provides a function to finalise and save plots to the
4
3
  file system. It is used to publish plots.
5
4
  """
6
5
 
7
6
  # --- imports
8
- from typing import Any, Final, NotRequired, Unpack, Callable
9
- from collections.abc import Sequence
10
7
  import re
8
+ from collections.abc import Callable, Sequence
9
+ from typing import Any, Final, NotRequired, Unpack
10
+
11
11
  import matplotlib as mpl
12
12
  import matplotlib.pyplot as plt
13
13
  from matplotlib.pyplot import Axes, Figure
14
14
 
15
+ from mgplot.keyword_checking import BaseKwargs, report_kwargs, validate_kwargs
15
16
  from mgplot.settings import get_setting
16
- from mgplot.keyword_checking import validate_kwargs, report_kwargs, BaseKwargs
17
-
18
17
 
19
18
  # --- constants
20
19
  ME: Final[str] = "finalise_plot"
@@ -93,9 +92,8 @@ _remove = re.compile(r"[^0-9A-Za-z]") # sensible file names from alphamum title
93
92
  _reduce = re.compile(r"[-]+") # eliminate multiple hyphens
94
93
 
95
94
 
96
- def make_legend(axes: Axes, legend: None | bool | dict[str, Any]) -> None:
95
+ def make_legend(axes: Axes, *, legend: None | bool | dict[str, Any]) -> None:
97
96
  """Create a legend for the plot."""
98
-
99
97
  if legend is None or legend is False:
100
98
  return
101
99
 
@@ -111,9 +109,8 @@ def make_legend(axes: Axes, legend: None | bool | dict[str, Any]) -> None:
111
109
 
112
110
  def apply_value_kwargs(axes: Axes, settings: Sequence[str], **kwargs) -> None:
113
111
  """Set matplotlib elements by name using Axes.set()."""
114
-
115
112
  for setting in settings:
116
- value = kwargs.get(setting, None)
113
+ value = kwargs.get(setting)
117
114
  if value is None and setting not in ("title", "xlabel", "ylabel"):
118
115
  continue
119
116
  function: dict[str, Callable[[], str]] = {
@@ -132,17 +129,15 @@ def apply_value_kwargs(axes: Axes, settings: Sequence[str], **kwargs) -> None:
132
129
 
133
130
 
134
131
  def apply_splat_kwargs(axes: Axes, settings: tuple, **kwargs) -> None:
135
- """
136
- Set matplotlib elements dynamically using setting_name and splat.
132
+ """Set matplotlib elements dynamically using setting_name and splat.
137
133
  This is used for legend, axhspan, axvspan, axhline, and axvline.
138
134
  These can be ignored if not in kwargs, or set to None in kwargs.
139
135
  """
140
-
141
136
  for method_name in settings:
142
137
  if method_name in kwargs:
143
138
  if method_name == "legend":
144
139
  # special case for legend
145
- make_legend(axes, kwargs[method_name])
140
+ make_legend(axes, legend=kwargs[method_name])
146
141
  continue
147
142
 
148
143
  if kwargs[method_name] is None or kwargs[method_name] is False:
@@ -157,14 +152,12 @@ def apply_splat_kwargs(axes: Axes, settings: tuple, **kwargs) -> None:
157
152
  method(**kwargs[method_name])
158
153
  else:
159
154
  print(
160
- f"Warning expected dict argument for {method_name} but got "
161
- + f"{type(kwargs[method_name])}."
155
+ f"Warning expected dict argument for {method_name} but got {type(kwargs[method_name])}.",
162
156
  )
163
157
 
164
158
 
165
159
  def apply_annotations(axes: Axes, **kwargs) -> None:
166
160
  """Set figure size and apply chart annotations."""
167
-
168
161
  fig = axes.figure
169
162
  fig_size = kwargs.get("figsize", get_setting("figsize"))
170
163
  if not isinstance(fig, mpl.figure.SubFigure):
@@ -227,7 +220,6 @@ def apply_kwargs(axes: Axes, **kwargs) -> None:
227
220
 
228
221
  def save_to_file(fig: Figure, **kwargs) -> None:
229
222
  """Save the figure to file."""
230
-
231
223
  saving = not kwargs.get("dont_save", False) # save by default
232
224
  if saving:
233
225
  chart_dir = kwargs.get("chart_dir", get_setting("chart_dir"))
@@ -250,8 +242,7 @@ def save_to_file(fig: Figure, **kwargs) -> None:
250
242
 
251
243
 
252
244
  def finalise_plot(axes: Axes, **kwargs: Unpack[FinaliseKwargs]) -> None:
253
- """
254
- A function to finalise and save plots to the file system. The filename
245
+ """A function to finalise and save plots to the file system. The filename
255
246
  for the saved plot is constructed from the global chart_dir, the plot's title,
256
247
  any specified tag text, and the file_type for the plot.
257
248
 
@@ -259,10 +250,10 @@ def finalise_plot(axes: Axes, **kwargs: Unpack[FinaliseKwargs]) -> None:
259
250
  - axes - matplotlib axes object - required
260
251
  - kwargs: FinaliseKwargs
261
252
 
262
- Returns:
253
+ Returns:
263
254
  - None
264
- """
265
255
 
256
+ """
266
257
  # --- check the kwargs
267
258
  me = "finalise_plot"
268
259
  report_kwargs(caller=me, **kwargs)
@@ -284,7 +275,7 @@ def finalise_plot(axes: Axes, **kwargs: Unpack[FinaliseKwargs]) -> None:
284
275
 
285
276
  # tight layout and save the figure
286
277
  fig = axes.figure
287
- if "preserve_lims" in kwargs and kwargs["preserve_lims"]:
278
+ if kwargs.get("preserve_lims"):
288
279
  # restore the original limits of the axes
289
280
  axes.set_xlim(xlim)
290
281
  axes.set_ylim(ylim)
@@ -298,7 +289,7 @@ def finalise_plot(axes: Axes, **kwargs: Unpack[FinaliseKwargs]) -> None:
298
289
  save_to_file(fig, **kwargs)
299
290
 
300
291
  # show the plot in Jupyter Lab
301
- if "show" in kwargs and kwargs["show"]:
292
+ if kwargs.get("show"):
302
293
  plt.show()
303
294
 
304
295
  # And close