tesorotools-python 0.0.20__tar.gz → 0.0.22__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.20 → tesorotools_python-0.0.22}/PKG-INFO +1 -1
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/pyproject.toml +1 -1
- tesorotools_python-0.0.22/tesorotools/artists/__init__.py +9 -0
- tesorotools_python-0.0.22/tesorotools/artists/barh.md +109 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/artists/barh_plot.py +169 -7
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/render/content/table.py +13 -7
- tesorotools_python-0.0.22/tesorotools/utils/__init__.py +3 -0
- tesorotools_python-0.0.22/tesorotools/utils/format.py +38 -0
- tesorotools_python-0.0.20/tesorotools/artists/__init__.py +0 -5
- tesorotools_python-0.0.20/tesorotools/utils/__init__.py +0 -1
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/.gitignore +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/__init__.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/artists/line_plot.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/artists/table.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/artists/type_curve.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/assets/README.md +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/assets/fonts/CabinetGrotesk-Black.otf +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/assets/fonts/CabinetGrotesk-Bold.otf +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/assets/fonts/CabinetGrotesk-Extrabold.otf +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/assets/fonts/CabinetGrotesk-Extralight.otf +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/assets/fonts/CabinetGrotesk-Light.otf +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/assets/fonts/CabinetGrotesk-Medium.otf +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/assets/fonts/CabinetGrotesk-Regular.otf +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/assets/fonts/CabinetGrotesk-Thin.otf +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/assets/fonts/README.md +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/assets/plots.yaml +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/assets/tesoro.mplstyle +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/convert.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/data_sources/README.md +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/data_sources/__init__.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/data_sources/debug.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/data_sources/lseg.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/database/__init__.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/database/local.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/database/push.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/dependencies/__init__.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/dependencies/functions.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/dependencies/node.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/dependencies/resolution.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/main.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/offsets/__init__.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/offsets/offsets.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/offsets/outliers.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/render/__init__.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/render/content/__init__.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/render/content/content.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/render/content/images.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/render/content/section.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/render/content/subtitle.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/render/content/text.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/render/content/title.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/render/report.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/utils/config.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/utils/globals.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/utils/matplotlib.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/utils/series.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/utils/shortcuts.py +0 -0
- {tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/utils/template.py +0 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import matplotlib.style
|
|
2
|
+
|
|
3
|
+
from tesorotools.artists.barh_plot import HorizontalBarChart
|
|
4
|
+
from tesorotools.utils.config import TemplateLoader
|
|
5
|
+
|
|
6
|
+
from ..utils.globals import STYLE_SHEET
|
|
7
|
+
|
|
8
|
+
matplotlib.style.use(STYLE_SHEET)
|
|
9
|
+
TemplateLoader.add_constructor("!barh", HorizontalBarChart.from_yaml)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Especificación de gráficos de barras horizontales
|
|
2
|
+
|
|
3
|
+
En un archivo `.yaml` los gráficos de barras horizontales se especifican con el constructor `!barh`.
|
|
4
|
+
|
|
5
|
+
## Series y bloques de series
|
|
6
|
+
|
|
7
|
+
Pueden especificarse, o bien *series*, con la entrada `series` o bien *bloques de series* con la entrada `blocks`, pero no ambas a la vez.
|
|
8
|
+
|
|
9
|
+
- Cada serie en la entrada `series` representará una barra en el gráfico final.
|
|
10
|
+
- Los *bloques de series* utilizan para controlar los colores de las barras. Las series dentro de un mismo bloque tendrán el mismo color en el gráfico final.
|
|
11
|
+
|
|
12
|
+
```yaml
|
|
13
|
+
test_plot1: !barh
|
|
14
|
+
blocks:
|
|
15
|
+
block1:
|
|
16
|
+
label: name_to_be_displayed_1
|
|
17
|
+
series:
|
|
18
|
+
series_name_1: series_name_to_be_displayed_1
|
|
19
|
+
series_name_2: series_name_to_be_displayed_2*
|
|
20
|
+
block2:
|
|
21
|
+
label: name_to_be_displayed_2
|
|
22
|
+
series:
|
|
23
|
+
series_name_3: series_name_to_be_displayed_3
|
|
24
|
+
series_name_4: series_name_to_be_displayed_4
|
|
25
|
+
|
|
26
|
+
test_plot2: !barh
|
|
27
|
+
series:
|
|
28
|
+
series_name_5: series_name_to_be_displayed_5
|
|
29
|
+
series_name_6: series_name_to_be_displayed_6
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Cómo destacar una serie
|
|
33
|
+
Si añadimos un `*` al nombre de una serie, esta será marcada como destacada en el gráfico. Concretamente, se le añadirá cierta transparencia.
|
|
34
|
+
|
|
35
|
+
## Qué muestra un gráfico de barras horizontales
|
|
36
|
+
|
|
37
|
+
Un gráfico de barras horizontales puede mostrar hasta dos conjuntos de valores asociados de forma simultánea.
|
|
38
|
+
|
|
39
|
+
- Si $(x_1,...,x_n)$ es un conjunto de valores, un gráfico de barras horizontales presenta estos valores de forma que cada uno de ellos queda asociado a una barra de longitud proporcional a su valor.
|
|
40
|
+
- Este conjunto de valores está representado por una *serie* de *pandas* y suele recibir, internamente, el nombre de `value_series`.
|
|
41
|
+
- Opcionalmente, podemos querer acompañar cada valor (o solo algunos) $x_i$ con otro valor auxiliar $y_i$ para añadir contexto.
|
|
42
|
+
- Estos valores auxiliares $y_i$ se monstrarán con el formato que decidamos, entre paréntesis, al lado del nombre asociado al valor $x_i$, en el eje de ordenadas.
|
|
43
|
+
- Este conjunto de valores está respresentado por una *serie* de *pandas* y suele recibir, internamente, el nombre de `axis_series`.
|
|
44
|
+
|
|
45
|
+
### Formato del eje *x*
|
|
46
|
+
Para especificar el número de decimales, la escala y las unidades que queremos que se muestren para los valores presentados en forma de barras (nótese que esta configración afectará al aspecto del eje *x*) podemos usar la entrada `format`.
|
|
47
|
+
|
|
48
|
+
```yaml
|
|
49
|
+
nombre_del_grafico: !barh
|
|
50
|
+
# other stuff
|
|
51
|
+
format:
|
|
52
|
+
decimals: 0
|
|
53
|
+
units: "%"
|
|
54
|
+
scale: 100
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Formato de los valores representados en el eje *y*
|
|
59
|
+
Para especificar el número de decimales, la escala y las unidades que queremos que se muestren para los valores auxiliares presentados numéricamente en el eje *y*, podemos usar la entrada `axis_format`.
|
|
60
|
+
|
|
61
|
+
```yaml
|
|
62
|
+
nombre_del_grafico: !barh
|
|
63
|
+
# other stuff
|
|
64
|
+
axis_format:
|
|
65
|
+
decimals: 2
|
|
66
|
+
units: €
|
|
67
|
+
scale: 1
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Formato de las anotaciones sobre las barras
|
|
71
|
+
En ocasiones es dificil ver el valor exacto asociado a cada barra, sobre todo cuando no hay muchas marcas en el eje $x$, la rejilla es demasiado clara, o la barra queda muy lejos del eje.
|
|
72
|
+
|
|
73
|
+
Para solventar este problema, podemos, opcionalmente, añadir una anotación al lado de cada una de las barras que indique numéricamente, y con el formato que queramos, el valor asociado a cada una de ellas. Para ello, podemos usar la entrada `annot_format`.
|
|
74
|
+
|
|
75
|
+
```yaml
|
|
76
|
+
nombre_del_grafico: !barh
|
|
77
|
+
# other stuff
|
|
78
|
+
annot_format:
|
|
79
|
+
decimals: 2
|
|
80
|
+
units: €
|
|
81
|
+
scale: 1
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Ordenar las barras por longitud
|
|
85
|
+
|
|
86
|
+
Usualmente querremos que las barras del gráfico se muestren ordenadas, para ellos podemos usar la entrada `sorted`, que se tomará como `True` por defecto.
|
|
87
|
+
|
|
88
|
+
- *TO DO*: Quizá aquí sería interesante añadir una entrada `ascending` que indique cómo ordenar las barras en caso de que la entrada `sorted` esté a `True`, y que será ignorada en caso de que `sorted` esté a `False`.
|
|
89
|
+
|
|
90
|
+
```yaml
|
|
91
|
+
nombre_del_grafico: !barh
|
|
92
|
+
# other stuff
|
|
93
|
+
sorted: True # default
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Gráficos de series a partir de *dataframes* con formato *flash*
|
|
98
|
+
|
|
99
|
+
### Entrada *flash*
|
|
100
|
+
|
|
101
|
+
```yaml
|
|
102
|
+
nombre_del_gráfico: !barh
|
|
103
|
+
# other stuff
|
|
104
|
+
flash:
|
|
105
|
+
date: # if None takes last date
|
|
106
|
+
deviations: True
|
|
107
|
+
offset: ftd # friday to date
|
|
108
|
+
difference: relative # relative/absolute difference
|
|
109
|
+
```
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
+
# Añadir anotaciones de tipos, el objetivo sería eliminar todos los diccionarios con anotaciones del tipo dict[str, Any] que dicen más bien poco
|
|
2
|
+
# Tener cuidado para que nada de lo que está hecho con esta librería deje de funcionar
|
|
3
|
+
|
|
1
4
|
from enum import Enum
|
|
2
5
|
from pathlib import Path
|
|
3
|
-
from typing import Any
|
|
6
|
+
from typing import Any, Self
|
|
4
7
|
|
|
5
8
|
import matplotlib.pyplot as plt
|
|
6
9
|
import pandas as pd
|
|
7
10
|
from matplotlib.container import BarContainer
|
|
8
11
|
from matplotlib.ticker import FuncFormatter
|
|
12
|
+
from yaml import MappingNode
|
|
9
13
|
|
|
10
14
|
from tesorotools.offsets.offsets import Difference, FloatingOffset, Stat
|
|
11
15
|
from tesorotools.offsets.outliers import flag_outliers
|
|
12
|
-
from tesorotools.utils.
|
|
16
|
+
from tesorotools.utils.config import TemplateLoader
|
|
13
17
|
from tesorotools.utils.matplotlib import (
|
|
14
18
|
PLOT_CONFIG,
|
|
15
19
|
format_annotation,
|
|
@@ -24,6 +28,8 @@ load_fonts()
|
|
|
24
28
|
|
|
25
29
|
|
|
26
30
|
class Column(Enum):
|
|
31
|
+
"""Enumerado usado para dar nombres normalizados a las series que se utilizan internamente en este módulo."""
|
|
32
|
+
|
|
27
33
|
VALUE = "value"
|
|
28
34
|
AXIS = "axis"
|
|
29
35
|
DEVIATION = "deviation"
|
|
@@ -39,6 +45,16 @@ def _style_spines(
|
|
|
39
45
|
color: str,
|
|
40
46
|
linewidth: str,
|
|
41
47
|
):
|
|
48
|
+
"""Estiliza los ejes acorde con la configuración
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
ax : plt.Axes
|
|
53
|
+
decimals : int
|
|
54
|
+
units : str
|
|
55
|
+
color : str
|
|
56
|
+
linewidth : str
|
|
57
|
+
"""
|
|
42
58
|
ax.grid(visible=True, axis="x")
|
|
43
59
|
for spine in ax.spines.values():
|
|
44
60
|
spine.set_color(color)
|
|
@@ -69,6 +85,25 @@ def _style_baseline(ax: plt.Axes, **baseline_config):
|
|
|
69
85
|
def _collect_series(
|
|
70
86
|
blocks: dict[str, Any] | None, series: dict[str, str] | None
|
|
71
87
|
) -> dict[str, str]:
|
|
88
|
+
"""Devuelve la unión de los diccionarios de series contenidos en los bloques. Si el gráfico no contiene bloques de series, simplemente devuelve su diccionario de series. Si contiene series y bloques (cosa que no debería ocurrir), simplemente ignora los bloques de series y devuelve las series.
|
|
89
|
+
|
|
90
|
+
Parameters
|
|
91
|
+
----------
|
|
92
|
+
blocks : dict[str, Any] | None
|
|
93
|
+
Diccionario de bloques del gráfico
|
|
94
|
+
series : dict[str, str] | None
|
|
95
|
+
Diccionario de series del gráfico
|
|
96
|
+
|
|
97
|
+
Returns
|
|
98
|
+
-------
|
|
99
|
+
dict[str, str]
|
|
100
|
+
Diccionario de series unificado
|
|
101
|
+
|
|
102
|
+
Raises
|
|
103
|
+
------
|
|
104
|
+
ValueError
|
|
105
|
+
Si en el gráfico no se especifican ni series ni bloques de series, se lanza una excepción.
|
|
106
|
+
"""
|
|
72
107
|
if series is None and blocks is None:
|
|
73
108
|
raise ValueError("blocks and series cannot be both missing")
|
|
74
109
|
if series is None and blocks is not None:
|
|
@@ -78,6 +113,18 @@ def _collect_series(
|
|
|
78
113
|
|
|
79
114
|
|
|
80
115
|
def _collect_block_series(blocks: dict[str, Any]) -> dict[str, str]:
|
|
116
|
+
"""Devuelve la unión de los diccionarios de series contenidos en los bloques
|
|
117
|
+
|
|
118
|
+
Parameters
|
|
119
|
+
----------
|
|
120
|
+
blocks : dict[str, Any]
|
|
121
|
+
Diccionario de bloques del gráfico
|
|
122
|
+
|
|
123
|
+
Returns
|
|
124
|
+
-------
|
|
125
|
+
dict[str, str]
|
|
126
|
+
Diccionario de series unificado
|
|
127
|
+
"""
|
|
81
128
|
series = {}
|
|
82
129
|
for _, block_cfg in blocks.items():
|
|
83
130
|
series = series | block_cfg["series"]
|
|
@@ -87,6 +134,24 @@ def _collect_block_series(blocks: dict[str, Any]) -> dict[str, str]:
|
|
|
87
134
|
def _infer_colors(
|
|
88
135
|
value_series: pd.Series, blocks: dict[str, Any] | None
|
|
89
136
|
) -> pd.Series:
|
|
137
|
+
"""Si en el gráfico no se especifican bloques de series, se asignará el primer color del tema a los valores positivos y el segundo a los colores negativos.
|
|
138
|
+
|
|
139
|
+
Si, por el contrario, se especifican bloques de series, se asignará un color a cada bloque, con independencia del signo de los valores.
|
|
140
|
+
|
|
141
|
+
Parameters
|
|
142
|
+
----------
|
|
143
|
+
value_series : pd.Series
|
|
144
|
+
Serie a representar en forma de barras
|
|
145
|
+
blocks : dict[str, Any] | None
|
|
146
|
+
Especificación de los bloques de series del gráfico, o None, en caso de no existir.
|
|
147
|
+
|
|
148
|
+
Returns
|
|
149
|
+
-------
|
|
150
|
+
pd.Series
|
|
151
|
+
Serie de strings del mismo tamaño de la serie a representar, representando cada string un color del tema "C0", "C1", etc
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
# create a series with the same index as the value series and a normalized name
|
|
90
155
|
color_series: pd.Series = pd.Series(
|
|
91
156
|
index=value_series.index, name=Column.COLOR.value, dtype=str
|
|
92
157
|
)
|
|
@@ -103,6 +168,21 @@ def _infer_colors(
|
|
|
103
168
|
def _highlight_series(
|
|
104
169
|
alias: dict[str, str], value_series: pd.Series
|
|
105
170
|
) -> pd.Series:
|
|
171
|
+
"""Asignación de la transparencia de cada una de las barras a representar. Si los nombres de las series no contienen un `*` final se le asigna una opacidad de 1, en caso contrario, se asigna una opacidad específica definida en la variable `BARH_CONFIG["highlight_factor"]`
|
|
172
|
+
|
|
173
|
+
Parameters
|
|
174
|
+
----------
|
|
175
|
+
alias : dict[str, str]
|
|
176
|
+
Diccionario de renombrado de la serie
|
|
177
|
+
value_series : pd.Series
|
|
178
|
+
Serie de valores a representar como barras
|
|
179
|
+
|
|
180
|
+
Returns
|
|
181
|
+
-------
|
|
182
|
+
pd.Series
|
|
183
|
+
Serie con el mismo tamaño que la serie de valores a representar. Contiene las transparencias asociadas a las barras de cada uno de los valores.
|
|
184
|
+
"""
|
|
185
|
+
# create a series with the same index as the value series and a normalized name
|
|
106
186
|
alpha_series: pd.Series = pd.Series(
|
|
107
187
|
index=value_series.index, name=Column.ALPHA.value
|
|
108
188
|
)
|
|
@@ -118,15 +198,38 @@ def _format_yaxis(
|
|
|
118
198
|
value_series: pd.Series,
|
|
119
199
|
axis_series: pd.Series | None,
|
|
120
200
|
) -> pd.Series:
|
|
121
|
-
|
|
201
|
+
"""Formatea el conjunto de valores auxiliares a ser representado, entre paréntesis, en el eje y.
|
|
202
|
+
|
|
203
|
+
Parameters
|
|
204
|
+
----------
|
|
205
|
+
alias : dict[str, str]
|
|
206
|
+
Diccionario de renombrado del índice de la serie de valores
|
|
207
|
+
axis_format : dict[str, Any]
|
|
208
|
+
Especificación del formateo a ser aplicado
|
|
209
|
+
value_series : pd.Series
|
|
210
|
+
Serie de valores a ser representados como barras (realmente no estoy muy seguro de que la necesitemos)
|
|
211
|
+
axis_series : pd.Series | None
|
|
212
|
+
Serie de valores a ser representados numéricamente en el eje y
|
|
213
|
+
|
|
214
|
+
Returns
|
|
215
|
+
-------
|
|
216
|
+
pd.Series
|
|
217
|
+
Serie de valores a ser representados como barras, con el índice renombrado de tal forma que incluya en sí mismo los valores a ser representados numéricamente en el eje y
|
|
218
|
+
"""
|
|
219
|
+
|
|
220
|
+
# remove the `*` from the series display names (this behabiour may change in the future sin may be interesting to be able to display names with the `*` character)
|
|
122
221
|
renamer = {_: label.replace("*", "") for _, label in alias.items()}
|
|
123
222
|
value_series = value_series.rename(renamer)
|
|
124
223
|
if axis_format is not None:
|
|
125
224
|
decimals: int = axis_format["decimals"]
|
|
126
225
|
units: str = axis_format["units"]
|
|
226
|
+
|
|
227
|
+
# rename the index and format the values as specified as strings
|
|
127
228
|
axis_series: pd.Series = axis_series.rename(renamer).apply(
|
|
128
229
|
lambda x: format_annotation(x, decimals, units)
|
|
129
230
|
)
|
|
231
|
+
|
|
232
|
+
# add the formatted value next to the series name, parenthesized
|
|
130
233
|
value_series = value_series.rename(
|
|
131
234
|
lambda x: f"{x} ({axis_series.loc[x]})"
|
|
132
235
|
)
|
|
@@ -141,14 +244,30 @@ def _annotate(
|
|
|
141
244
|
decimals: int,
|
|
142
245
|
units: str,
|
|
143
246
|
):
|
|
144
|
-
|
|
247
|
+
"""Añade las etiquetas a las barras
|
|
248
|
+
|
|
249
|
+
Parameters
|
|
250
|
+
----------
|
|
251
|
+
fig : plt.Figure
|
|
252
|
+
Fugura de matplotlib
|
|
253
|
+
ax : plt.Axes
|
|
254
|
+
Axes asociado a la figura de matplotlib
|
|
255
|
+
bar_container : BarContainer
|
|
256
|
+
Contenedor de barras
|
|
257
|
+
decimals : int
|
|
258
|
+
Decimales a mostrar en las anotaciones
|
|
259
|
+
units : str
|
|
260
|
+
Unidades a mostrar en las anotaciones
|
|
261
|
+
"""
|
|
262
|
+
|
|
263
|
+
# annotate (just use the default matplotlib capabilities)
|
|
145
264
|
labels = ax.bar_label(
|
|
146
265
|
container=bar_container,
|
|
147
266
|
fmt=lambda x: format_annotation(x, decimals, units),
|
|
148
267
|
padding=BARH_CONFIG["padding"],
|
|
149
268
|
)
|
|
150
269
|
|
|
151
|
-
# rescale
|
|
270
|
+
# rescale (kind of hacky, explanation pending)
|
|
152
271
|
fig.canvas.draw_idle()
|
|
153
272
|
for label in labels:
|
|
154
273
|
bbox = label.get_window_extent()
|
|
@@ -176,18 +295,21 @@ def _plot_barh_chart(
|
|
|
176
295
|
# format y axis ticker labels
|
|
177
296
|
axis_series = standard_dict[Column.AXIS]
|
|
178
297
|
value_series = _format_yaxis(alias, axis_format, value_series, axis_series)
|
|
298
|
+
|
|
299
|
+
# change the index of the other auxiliary series accordingly
|
|
179
300
|
color_series.index = value_series.index
|
|
180
301
|
alpha_series.index = value_series.index
|
|
181
302
|
|
|
303
|
+
# pack all the necessary data in a single dataframe for convenience
|
|
182
304
|
data: pd.DataFrame = pd.concat(
|
|
183
305
|
[value_series, color_series, alpha_series], axis=1
|
|
184
306
|
)
|
|
185
307
|
|
|
186
|
-
# sort if required
|
|
308
|
+
# sort if required (here should be added the ascending/descending feature)
|
|
187
309
|
if sorted:
|
|
188
310
|
data = data.sort_values(by=Column.VALUE.value)
|
|
189
311
|
|
|
190
|
-
|
|
312
|
+
### actually plot code
|
|
191
313
|
fig = plt.figure(**FIG_CONFIG)
|
|
192
314
|
ax = fig.add_subplot()
|
|
193
315
|
|
|
@@ -308,3 +430,43 @@ def plot_barh_charts_from_flash(
|
|
|
308
430
|
)
|
|
309
431
|
out_file = out_path / f"{name}.png"
|
|
310
432
|
_plot_barh_chart(out_file, standard_dict, alias, **config)
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
# estaría bien crear un tipo específico para las entradas en el yaml de tipo formato
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
class HorizontalBarChart:
|
|
439
|
+
|
|
440
|
+
def __init__(self, alias: dict[str, str], **kwargs):
|
|
441
|
+
# el producto de esto deben ser dos series indexadas con el mismo índice
|
|
442
|
+
self._alias = alias
|
|
443
|
+
|
|
444
|
+
@classmethod
|
|
445
|
+
def from_yaml(cls, loader: TemplateLoader, node: MappingNode) -> Self:
|
|
446
|
+
plot_cfg: dict[str, Any] = loader.construct_mapping(node, deep=True)
|
|
447
|
+
plot_name: str = plot_cfg.pop("id")
|
|
448
|
+
blocks: dict[str, Any] | None = plot_cfg.get("blocks", None)
|
|
449
|
+
series: dict[str, str] | None = plot_cfg.get("series", None)
|
|
450
|
+
axis_format: dict[str, Any] | None = plot_cfg.get("axis_format", None)
|
|
451
|
+
|
|
452
|
+
# maybe here it's not the place to get the aliases, maybe the constructor is a better place
|
|
453
|
+
alias: dict[str, str] = _collect_series(blocks, series)
|
|
454
|
+
|
|
455
|
+
return cls(
|
|
456
|
+
alias,
|
|
457
|
+
**plot_cfg,
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
# this may be an "instance" of a protocol, just check what we did in the render package
|
|
461
|
+
def render(self, out: Path | None = None):
|
|
462
|
+
"""Export the plot as an image to the path specified in the argument, or, if missing, to the path specified in the template
|
|
463
|
+
|
|
464
|
+
Parameters
|
|
465
|
+
----------
|
|
466
|
+
out : Path | None, optional
|
|
467
|
+
Path in which the image will be saved, by default None. If None, the image will be saved in the previously specified path in the yaml template.
|
|
468
|
+
"""
|
|
469
|
+
pass
|
|
470
|
+
|
|
471
|
+
def __repr__(self):
|
|
472
|
+
return str(self._alias)
|
|
@@ -115,7 +115,7 @@ def _style_index_names(cell: TableCell):
|
|
|
115
115
|
|
|
116
116
|
|
|
117
117
|
def _fill_index_names(
|
|
118
|
-
index: pd.Index | pd.MultiIndex, table_docx: TableDocx, horizontal: bool
|
|
118
|
+
index: pd.Index | pd.MultiIndex, table_docx: TableDocx, horizontal: bool, index_name: bool
|
|
119
119
|
):
|
|
120
120
|
start_row: int = 2 if horizontal else 1
|
|
121
121
|
|
|
@@ -125,9 +125,13 @@ def _fill_index_names(
|
|
|
125
125
|
else index.get_level_values(level=1)
|
|
126
126
|
)
|
|
127
127
|
|
|
128
|
+
if index_name:
|
|
129
|
+
cell: TableCell = table_docx.cell(start_row-1, 0)
|
|
130
|
+
cell.text = index.name
|
|
131
|
+
|
|
132
|
+
|
|
128
133
|
for idx, name in enumerate(index_names, start=start_row):
|
|
129
134
|
cell: TableCell = table_docx.cell(idx, 0)
|
|
130
|
-
print(name)
|
|
131
135
|
cell.text = name
|
|
132
136
|
_style_index_names(cell)
|
|
133
137
|
|
|
@@ -233,7 +237,7 @@ def render_table(
|
|
|
233
237
|
_style_table(table_docx)
|
|
234
238
|
_fill_column_names(table, table_docx, horizontal)
|
|
235
239
|
_fill_index_names(
|
|
236
|
-
index=table.index, table_docx=table_docx, horizontal=horizontal
|
|
240
|
+
index=table.index, table_docx=table_docx, horizontal=horizontal, **kwargs
|
|
237
241
|
)
|
|
238
242
|
if block_sep:
|
|
239
243
|
_separate_blocks(table.index, table_docx)
|
|
@@ -253,6 +257,7 @@ class Table:
|
|
|
253
257
|
block_sep: bool = False,
|
|
254
258
|
title: str | None = None,
|
|
255
259
|
columns: list[str] | None = None,
|
|
260
|
+
index_name: bool = False
|
|
256
261
|
):
|
|
257
262
|
if (
|
|
258
263
|
(data_file is None)
|
|
@@ -277,6 +282,7 @@ class Table:
|
|
|
277
282
|
|
|
278
283
|
self._title: str | None = title
|
|
279
284
|
self._block_sep: bool = block_sep
|
|
285
|
+
self._index_name: bool = index_name
|
|
280
286
|
|
|
281
287
|
@classmethod
|
|
282
288
|
def from_yaml(cls, loader: TemplateLoader, node: MappingNode) -> Self:
|
|
@@ -304,10 +310,9 @@ class Table:
|
|
|
304
310
|
def render(self, document: Document) -> Document:
|
|
305
311
|
heading = document.add_heading(self._title, level=2)
|
|
306
312
|
heading.alignment = CENTER
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
print(self._data.index)
|
|
313
|
+
|
|
314
|
+
if len(heading.runs) > 0:
|
|
315
|
+
heading.runs[0].font.size = Pt(10)
|
|
311
316
|
|
|
312
317
|
render_table(
|
|
313
318
|
self._data,
|
|
@@ -315,5 +320,6 @@ class Table:
|
|
|
315
320
|
self._shade,
|
|
316
321
|
document,
|
|
317
322
|
block_sep=self._block_sep,
|
|
323
|
+
index_name=self._index_name
|
|
318
324
|
)
|
|
319
325
|
return document
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
from tesorotools.utils.matplotlib import format_annotation
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def format_table(
|
|
9
|
+
table: pd.DataFrame, format_dict: dict[str, dict[str, Any]]
|
|
10
|
+
) -> pd.DataFrame:
|
|
11
|
+
"""Formats a pandas dataframe to get it ready for being rendered into a .docx file.
|
|
12
|
+
|
|
13
|
+
Parameters
|
|
14
|
+
----------
|
|
15
|
+
table : pd.DataFrame
|
|
16
|
+
format_dict : dict[str, dict[str, Any]]
|
|
17
|
+
Each key in the dictionary is a column in the dataframe, each value is a dictionary with 3 possible entries: "scale" (integer, default=1), "decimals" (integer, default=0), "units" (string, default="").
|
|
18
|
+
|
|
19
|
+
Returns
|
|
20
|
+
-------
|
|
21
|
+
pd.DataFrame
|
|
22
|
+
The formatted dataframe
|
|
23
|
+
"""
|
|
24
|
+
for column, fmt in format_dict.items():
|
|
25
|
+
if pd.api.types.is_numeric_dtype(table[column]):
|
|
26
|
+
scale = fmt.get("scale", 1)
|
|
27
|
+
table[column] = table[column] / scale
|
|
28
|
+
table[column] = table[column].apply(
|
|
29
|
+
lambda x: format_annotation(
|
|
30
|
+
value=x,
|
|
31
|
+
decimals=fmt.get("decimals", 0),
|
|
32
|
+
units=fmt.get("units", ""),
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
for column in table.columns:
|
|
37
|
+
table[column] = table[column].astype(str)
|
|
38
|
+
return table
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from tesorotools.utils.globals import SYSTEM
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/data_sources/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/dependencies/__init__.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/dependencies/functions.py
RENAMED
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/dependencies/resolution.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/render/content/__init__.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/render/content/content.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/render/content/images.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/render/content/section.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.20 → tesorotools_python-0.0.22}/tesorotools/render/content/subtitle.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|