tesorotools-python 0.0.42__tar.gz → 0.0.44__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.42 → tesorotools_python-0.0.44}/.gitignore +26 -29
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/PKG-INFO +1 -1
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/pyproject.toml +69 -66
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/__init__.py +3 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/artists/__init__.py +2 -1
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/artists/barh_plot.py +207 -1
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/assets/README.md +4 -4
- tesorotools_python-0.0.44/src/tesorotools/assets/fonts/README.md +4 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/assets/plots.yaml +44 -44
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/assets/tesoro.mplstyle +21 -21
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/database/__init__.py +14 -14
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/providers/ecb.py +217 -217
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/render/content/content.py +17 -17
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/render/content/images.py +159 -159
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/render/content/table.py +505 -505
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/testing/compare.py +147 -147
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/utils/__init__.py +3 -3
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/utils/format.py +59 -59
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/utils/globals.py +18 -18
- tesorotools_python-0.0.44/src/tesorotools/utils/shortcuts.py +42 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/utils/template.py +132 -132
- tesorotools_python-0.0.42/src/tesorotools/assets/fonts/README.md +0 -1
- tesorotools_python-0.0.42/src/tesorotools/utils/shortcuts.py +0 -35
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/_build_context.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/_registry.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/artists/_common.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/artists/line_plot.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/artists/stacked.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/artists/type_curve.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/assets/fonts/CabinetGrotesk-Black.otf +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/assets/fonts/CabinetGrotesk-Bold.otf +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/assets/fonts/CabinetGrotesk-Extrabold.otf +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/assets/fonts/CabinetGrotesk-Extralight.otf +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/assets/fonts/CabinetGrotesk-Light.otf +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/assets/fonts/CabinetGrotesk-Medium.otf +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/assets/fonts/CabinetGrotesk-Regular.otf +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/assets/fonts/CabinetGrotesk-Thin.otf +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/data_sources/__init__.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/data_sources/debug.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/database/local.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/database/push.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/database/shared.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/dependencies/__init__.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/dependencies/node.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/dependencies/resolution.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/driver.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/manifest.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/offsets/__init__.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/offsets/offsets.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/offsets/outliers.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/orchestration.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/pipeline/__init__.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/pipeline/diagnose.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/pipeline/engine.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/pipeline/rules.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/providers/__init__.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/providers/base.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/providers/bde.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/providers/lseg.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/py.typed +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/render/__init__.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/render/content/__init__.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/render/content/section.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/render/content/subtitle.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/render/content/text.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/render/content/title.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/render/report.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/testing/__init__.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/utils/config.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/utils/matplotlib.py +0 -0
- {tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/utils/series.py +0 -0
|
@@ -1,30 +1,27 @@
|
|
|
1
|
-
.venv/
|
|
2
|
-
.build1/
|
|
3
|
-
|
|
4
|
-
__pycache__
|
|
5
|
-
|
|
6
|
-
.vscode/
|
|
7
|
-
|
|
8
|
-
data/
|
|
9
|
-
config/
|
|
10
|
-
debug/
|
|
11
|
-
tablas/
|
|
12
|
-
|
|
13
|
-
dist/
|
|
14
|
-
|
|
15
|
-
*.png
|
|
16
|
-
*.feather
|
|
17
|
-
*.csv
|
|
18
|
-
*.docx
|
|
19
|
-
*.docx#
|
|
20
|
-
|
|
21
|
-
test/
|
|
22
|
-
|
|
23
|
-
#
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
#
|
|
27
|
-
.coverage
|
|
28
|
-
|
|
29
|
-
# ruff
|
|
1
|
+
.venv/
|
|
2
|
+
.build1/
|
|
3
|
+
|
|
4
|
+
__pycache__
|
|
5
|
+
|
|
6
|
+
.vscode/
|
|
7
|
+
|
|
8
|
+
data/
|
|
9
|
+
config/
|
|
10
|
+
debug/
|
|
11
|
+
tablas/
|
|
12
|
+
|
|
13
|
+
dist/
|
|
14
|
+
|
|
15
|
+
*.png
|
|
16
|
+
*.feather
|
|
17
|
+
*.csv
|
|
18
|
+
*.docx
|
|
19
|
+
*.docx#
|
|
20
|
+
|
|
21
|
+
test/
|
|
22
|
+
|
|
23
|
+
# coverage
|
|
24
|
+
.coverage
|
|
25
|
+
|
|
26
|
+
# ruff
|
|
30
27
|
.ruff_cache/
|
|
@@ -1,66 +1,69 @@
|
|
|
1
|
-
[project]
|
|
2
|
-
name = "tesorotools-python"
|
|
3
|
-
requires-python = ">=3.13"
|
|
4
|
-
|
|
5
|
-
dependencies = [
|
|
6
|
-
# database and ORM
|
|
7
|
-
"psycopg[binary]>=3.1",
|
|
8
|
-
"SQLAlchemy>=2.0",
|
|
9
|
-
|
|
10
|
-
# data analysis
|
|
11
|
-
"pandas>=2.2",
|
|
12
|
-
"pyarrow>=18.0",
|
|
13
|
-
"openpyxl>=3.1",
|
|
14
|
-
|
|
15
|
-
# utils
|
|
16
|
-
"PyYAML>=6.0",
|
|
17
|
-
"babel>=2.17",
|
|
18
|
-
|
|
19
|
-
# data visualization
|
|
20
|
-
"matplotlib>=3.10",
|
|
21
|
-
"python-docx>=1.1",
|
|
22
|
-
|
|
23
|
-
# os dependencies
|
|
24
|
-
"pywin32>=311; sys_platform == 'win32'",
|
|
25
|
-
]
|
|
26
|
-
|
|
27
|
-
[project.optional-dependencies]
|
|
28
|
-
bde = ["requests>=2.31"]
|
|
29
|
-
ecb = ["requests>=2.31"]
|
|
30
|
-
lseg = ["lseg-data>=2.1"]
|
|
31
|
-
|
|
32
|
-
[dependency-groups]
|
|
33
|
-
dev = [
|
|
34
|
-
"ruff>=0.8",
|
|
35
|
-
"pyright>=1.1",
|
|
36
|
-
"pre-commit>=4.0",
|
|
37
|
-
"pandas-stubs>=2.2",
|
|
38
|
-
"coverage>=7.0",
|
|
39
|
-
]
|
|
40
|
-
|
|
41
|
-
[build-system]
|
|
42
|
-
requires = ["hatchling"]
|
|
43
|
-
build-backend = "hatchling.build"
|
|
44
|
-
|
|
45
|
-
[tool.hatch.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
[
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
[
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
[tool.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
1
|
+
[project]
|
|
2
|
+
name = "tesorotools-python"
|
|
3
|
+
requires-python = ">=3.13"
|
|
4
|
+
dynamic = ["version"]
|
|
5
|
+
dependencies = [
|
|
6
|
+
# database and ORM
|
|
7
|
+
"psycopg[binary]>=3.1",
|
|
8
|
+
"SQLAlchemy>=2.0",
|
|
9
|
+
|
|
10
|
+
# data analysis
|
|
11
|
+
"pandas>=2.2",
|
|
12
|
+
"pyarrow>=18.0",
|
|
13
|
+
"openpyxl>=3.1",
|
|
14
|
+
|
|
15
|
+
# utils
|
|
16
|
+
"PyYAML>=6.0",
|
|
17
|
+
"babel>=2.17",
|
|
18
|
+
|
|
19
|
+
# data visualization
|
|
20
|
+
"matplotlib>=3.10",
|
|
21
|
+
"python-docx>=1.1",
|
|
22
|
+
|
|
23
|
+
# os dependencies
|
|
24
|
+
"pywin32>=311; sys_platform == 'win32'",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
bde = ["requests>=2.31"]
|
|
29
|
+
ecb = ["requests>=2.31"]
|
|
30
|
+
lseg = ["lseg-data>=2.1"]
|
|
31
|
+
|
|
32
|
+
[dependency-groups]
|
|
33
|
+
dev = [
|
|
34
|
+
"ruff>=0.8",
|
|
35
|
+
"pyright>=1.1",
|
|
36
|
+
"pre-commit>=4.0",
|
|
37
|
+
"pandas-stubs>=2.2",
|
|
38
|
+
"coverage>=7.0",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[build-system]
|
|
42
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
43
|
+
build-backend = "hatchling.build"
|
|
44
|
+
|
|
45
|
+
[tool.hatch.version]
|
|
46
|
+
source = "vcs"
|
|
47
|
+
|
|
48
|
+
[tool.hatch.build.targets.wheel]
|
|
49
|
+
packages = ["src/tesorotools"]
|
|
50
|
+
artifacts = ["src/tesorotools/assets/fonts/*.otf"]
|
|
51
|
+
|
|
52
|
+
[tool.hatch.build.targets.sdist]
|
|
53
|
+
include = ["src/tesorotools"]
|
|
54
|
+
artifacts = ["src/tesorotools/assets/fonts/*.otf"]
|
|
55
|
+
|
|
56
|
+
[tool.coverage.report]
|
|
57
|
+
exclude_lines = [
|
|
58
|
+
'if __name__ == "__main__"',
|
|
59
|
+
'^\s*\.\.\.$',
|
|
60
|
+
'if TYPE_CHECKING',
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
[tool.ruff]
|
|
64
|
+
line-length = 80
|
|
65
|
+
|
|
66
|
+
[tool.pyright]
|
|
67
|
+
pythonVersion = "3.13"
|
|
68
|
+
extraPaths = ["src"]
|
|
69
|
+
typeCheckingMode = "strict"
|
|
@@ -45,6 +45,7 @@ from tesorotools._registry import (
|
|
|
45
45
|
)
|
|
46
46
|
from tesorotools.artists import (
|
|
47
47
|
Format,
|
|
48
|
+
GroupedBarChart,
|
|
48
49
|
HorizontalBarChart,
|
|
49
50
|
Legend,
|
|
50
51
|
LinePlot,
|
|
@@ -76,6 +77,7 @@ def _register_builtins() -> None:
|
|
|
76
77
|
register_artist("stacked_area", StackedAreaPlot)
|
|
77
78
|
register_artist("stacked_bar", StackedBarPlot)
|
|
78
79
|
register_artist("barh", HorizontalBarChart)
|
|
80
|
+
register_artist("grouped_barh", GroupedBarChart)
|
|
79
81
|
register_artist("type_curve", TypeCurve)
|
|
80
82
|
|
|
81
83
|
register_tag("format", Format)
|
|
@@ -102,6 +104,7 @@ __all__ = [
|
|
|
102
104
|
"DataProvider",
|
|
103
105
|
"EcbProvider",
|
|
104
106
|
"Format",
|
|
107
|
+
"GroupedBarChart",
|
|
105
108
|
"HorizontalBarChart",
|
|
106
109
|
"Image",
|
|
107
110
|
"Images",
|
|
@@ -27,7 +27,7 @@ strictly image output.
|
|
|
27
27
|
import matplotlib.style
|
|
28
28
|
|
|
29
29
|
from tesorotools.artists._common import Artist, Format, Legend
|
|
30
|
-
from tesorotools.artists.barh_plot import HorizontalBarChart
|
|
30
|
+
from tesorotools.artists.barh_plot import GroupedBarChart, HorizontalBarChart
|
|
31
31
|
from tesorotools.artists.line_plot import LinePlot
|
|
32
32
|
from tesorotools.artists.stacked import StackedAreaPlot, StackedBarPlot
|
|
33
33
|
from tesorotools.artists.type_curve import TypeCurve
|
|
@@ -38,6 +38,7 @@ matplotlib.style.use(STYLE_SHEET)
|
|
|
38
38
|
__all__ = [
|
|
39
39
|
"Artist",
|
|
40
40
|
"Format",
|
|
41
|
+
"GroupedBarChart",
|
|
41
42
|
"HorizontalBarChart",
|
|
42
43
|
"Legend",
|
|
43
44
|
"LinePlot",
|
{tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/artists/barh_plot.py
RENAMED
|
@@ -21,6 +21,7 @@ from pathlib import Path
|
|
|
21
21
|
from typing import Any, Self
|
|
22
22
|
|
|
23
23
|
import matplotlib.pyplot as plt
|
|
24
|
+
import numpy as np
|
|
24
25
|
import pandas as pd
|
|
25
26
|
from matplotlib.axes import Axes
|
|
26
27
|
from matplotlib.container import BarContainer
|
|
@@ -32,6 +33,9 @@ from tesorotools.artists._common import (
|
|
|
32
33
|
AX_CONFIG,
|
|
33
34
|
FIG_CONFIG,
|
|
34
35
|
Format,
|
|
36
|
+
Legend,
|
|
37
|
+
adjust_figure_for_plot_size,
|
|
38
|
+
auto_ncol,
|
|
35
39
|
resolve_data,
|
|
36
40
|
)
|
|
37
41
|
from tesorotools.utils.matplotlib import PLOT_CONFIG, format_annotation
|
|
@@ -39,7 +43,7 @@ from tesorotools.utils.template import TemplateLoader
|
|
|
39
43
|
|
|
40
44
|
BARH_CONFIG: dict[str, Any] = PLOT_CONFIG["barh"]
|
|
41
45
|
|
|
42
|
-
__all__ = ["HorizontalBarChart"]
|
|
46
|
+
__all__ = ["GroupedBarChart", "HorizontalBarChart"]
|
|
43
47
|
|
|
44
48
|
|
|
45
49
|
class _Col(Enum):
|
|
@@ -370,3 +374,205 @@ class HorizontalBarChart:
|
|
|
370
374
|
)
|
|
371
375
|
plt.close(fig)
|
|
372
376
|
return ax
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
class GroupedBarChart:
|
|
380
|
+
"""Grouped (clustered) horizontal bar chart artist.
|
|
381
|
+
|
|
382
|
+
Compares two or more series side-by-side within each
|
|
383
|
+
category -- the "double horizontal bar" the single-series
|
|
384
|
+
:class:`HorizontalBarChart` cannot express (e.g. rural vs.
|
|
385
|
+
urban, habitual vs. preferred). Categories run down the
|
|
386
|
+
y-axis; each category holds one bar per series, the i-th
|
|
387
|
+
series coloured with theme colour ``C{i}``.
|
|
388
|
+
|
|
389
|
+
The input ``data`` is a flat ``pd.DataFrame`` indexed by
|
|
390
|
+
category id with one column per series id; ``categories``
|
|
391
|
+
selects and labels the rows and ``series`` selects and
|
|
392
|
+
labels the columns, so the same wide frame can feed
|
|
393
|
+
several charts.
|
|
394
|
+
|
|
395
|
+
Parameters
|
|
396
|
+
----------
|
|
397
|
+
out_path
|
|
398
|
+
``.png`` path the rendered chart is saved to.
|
|
399
|
+
data, data_path
|
|
400
|
+
Provide one. The DataFrame is indexed by the ids in
|
|
401
|
+
``categories`` and carries one column per id in
|
|
402
|
+
``series``.
|
|
403
|
+
categories
|
|
404
|
+
``id -> label`` for the y-axis rows, in display order
|
|
405
|
+
(unless ``sorted``).
|
|
406
|
+
series
|
|
407
|
+
``id -> label`` for the bars within each category, in
|
|
408
|
+
colour order (first gets ``C0``).
|
|
409
|
+
fmt, annot_fmt
|
|
410
|
+
Number formats for the x-axis ticks and the per-bar
|
|
411
|
+
value annotations. ``annot_fmt`` defaults to ``fmt``.
|
|
412
|
+
annotate
|
|
413
|
+
Label each bar with its formatted value (default
|
|
414
|
+
``True``).
|
|
415
|
+
sorted
|
|
416
|
+
Sort categories ascending by the first series' value
|
|
417
|
+
before drawing (default ``False`` -- preserve the
|
|
418
|
+
declared order, which usually mirrors a questionnaire).
|
|
419
|
+
group_width
|
|
420
|
+
Fraction of the category slot occupied by its bar
|
|
421
|
+
group (default ``0.8``); the rest is inter-group gap.
|
|
422
|
+
legend
|
|
423
|
+
Legend layout (``ncol``); when omitted the column
|
|
424
|
+
count is auto-fitted.
|
|
425
|
+
figsize, plot_size
|
|
426
|
+
Override ``FIG_CONFIG['figsize']`` / resize the figure
|
|
427
|
+
so the axes area matches ``plot_size`` after the
|
|
428
|
+
legend is placed.
|
|
429
|
+
"""
|
|
430
|
+
|
|
431
|
+
def __init__(
|
|
432
|
+
self,
|
|
433
|
+
out_path: Path,
|
|
434
|
+
*,
|
|
435
|
+
data: pd.DataFrame | None = None,
|
|
436
|
+
data_path: Path | None = None,
|
|
437
|
+
categories: dict[str, str],
|
|
438
|
+
series: dict[str, str],
|
|
439
|
+
fmt: Format | None = None,
|
|
440
|
+
annot_fmt: Format | None = None,
|
|
441
|
+
annotate: bool = True,
|
|
442
|
+
sorted: bool = False,
|
|
443
|
+
group_width: float = 0.8,
|
|
444
|
+
legend: Legend | None = None,
|
|
445
|
+
figsize: tuple[float, float] | None = None,
|
|
446
|
+
plot_size: tuple[float, float] | None = None,
|
|
447
|
+
) -> None:
|
|
448
|
+
if out_path.suffix != ".png":
|
|
449
|
+
raise ValueError(f"out_path must be .png: {out_path}")
|
|
450
|
+
if not series:
|
|
451
|
+
raise ValueError("Must declare at least one series")
|
|
452
|
+
self.out_path = out_path
|
|
453
|
+
self.data = resolve_data(data, data_path)
|
|
454
|
+
self.categories = categories
|
|
455
|
+
self.series = series
|
|
456
|
+
self.fmt = fmt or Format()
|
|
457
|
+
self.annot_fmt = annot_fmt or self.fmt
|
|
458
|
+
self.annotate = annotate
|
|
459
|
+
self.sorted = sorted
|
|
460
|
+
self.group_width = group_width
|
|
461
|
+
self.legend = legend
|
|
462
|
+
self.figsize = figsize
|
|
463
|
+
self.plot_size = plot_size
|
|
464
|
+
|
|
465
|
+
@classmethod
|
|
466
|
+
def from_yaml(cls, loader: TemplateLoader, node: MappingNode) -> Self:
|
|
467
|
+
"""Build a :class:`GroupedBarChart` from ``!grouped_barh``.
|
|
468
|
+
|
|
469
|
+
Required keys
|
|
470
|
+
-------------
|
|
471
|
+
``out_path``
|
|
472
|
+
``.png`` destination path.
|
|
473
|
+
``data_path``
|
|
474
|
+
``.feather`` file with a DataFrame indexed by
|
|
475
|
+
category id and one column per series id.
|
|
476
|
+
``categories``
|
|
477
|
+
``id -> label`` for the y-axis rows.
|
|
478
|
+
``series``
|
|
479
|
+
``id -> label`` for the bars within each category.
|
|
480
|
+
|
|
481
|
+
Optional keys
|
|
482
|
+
-------------
|
|
483
|
+
``fmt`` (``!format``), ``annot_fmt`` (``!format``),
|
|
484
|
+
``annotate`` (default ``True``), ``sorted`` (default
|
|
485
|
+
``False``), ``group_width`` (default ``0.8``),
|
|
486
|
+
``legend`` (``!legend``), ``figsize``, ``plot_size``.
|
|
487
|
+
|
|
488
|
+
Example
|
|
489
|
+
-------
|
|
490
|
+
.. code-block:: yaml
|
|
491
|
+
|
|
492
|
+
mayores_rural_urbano: !grouped_barh
|
|
493
|
+
out_path: out/b7_mayores.png
|
|
494
|
+
data_path: data/b7.feather
|
|
495
|
+
categories:
|
|
496
|
+
banca_online: "Usa banca online"
|
|
497
|
+
cajero: "Usa el cajero"
|
|
498
|
+
oficina: "Acude a la oficina"
|
|
499
|
+
series:
|
|
500
|
+
rural: "Rural (< 5.000 hab.)"
|
|
501
|
+
urban: "Mayores (urbano)"
|
|
502
|
+
fmt: !format {decimals: 1, units: "%"}
|
|
503
|
+
"""
|
|
504
|
+
cfg: dict[str, Any] = loader.construct_mapping( # type: ignore[assignment]
|
|
505
|
+
node, deep=True
|
|
506
|
+
)
|
|
507
|
+
cfg.pop("id")
|
|
508
|
+
cfg["out_path"] = Path(cfg["out_path"])
|
|
509
|
+
if "data_path" in cfg:
|
|
510
|
+
cfg["data_path"] = Path(cfg["data_path"])
|
|
511
|
+
return cls(**cfg)
|
|
512
|
+
|
|
513
|
+
def plot(self) -> Axes:
|
|
514
|
+
"""Render the chart and persist it to ``self.out_path``."""
|
|
515
|
+
cat_ids = list(self.categories.keys())
|
|
516
|
+
ser_ids = list(self.series.keys())
|
|
517
|
+
frame = self.data.loc[cat_ids, ser_ids].copy()
|
|
518
|
+
if self.sorted:
|
|
519
|
+
frame = frame.sort_values(by=ser_ids[0])
|
|
520
|
+
cat_ids = list(frame.index)
|
|
521
|
+
cat_labels = [self.categories[c] for c in cat_ids]
|
|
522
|
+
ser_labels = list(self.series.values())
|
|
523
|
+
|
|
524
|
+
fig_kw = dict(FIG_CONFIG)
|
|
525
|
+
if self.figsize is not None:
|
|
526
|
+
fig_kw["figsize"] = self.figsize
|
|
527
|
+
fig: Figure = plt.figure( # type: ignore[reportUnknownMemberType]
|
|
528
|
+
**fig_kw
|
|
529
|
+
)
|
|
530
|
+
ax: Axes = fig.add_subplot()
|
|
531
|
+
|
|
532
|
+
n = len(ser_ids)
|
|
533
|
+
y = np.arange(len(cat_ids))
|
|
534
|
+
height = self.group_width / n
|
|
535
|
+
for k, sid in enumerate(ser_ids):
|
|
536
|
+
# First series sits at the top of each category slot;
|
|
537
|
+
# offsets march downward so colour order reads top-to-bottom.
|
|
538
|
+
offset = (n - 1) / 2 * height - k * height
|
|
539
|
+
container: BarContainer = ax.barh( # type: ignore[reportUnknownMemberType]
|
|
540
|
+
y=y + offset,
|
|
541
|
+
width=frame[sid].to_numpy(dtype=np.float64),
|
|
542
|
+
height=height,
|
|
543
|
+
color=f"C{k}",
|
|
544
|
+
label=ser_labels[k],
|
|
545
|
+
)
|
|
546
|
+
if self.annotate:
|
|
547
|
+
_annotate_bars(fig, ax, container, self.annot_fmt)
|
|
548
|
+
|
|
549
|
+
ax.set_yticks(y) # type: ignore[reportUnknownMemberType]
|
|
550
|
+
ax.set_yticklabels( # type: ignore[reportUnknownMemberType]
|
|
551
|
+
cat_labels
|
|
552
|
+
)
|
|
553
|
+
_style_axes(ax, self.fmt, **AX_CONFIG["spines"])
|
|
554
|
+
_style_zero_baseline(ax, **AX_CONFIG["baseline"])
|
|
555
|
+
|
|
556
|
+
fig_width_px: float = fig.get_size_inches()[0] * fig.dpi
|
|
557
|
+
legend_ncol = self.legend.ncol if self.legend else None
|
|
558
|
+
ncol = (
|
|
559
|
+
legend_ncol
|
|
560
|
+
if legend_ncol is not None
|
|
561
|
+
else auto_ncol(ax, ser_labels, available_width_px=fig_width_px)
|
|
562
|
+
)
|
|
563
|
+
handles, label_strs = ax.get_legend_handles_labels()
|
|
564
|
+
fig.legend( # type: ignore[reportUnknownMemberType]
|
|
565
|
+
handles,
|
|
566
|
+
label_strs,
|
|
567
|
+
loc="outside lower center",
|
|
568
|
+
ncol=ncol,
|
|
569
|
+
)
|
|
570
|
+
|
|
571
|
+
if self.plot_size is not None:
|
|
572
|
+
adjust_figure_for_plot_size(fig, ax, self.plot_size)
|
|
573
|
+
|
|
574
|
+
fig.savefig( # type: ignore[reportUnknownMemberType]
|
|
575
|
+
self.out_path
|
|
576
|
+
)
|
|
577
|
+
plt.close(fig)
|
|
578
|
+
return ax
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
En esta carpeta se guardarán todos los *assets* del proyecto, como
|
|
2
|
-
|
|
3
|
-
- [fonts](fonts/): Fuentes (archivos *.otf).
|
|
4
|
-
- [plots.yaml](plots.yaml): Archivo de configuración general para los gráficos.
|
|
1
|
+
En esta carpeta se guardarán todos los *assets* del proyecto, como
|
|
2
|
+
|
|
3
|
+
- [fonts](fonts/): Fuentes (archivos *.otf).
|
|
4
|
+
- [plots.yaml](plots.yaml): Archivo de configuración general para los gráficos.
|
|
5
5
|
- [tesoro.mplstyle](tesoro.mplstyle): Archivo de estilos *matplotlib* para los gráficos.
|
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
# general plot config file
|
|
2
|
-
|
|
3
|
-
style:
|
|
4
|
-
font: Cabinet Grotesk
|
|
5
|
-
|
|
6
|
-
figure:
|
|
7
|
-
dpi: 500
|
|
8
|
-
layout: constrained
|
|
9
|
-
|
|
10
|
-
ax:
|
|
11
|
-
spines:
|
|
12
|
-
color: gray
|
|
13
|
-
linewidth: 1
|
|
14
|
-
baseline:
|
|
15
|
-
color: "#d9e1fc"
|
|
16
|
-
linestyle: solid
|
|
17
|
-
linewidth: 1
|
|
18
|
-
zorder: 1
|
|
19
|
-
|
|
20
|
-
type_curve:
|
|
21
|
-
last:
|
|
22
|
-
alpha: 0.2
|
|
23
|
-
color: C1
|
|
24
|
-
current:
|
|
25
|
-
alpha: 0.3
|
|
26
|
-
color: C0
|
|
27
|
-
line:
|
|
28
|
-
linewidth: 2
|
|
29
|
-
marker: D
|
|
30
|
-
color: C0
|
|
31
|
-
|
|
32
|
-
barh:
|
|
33
|
-
highlight_factor: 0.4
|
|
34
|
-
padding: 5
|
|
35
|
-
|
|
36
|
-
line:
|
|
37
|
-
ncol: 5
|
|
38
|
-
|
|
39
|
-
table:
|
|
40
|
-
style: Light Shading Accent 1
|
|
41
|
-
autofit: False
|
|
42
|
-
block_separator:
|
|
43
|
-
fill: "BFBFBF" # hex color of the inter-block band
|
|
44
|
-
height_twips: 20 # half-band height; total band = 2 * height_twips
|
|
1
|
+
# general plot config file
|
|
2
|
+
|
|
3
|
+
style:
|
|
4
|
+
font: Cabinet Grotesk
|
|
5
|
+
|
|
6
|
+
figure:
|
|
7
|
+
dpi: 500
|
|
8
|
+
layout: constrained
|
|
9
|
+
|
|
10
|
+
ax:
|
|
11
|
+
spines:
|
|
12
|
+
color: gray
|
|
13
|
+
linewidth: 1
|
|
14
|
+
baseline:
|
|
15
|
+
color: "#d9e1fc"
|
|
16
|
+
linestyle: solid
|
|
17
|
+
linewidth: 1
|
|
18
|
+
zorder: 1
|
|
19
|
+
|
|
20
|
+
type_curve:
|
|
21
|
+
last:
|
|
22
|
+
alpha: 0.2
|
|
23
|
+
color: C1
|
|
24
|
+
current:
|
|
25
|
+
alpha: 0.3
|
|
26
|
+
color: C0
|
|
27
|
+
line:
|
|
28
|
+
linewidth: 2
|
|
29
|
+
marker: D
|
|
30
|
+
color: C0
|
|
31
|
+
|
|
32
|
+
barh:
|
|
33
|
+
highlight_factor: 0.4
|
|
34
|
+
padding: 5
|
|
35
|
+
|
|
36
|
+
line:
|
|
37
|
+
ncol: 5
|
|
38
|
+
|
|
39
|
+
table:
|
|
40
|
+
style: Light Shading Accent 1
|
|
41
|
+
autofit: False
|
|
42
|
+
block_separator:
|
|
43
|
+
fill: "BFBFBF" # hex color of the inter-block band
|
|
44
|
+
height_twips: 20 # half-band height; total band = 2 * height_twips
|
{tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/assets/tesoro.mplstyle
RENAMED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
# fonts
|
|
2
|
-
font.size: 12.0
|
|
3
|
-
xtick.labelsize: 12.0
|
|
4
|
-
ytick.labelsize: 12.0
|
|
5
|
-
legend.fontsize: 12.0
|
|
6
|
-
|
|
7
|
-
# modified dashed_pattern
|
|
8
|
-
lines.dashed_pattern: 10, 5
|
|
9
|
-
|
|
10
|
-
# markers
|
|
11
|
-
lines.markersize: 8
|
|
12
|
-
|
|
13
|
-
# grids
|
|
14
|
-
grid.color: d9e1fc
|
|
15
|
-
grid.linestyle: dashed
|
|
16
|
-
grid.linewidth: 0.5
|
|
17
|
-
grid.alpha: 0.5
|
|
18
|
-
|
|
19
|
-
# color palette
|
|
20
|
-
axes.prop_cycle: cycler('color', ['001e93', 'ffbd4c', '6d6d6d', '000000', 'e6821e', '2957ff', 'fba737', '5da6ff', '00ebb2', '6d0000', '009200', 'df2871', 'd9e1fc', 'ffddab'])
|
|
21
|
-
|
|
1
|
+
# fonts
|
|
2
|
+
font.size: 12.0
|
|
3
|
+
xtick.labelsize: 12.0
|
|
4
|
+
ytick.labelsize: 12.0
|
|
5
|
+
legend.fontsize: 12.0
|
|
6
|
+
|
|
7
|
+
# modified dashed_pattern
|
|
8
|
+
lines.dashed_pattern: 10, 5
|
|
9
|
+
|
|
10
|
+
# markers
|
|
11
|
+
lines.markersize: 8
|
|
12
|
+
|
|
13
|
+
# grids
|
|
14
|
+
grid.color: d9e1fc
|
|
15
|
+
grid.linestyle: dashed
|
|
16
|
+
grid.linewidth: 0.5
|
|
17
|
+
grid.alpha: 0.5
|
|
18
|
+
|
|
19
|
+
# color palette
|
|
20
|
+
axes.prop_cycle: cycler('color', ['001e93', 'ffbd4c', '6d6d6d', '000000', 'e6821e', '2957ff', 'fba737', '5da6ff', '00ebb2', '6d0000', '009200', 'df2871', 'd9e1fc', 'ffddab'])
|
|
21
|
+
|
{tesorotools_python-0.0.42 → tesorotools_python-0.0.44}/src/tesorotools/database/__init__.py
RENAMED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Módulo de gestión de bases de datos y persistencia para tesorotools.
|
|
3
|
-
Contiene las utilidades para interactuar con bases de datos locales y remotas.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
from tesorotools.database.local import LocalDatabase, ShortcutDatabase
|
|
7
|
-
from tesorotools.database.shared import SharedDatabase, resolve_shared_root
|
|
8
|
-
|
|
9
|
-
__all__ = [
|
|
10
|
-
"LocalDatabase",
|
|
11
|
-
"SharedDatabase",
|
|
12
|
-
"ShortcutDatabase",
|
|
13
|
-
"resolve_shared_root",
|
|
14
|
-
]
|
|
1
|
+
"""
|
|
2
|
+
Módulo de gestión de bases de datos y persistencia para tesorotools.
|
|
3
|
+
Contiene las utilidades para interactuar con bases de datos locales y remotas.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from tesorotools.database.local import LocalDatabase, ShortcutDatabase
|
|
7
|
+
from tesorotools.database.shared import SharedDatabase, resolve_shared_root
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"LocalDatabase",
|
|
11
|
+
"SharedDatabase",
|
|
12
|
+
"ShortcutDatabase",
|
|
13
|
+
"resolve_shared_root",
|
|
14
|
+
]
|