tesorotools-python 0.0.34__tar.gz → 0.0.36__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.
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/PKG-INFO +1 -1
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/pyproject.toml +1 -1
- tesorotools_python-0.0.36/src/tesorotools/__init__.py +115 -0
- tesorotools_python-0.0.36/src/tesorotools/_registry.py +117 -0
- tesorotools_python-0.0.36/src/tesorotools/artists/__init__.py +25 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/artists/line_plot.py +31 -38
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/artists/stacked.py +25 -27
- tesorotools_python-0.0.36/src/tesorotools/artists/type_curve.py +229 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/assets/plots.yaml +0 -2
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/convert.py +17 -3
- tesorotools_python-0.0.36/src/tesorotools/driver.py +138 -0
- tesorotools_python-0.0.36/src/tesorotools/providers/__init__.py +32 -0
- tesorotools_python-0.0.36/src/tesorotools/render/__init__.py +22 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/render/content/table.py +42 -7
- tesorotools_python-0.0.36/src/tesorotools/utils/config.py +38 -0
- tesorotools_python-0.0.34/src/tesorotools/artists/__init__.py +0 -5
- tesorotools_python-0.0.34/src/tesorotools/artists/type_curve.py +0 -199
- tesorotools_python-0.0.34/src/tesorotools/providers/__init__.py +0 -0
- tesorotools_python-0.0.34/src/tesorotools/render/__init__.py +0 -0
- tesorotools_python-0.0.34/src/tesorotools/render/content/__init__.py +0 -0
- tesorotools_python-0.0.34/src/tesorotools/utils/config.py +0 -98
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/.gitignore +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/artists/barh.md +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/artists/barh_plot.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/artists/table.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/assets/README.md +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/assets/fonts/CabinetGrotesk-Black.otf +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/assets/fonts/CabinetGrotesk-Bold.otf +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/assets/fonts/CabinetGrotesk-Extrabold.otf +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/assets/fonts/CabinetGrotesk-Extralight.otf +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/assets/fonts/CabinetGrotesk-Light.otf +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/assets/fonts/CabinetGrotesk-Medium.otf +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/assets/fonts/CabinetGrotesk-Regular.otf +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/assets/fonts/CabinetGrotesk-Thin.otf +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/assets/fonts/README.md +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/assets/tesoro.mplstyle +0 -0
- {tesorotools_python-0.0.34/src/tesorotools → tesorotools_python-0.0.36/src/tesorotools/data_sources}/__init__.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/data_sources/debug.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/database/__init__.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/database/local.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/database/push.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/database/shared.py +0 -0
- {tesorotools_python-0.0.34/src/tesorotools/data_sources → tesorotools_python-0.0.36/src/tesorotools/dependencies}/__init__.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/dependencies/node.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/dependencies/resolution.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/main.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/manifest.py +0 -0
- {tesorotools_python-0.0.34/src/tesorotools/dependencies → tesorotools_python-0.0.36/src/tesorotools/offsets}/__init__.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/offsets/offsets.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/offsets/outliers.py +0 -0
- {tesorotools_python-0.0.34/src/tesorotools/offsets → tesorotools_python-0.0.36/src/tesorotools/pipeline}/__init__.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/pipeline/diagnose.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/pipeline/engine.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/pipeline/rules.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/providers/base.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/providers/bde.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/providers/ecb.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/py.typed +0 -0
- {tesorotools_python-0.0.34/src/tesorotools/pipeline → tesorotools_python-0.0.36/src/tesorotools/render/content}/__init__.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/render/content/content.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/render/content/images.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/render/content/section.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/render/content/subtitle.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/render/content/text.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/render/content/title.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/render/report.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/testing/__init__.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/testing/compare.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/utils/__init__.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/utils/format.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/utils/globals.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/utils/matplotlib.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/utils/series.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/utils/shortcuts.py +0 -0
- {tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/utils/template.py +0 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""tesorotools public API.
|
|
2
|
+
|
|
3
|
+
Importing ``tesorotools`` eagerly loads artist and render
|
|
4
|
+
classes (their modules run matplotlib/locale setup as side
|
|
5
|
+
effects) and registers their YAML tags via
|
|
6
|
+
``_register_builtins``.
|
|
7
|
+
|
|
8
|
+
Provider subclasses gated by optional extras
|
|
9
|
+
(``BdeProvider`` requires ``[bde]``, ``EcbProvider``
|
|
10
|
+
requires ``[ecb]``) are exposed lazily through
|
|
11
|
+
``__getattr__``; importing this module does not require the
|
|
12
|
+
extras to be installed.
|
|
13
|
+
|
|
14
|
+
Third parties extend the package via ``register_artist``,
|
|
15
|
+
``register_tag``, and ``register_provider``.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from typing import TYPE_CHECKING, Any
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from tesorotools.providers.bde import BdeProvider
|
|
22
|
+
from tesorotools.providers.ecb import EcbProvider
|
|
23
|
+
|
|
24
|
+
from tesorotools._registry import (
|
|
25
|
+
Artist,
|
|
26
|
+
get_artist,
|
|
27
|
+
get_provider,
|
|
28
|
+
register_artist,
|
|
29
|
+
register_provider,
|
|
30
|
+
register_tag,
|
|
31
|
+
)
|
|
32
|
+
from tesorotools.artists import (
|
|
33
|
+
Format,
|
|
34
|
+
HorizontalBarChart,
|
|
35
|
+
Legend,
|
|
36
|
+
LinePlot,
|
|
37
|
+
StackedAreaPlot,
|
|
38
|
+
StackedBarPlot,
|
|
39
|
+
TypeCurve,
|
|
40
|
+
)
|
|
41
|
+
from tesorotools.providers.base import DataProvider
|
|
42
|
+
from tesorotools.render import (
|
|
43
|
+
Content,
|
|
44
|
+
Image,
|
|
45
|
+
Images,
|
|
46
|
+
Report,
|
|
47
|
+
Section,
|
|
48
|
+
Subtitle,
|
|
49
|
+
Table,
|
|
50
|
+
Text,
|
|
51
|
+
Title,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _register_builtins() -> None:
|
|
56
|
+
register_artist("line_plot", LinePlot)
|
|
57
|
+
register_artist("stacked_area", StackedAreaPlot)
|
|
58
|
+
register_artist("stacked_bar", StackedBarPlot)
|
|
59
|
+
register_artist("barh", HorizontalBarChart)
|
|
60
|
+
register_artist("type_curve", TypeCurve)
|
|
61
|
+
|
|
62
|
+
register_tag("format", Format)
|
|
63
|
+
register_tag("legend", Legend)
|
|
64
|
+
register_tag("report", Report)
|
|
65
|
+
register_tag("section", Section)
|
|
66
|
+
register_tag("image", Image)
|
|
67
|
+
register_tag("images", Images)
|
|
68
|
+
register_tag("table", Table)
|
|
69
|
+
register_tag("text", Text)
|
|
70
|
+
register_tag("title", Title)
|
|
71
|
+
register_tag("subtitle", Subtitle)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
_register_builtins()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
__all__ = [
|
|
78
|
+
"Artist",
|
|
79
|
+
"BdeProvider",
|
|
80
|
+
"Content",
|
|
81
|
+
"DataProvider",
|
|
82
|
+
"EcbProvider",
|
|
83
|
+
"Format",
|
|
84
|
+
"HorizontalBarChart",
|
|
85
|
+
"Image",
|
|
86
|
+
"Images",
|
|
87
|
+
"Legend",
|
|
88
|
+
"LinePlot",
|
|
89
|
+
"Report",
|
|
90
|
+
"Section",
|
|
91
|
+
"StackedAreaPlot",
|
|
92
|
+
"StackedBarPlot",
|
|
93
|
+
"Subtitle",
|
|
94
|
+
"Table",
|
|
95
|
+
"Text",
|
|
96
|
+
"Title",
|
|
97
|
+
"TypeCurve",
|
|
98
|
+
"get_artist",
|
|
99
|
+
"get_provider",
|
|
100
|
+
"register_artist",
|
|
101
|
+
"register_provider",
|
|
102
|
+
"register_tag",
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def __getattr__(name: str) -> Any:
|
|
107
|
+
if name == "BdeProvider":
|
|
108
|
+
from tesorotools.providers.bde import BdeProvider
|
|
109
|
+
|
|
110
|
+
return BdeProvider
|
|
111
|
+
if name == "EcbProvider":
|
|
112
|
+
from tesorotools.providers.ecb import EcbProvider
|
|
113
|
+
|
|
114
|
+
return EcbProvider
|
|
115
|
+
raise AttributeError(f"module 'tesorotools' has no attribute {name!r}")
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Registries for artists, providers, and YAML tags.
|
|
2
|
+
|
|
3
|
+
Single source of truth for "name -> class" lookups used both
|
|
4
|
+
by the YAML loader (``TemplateLoader``) and by code that
|
|
5
|
+
dispatches by string name.
|
|
6
|
+
|
|
7
|
+
Three public entry points:
|
|
8
|
+
|
|
9
|
+
``register_artist(name, cls)``
|
|
10
|
+
Adds ``cls`` to the artist registry **and** registers the
|
|
11
|
+
YAML tag ``!{name}`` via ``cls.from_yaml``. Use for
|
|
12
|
+
chart/figure classes (LinePlot, StackedAreaPlot, etc.).
|
|
13
|
+
|
|
14
|
+
``register_tag(name, constructor)``
|
|
15
|
+
Registers a YAML tag ``!{name}`` only. Use for things
|
|
16
|
+
that exist as YAML constructors but are not artists or
|
|
17
|
+
providers (Format, Legend, Title, Subtitle, Section,
|
|
18
|
+
Image, Text, Table, Report).
|
|
19
|
+
|
|
20
|
+
``register_provider(name, cls)``
|
|
21
|
+
Adds ``cls`` to the provider registry. Programmatic
|
|
22
|
+
only -- providers do not appear in YAML today.
|
|
23
|
+
|
|
24
|
+
Look up registered classes via ``get_artist`` / ``get_provider``;
|
|
25
|
+
both raise ``KeyError`` listing the available names.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
from typing import Any, Callable, Protocol, cast
|
|
31
|
+
|
|
32
|
+
from yaml.nodes import MappingNode
|
|
33
|
+
|
|
34
|
+
from tesorotools.providers.base import DataProvider
|
|
35
|
+
from tesorotools.utils.template import TemplateLoader
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Artist(Protocol):
|
|
39
|
+
"""Anything constructible from a YAML mapping node.
|
|
40
|
+
|
|
41
|
+
Concrete artists satisfy this Protocol structurally;
|
|
42
|
+
inheriting from it is not required.
|
|
43
|
+
|
|
44
|
+
The ``loader`` parameter is typed as ``Any`` because some
|
|
45
|
+
in-tree ``from_yaml`` classmethods are declared with
|
|
46
|
+
``yaml.Loader`` (the base class) and others with
|
|
47
|
+
``TemplateLoader``; both work at runtime since the loader
|
|
48
|
+
is always a ``TemplateLoader``.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
@classmethod
|
|
52
|
+
def from_yaml(cls, loader: Any, node: MappingNode) -> Any: ...
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
YamlConstructor = Callable[[Any, MappingNode], Any]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
_ARTIST_REGISTRY: dict[str, type[Artist]] = {}
|
|
59
|
+
_PROVIDER_REGISTRY: dict[str, type[DataProvider]] = {}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def register_artist(name: str, cls: type[Artist]) -> None:
|
|
63
|
+
"""Register ``cls`` as the artist for ``name``.
|
|
64
|
+
|
|
65
|
+
Adds ``cls`` to the artist registry and binds the YAML
|
|
66
|
+
tag ``!{name}`` to ``cls.from_yaml``. Re-registering the
|
|
67
|
+
same name overrides both bindings.
|
|
68
|
+
"""
|
|
69
|
+
_ARTIST_REGISTRY[name] = cls
|
|
70
|
+
TemplateLoader.add_constructor(
|
|
71
|
+
f"!{name}", cast(YamlConstructor, cls.from_yaml)
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_artist(name: str) -> type[Artist]:
|
|
76
|
+
try:
|
|
77
|
+
return _ARTIST_REGISTRY[name]
|
|
78
|
+
except KeyError:
|
|
79
|
+
available = sorted(_ARTIST_REGISTRY)
|
|
80
|
+
raise KeyError(
|
|
81
|
+
f"No artist registered as {name!r}. Available: {available}"
|
|
82
|
+
) from None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def register_provider(name: str, cls: type[DataProvider]) -> None:
|
|
86
|
+
"""Register ``cls`` as the provider for ``name``."""
|
|
87
|
+
_PROVIDER_REGISTRY[name] = cls
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def get_provider(name: str) -> type[DataProvider]:
|
|
91
|
+
try:
|
|
92
|
+
return _PROVIDER_REGISTRY[name]
|
|
93
|
+
except KeyError:
|
|
94
|
+
available = sorted(_PROVIDER_REGISTRY)
|
|
95
|
+
raise KeyError(
|
|
96
|
+
f"No provider registered as {name!r}. Available: {available}"
|
|
97
|
+
) from None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def register_tag(
|
|
101
|
+
name: str,
|
|
102
|
+
constructor: type[Artist] | YamlConstructor,
|
|
103
|
+
) -> None:
|
|
104
|
+
"""Register a YAML tag ``!{name}``.
|
|
105
|
+
|
|
106
|
+
Accepts either a class with a ``from_yaml`` classmethod
|
|
107
|
+
or a bare callable matching the loader signature. Use
|
|
108
|
+
``register_artist`` / ``register_provider`` instead when
|
|
109
|
+
the registered name should also be looked up by code.
|
|
110
|
+
"""
|
|
111
|
+
if isinstance(constructor, type):
|
|
112
|
+
cls = cast(type[Artist], constructor)
|
|
113
|
+
TemplateLoader.add_constructor(
|
|
114
|
+
f"!{name}", cast(YamlConstructor, cls.from_yaml)
|
|
115
|
+
)
|
|
116
|
+
else:
|
|
117
|
+
TemplateLoader.add_constructor(f"!{name}", constructor)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Public artist API.
|
|
2
|
+
|
|
3
|
+
Importing this module applies the matplotlib stylesheet
|
|
4
|
+
defined by ``tesorotools.utils.globals.STYLE_SHEET``.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import matplotlib.style
|
|
8
|
+
|
|
9
|
+
from tesorotools.artists.barh_plot import HorizontalBarChart
|
|
10
|
+
from tesorotools.artists.line_plot import Format, Legend, LinePlot
|
|
11
|
+
from tesorotools.artists.stacked import StackedAreaPlot, StackedBarPlot
|
|
12
|
+
from tesorotools.artists.type_curve import TypeCurve
|
|
13
|
+
from tesorotools.utils.globals import STYLE_SHEET
|
|
14
|
+
|
|
15
|
+
matplotlib.style.use(STYLE_SHEET)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"Format",
|
|
19
|
+
"HorizontalBarChart",
|
|
20
|
+
"Legend",
|
|
21
|
+
"LinePlot",
|
|
22
|
+
"StackedAreaPlot",
|
|
23
|
+
"StackedBarPlot",
|
|
24
|
+
"TypeCurve",
|
|
25
|
+
]
|
{tesorotools_python-0.0.34 → tesorotools_python-0.0.36}/src/tesorotools/artists/line_plot.py
RENAMED
|
@@ -310,21 +310,20 @@ def style_baseline(
|
|
|
310
310
|
reference: float = 0,
|
|
311
311
|
**baseline_config: Any,
|
|
312
312
|
) -> None:
|
|
313
|
-
|
|
313
|
+
"""Draw a horizontal baseline at *reference*.
|
|
314
|
+
|
|
315
|
+
Always uses ``axhline`` (with high zorder) so callers that
|
|
316
|
+
later restyle the spines do not silently erase the baseline.
|
|
317
|
+
"""
|
|
314
318
|
bottom_lim, top_lim = ax.get_ylim()
|
|
315
319
|
ax.set_ylim(
|
|
316
320
|
bottom=min(reference, bottom_lim),
|
|
317
321
|
top=max(reference, top_lim),
|
|
318
322
|
)
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
ax.spines["top"].set_edgecolor(color)
|
|
324
|
-
else:
|
|
325
|
-
ax.axhline( # type: ignore[reportUnknownMemberType]
|
|
326
|
-
y=reference, **baseline_config
|
|
327
|
-
)
|
|
323
|
+
baseline_config.setdefault("zorder", 2.5)
|
|
324
|
+
ax.axhline( # type: ignore[reportUnknownMemberType]
|
|
325
|
+
y=reference, **baseline_config
|
|
326
|
+
)
|
|
328
327
|
|
|
329
328
|
|
|
330
329
|
def plot_line_chart(
|
|
@@ -333,12 +332,12 @@ def plot_line_chart(
|
|
|
333
332
|
*,
|
|
334
333
|
base_100: bool,
|
|
335
334
|
annotate: bool,
|
|
336
|
-
|
|
335
|
+
fmt: dict[str, Any],
|
|
337
336
|
**kwargs: Any,
|
|
338
337
|
) -> None:
|
|
339
338
|
if base_100:
|
|
340
339
|
data = data / data.iloc[0, :] * 100
|
|
341
|
-
if
|
|
340
|
+
if fmt["units"] == "p.b.":
|
|
342
341
|
data = data * 100
|
|
343
342
|
fig: Figure = plt.figure( # type: ignore[reportUnknownMemberType]
|
|
344
343
|
**FIG_CONFIG
|
|
@@ -349,11 +348,13 @@ def plot_line_chart(
|
|
|
349
348
|
pass
|
|
350
349
|
|
|
351
350
|
reference = 100 if base_100 else 0
|
|
352
|
-
style_spines(ax, **
|
|
351
|
+
style_spines(ax, **fmt, **AX_CONFIG["spines"])
|
|
353
352
|
style_baseline(ax, reference, **AX_CONFIG["baseline"])
|
|
354
|
-
ax.
|
|
355
|
-
|
|
356
|
-
|
|
353
|
+
handles, label_strs = ax.get_legend_handles_labels()
|
|
354
|
+
fig.legend( # type: ignore[reportUnknownMemberType]
|
|
355
|
+
handles,
|
|
356
|
+
label_strs,
|
|
357
|
+
loc="outside lower center",
|
|
357
358
|
ncol=(
|
|
358
359
|
kwargs["legend"]["ncol"]
|
|
359
360
|
if kwargs.get("legend", None) is not None
|
|
@@ -407,10 +408,8 @@ class Legend:
|
|
|
407
408
|
def __init__(
|
|
408
409
|
self,
|
|
409
410
|
ncol: int | None = None,
|
|
410
|
-
sep: float | None = None,
|
|
411
411
|
) -> None:
|
|
412
412
|
self.ncol = ncol
|
|
413
|
-
self.sep = sep
|
|
414
413
|
|
|
415
414
|
@classmethod
|
|
416
415
|
def from_yaml(cls, loader: TemplateLoader, node: MappingNode) -> Self:
|
|
@@ -436,7 +435,7 @@ class LinePlot:
|
|
|
436
435
|
annotate: bool = False,
|
|
437
436
|
annotate_color: str | None = None,
|
|
438
437
|
baseline: bool = False,
|
|
439
|
-
|
|
438
|
+
fmt: Format | None = None,
|
|
440
439
|
legend: Legend | None = None,
|
|
441
440
|
data: pd.DataFrame | None = None,
|
|
442
441
|
figsize: tuple[float, float] | None = None,
|
|
@@ -469,7 +468,7 @@ class LinePlot:
|
|
|
469
468
|
self.base_100_date = base_100_date
|
|
470
469
|
self.annotate = annotate
|
|
471
470
|
self.annotate_color = annotate_color
|
|
472
|
-
self.
|
|
471
|
+
self.fmt = fmt
|
|
473
472
|
self.start_date = start_date
|
|
474
473
|
self.end_date = end_date
|
|
475
474
|
self.series = series
|
|
@@ -545,13 +544,13 @@ class LinePlot:
|
|
|
545
544
|
if self.vlines:
|
|
546
545
|
draw_vlines(ax, self.vlines)
|
|
547
546
|
|
|
548
|
-
assert self.
|
|
547
|
+
assert self.fmt is not None
|
|
549
548
|
if self.annotate:
|
|
550
549
|
annotate_last_values(
|
|
551
550
|
ax,
|
|
552
551
|
plot_data,
|
|
553
|
-
decimals=self.
|
|
554
|
-
units=self.
|
|
552
|
+
decimals=self.fmt.decimals,
|
|
553
|
+
units=self.fmt.units,
|
|
555
554
|
labels=self.series,
|
|
556
555
|
series_styles=self.series_styles,
|
|
557
556
|
annotate_color=self.annotate_color,
|
|
@@ -559,8 +558,8 @@ class LinePlot:
|
|
|
559
558
|
|
|
560
559
|
style_spines( # maybe make this function accept a Format object
|
|
561
560
|
ax,
|
|
562
|
-
decimals=self.
|
|
563
|
-
units=self.
|
|
561
|
+
decimals=self.fmt.decimals,
|
|
562
|
+
units=self.fmt.units,
|
|
564
563
|
**AX_CONFIG["spines"],
|
|
565
564
|
)
|
|
566
565
|
if self.baseline:
|
|
@@ -569,25 +568,19 @@ class LinePlot:
|
|
|
569
568
|
|
|
570
569
|
if self.legend is not None:
|
|
571
570
|
labels = [self.series[c] for c in plot_data.columns]
|
|
571
|
+
handles, label_strs = ax.get_legend_handles_labels()
|
|
572
|
+
fig_width_px: float = fig.get_size_inches()[0] * fig.dpi
|
|
572
573
|
ncol = (
|
|
573
574
|
self.legend.ncol
|
|
574
575
|
if self.legend.ncol is not None
|
|
575
|
-
else auto_ncol(ax, labels)
|
|
576
|
-
)
|
|
577
|
-
sep = (
|
|
578
|
-
self.legend.sep
|
|
579
|
-
if self.legend.sep is not None
|
|
580
|
-
else LINE_PLOT_CONFIG["legend_sep"]
|
|
576
|
+
else auto_ncol(ax, labels, available_width_px=fig_width_px)
|
|
581
577
|
)
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
578
|
+
fig.legend( # type: ignore[reportUnknownMemberType]
|
|
579
|
+
handles,
|
|
580
|
+
label_strs,
|
|
581
|
+
loc="outside lower center",
|
|
585
582
|
ncol=ncol,
|
|
586
583
|
)
|
|
587
|
-
else:
|
|
588
|
-
ax.legend().set_visible( # type: ignore[reportUnknownMemberType]
|
|
589
|
-
False
|
|
590
|
-
)
|
|
591
584
|
|
|
592
585
|
if self.plot_size is not None:
|
|
593
586
|
adjust_figure_for_plot_size(fig, ax, self.plot_size)
|
|
@@ -22,8 +22,6 @@ from tesorotools.artists.line_plot import (
|
|
|
22
22
|
)
|
|
23
23
|
from tesorotools.utils.config import TemplateLoader
|
|
24
24
|
|
|
25
|
-
_DEFAULT_SEP = -0.125
|
|
26
|
-
|
|
27
25
|
|
|
28
26
|
class StackedAreaPlot:
|
|
29
27
|
"""Stacked area chart with the tesorotools visual style.
|
|
@@ -43,7 +41,7 @@ class StackedAreaPlot:
|
|
|
43
41
|
start_date: str | None = None,
|
|
44
42
|
end_date: str | None = None,
|
|
45
43
|
baseline: bool = False,
|
|
46
|
-
|
|
44
|
+
fmt: Format | None = None,
|
|
47
45
|
legend: Legend | None = None,
|
|
48
46
|
figsize: tuple[float, float] | None = None,
|
|
49
47
|
plot_size: tuple[float, float] | None = None,
|
|
@@ -57,7 +55,7 @@ class StackedAreaPlot:
|
|
|
57
55
|
self.start_date = start_date
|
|
58
56
|
self.end_date = end_date
|
|
59
57
|
self.baseline = baseline
|
|
60
|
-
self.
|
|
58
|
+
self.fmt = fmt or Format()
|
|
61
59
|
self.legend = legend
|
|
62
60
|
self.figsize = figsize
|
|
63
61
|
self.plot_size = plot_size
|
|
@@ -109,23 +107,25 @@ class StackedAreaPlot:
|
|
|
109
107
|
|
|
110
108
|
style_spines(
|
|
111
109
|
ax,
|
|
112
|
-
decimals=self.
|
|
113
|
-
units=self.
|
|
110
|
+
decimals=self.fmt.decimals,
|
|
111
|
+
units=self.fmt.units,
|
|
114
112
|
**AX_CONFIG["spines"],
|
|
115
113
|
)
|
|
116
114
|
if self.baseline:
|
|
117
115
|
style_baseline(ax, 0, **AX_CONFIG["baseline"])
|
|
118
116
|
|
|
117
|
+
fig_width_px: float = fig.get_size_inches()[0] * fig.dpi
|
|
119
118
|
legend_ncol = self.legend.ncol if self.legend else None
|
|
120
|
-
ncol =
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
else _DEFAULT_SEP
|
|
119
|
+
ncol = (
|
|
120
|
+
legend_ncol
|
|
121
|
+
if legend_ncol is not None
|
|
122
|
+
else auto_ncol(ax, labels, available_width_px=fig_width_px)
|
|
125
123
|
)
|
|
126
|
-
ax.
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
handles, label_strs = ax.get_legend_handles_labels()
|
|
125
|
+
fig.legend( # type: ignore[reportUnknownMemberType]
|
|
126
|
+
handles,
|
|
127
|
+
label_strs,
|
|
128
|
+
loc="outside lower center",
|
|
129
129
|
ncol=ncol,
|
|
130
130
|
)
|
|
131
131
|
|
|
@@ -163,7 +163,7 @@ class StackedBarPlot:
|
|
|
163
163
|
start_date: str | None = None,
|
|
164
164
|
end_date: str | None = None,
|
|
165
165
|
baseline: bool = True,
|
|
166
|
-
|
|
166
|
+
fmt: Format | None = None,
|
|
167
167
|
legend: Legend | None = None,
|
|
168
168
|
figsize: tuple[float, float] | None = None,
|
|
169
169
|
overlay_series: dict[str, str] | None = None,
|
|
@@ -180,7 +180,7 @@ class StackedBarPlot:
|
|
|
180
180
|
self.start_date = start_date
|
|
181
181
|
self.end_date = end_date
|
|
182
182
|
self.baseline = baseline
|
|
183
|
-
self.
|
|
183
|
+
self.fmt = fmt or Format()
|
|
184
184
|
self.legend = legend
|
|
185
185
|
self.plot_size = plot_size
|
|
186
186
|
self.figsize = figsize
|
|
@@ -313,8 +313,8 @@ class StackedBarPlot:
|
|
|
313
313
|
|
|
314
314
|
style_spines(
|
|
315
315
|
ax,
|
|
316
|
-
decimals=self.
|
|
317
|
-
units=self.
|
|
316
|
+
decimals=self.fmt.decimals,
|
|
317
|
+
units=self.fmt.units,
|
|
318
318
|
**AX_CONFIG["spines"],
|
|
319
319
|
)
|
|
320
320
|
if self.baseline:
|
|
@@ -323,20 +323,18 @@ class StackedBarPlot:
|
|
|
323
323
|
all_labels = list(self.series.values()) + list(
|
|
324
324
|
self.overlay_series.values()
|
|
325
325
|
)
|
|
326
|
+
fig_width_px: float = fig.get_size_inches()[0] * fig.dpi
|
|
326
327
|
legend_ncol = self.legend.ncol if self.legend else None
|
|
327
328
|
ncol = (
|
|
328
329
|
legend_ncol
|
|
329
330
|
if legend_ncol is not None
|
|
330
|
-
else auto_ncol(ax, all_labels)
|
|
331
|
-
)
|
|
332
|
-
sep = (
|
|
333
|
-
self.legend.sep
|
|
334
|
-
if self.legend and self.legend.sep is not None
|
|
335
|
-
else _DEFAULT_SEP
|
|
331
|
+
else auto_ncol(ax, all_labels, available_width_px=fig_width_px)
|
|
336
332
|
)
|
|
337
|
-
ax.
|
|
338
|
-
|
|
339
|
-
|
|
333
|
+
handles, label_strs = ax.get_legend_handles_labels()
|
|
334
|
+
fig.legend( # type: ignore[reportUnknownMemberType]
|
|
335
|
+
handles,
|
|
336
|
+
label_strs,
|
|
337
|
+
loc="outside lower center",
|
|
340
338
|
ncol=ncol,
|
|
341
339
|
)
|
|
342
340
|
|