tesorotools-python 0.0.27__tar.gz → 0.0.29__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.27 → tesorotools_python-0.0.29}/PKG-INFO +1 -1
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/pyproject.toml +1 -1
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/artists/line_plot.py +107 -4
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/pipeline/engine.py +26 -2
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/.gitignore +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/__init__.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/artists/__init__.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/artists/barh.md +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/artists/barh_plot.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/artists/stacked.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/artists/table.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/artists/type_curve.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/assets/README.md +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/assets/fonts/CabinetGrotesk-Black.otf +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/assets/fonts/CabinetGrotesk-Bold.otf +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/assets/fonts/CabinetGrotesk-Extrabold.otf +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/assets/fonts/CabinetGrotesk-Extralight.otf +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/assets/fonts/CabinetGrotesk-Light.otf +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/assets/fonts/CabinetGrotesk-Medium.otf +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/assets/fonts/CabinetGrotesk-Regular.otf +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/assets/fonts/CabinetGrotesk-Thin.otf +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/assets/fonts/README.md +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/assets/plots.yaml +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/assets/tesoro.mplstyle +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/convert.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/data_sources/README.md +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/data_sources/__init__.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/data_sources/debug.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/data_sources/lseg.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/database/__init__.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/database/local.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/database/push.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/dependencies/__init__.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/dependencies/node.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/dependencies/resolution.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/main.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/offsets/__init__.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/offsets/offsets.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/offsets/outliers.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/pipeline/__init__.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/pipeline/diagnose.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/pipeline/rules.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/providers/__init__.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/providers/base.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/providers/bde.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/py.typed +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/render/__init__.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/render/content/__init__.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/render/content/content.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/render/content/images.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/render/content/section.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/render/content/subtitle.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/render/content/table.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/render/content/text.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/render/content/title.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/render/report.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/utils/__init__.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/utils/config.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/utils/format.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/utils/globals.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/utils/matplotlib.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/utils/series.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/utils/shortcuts.py +0 -0
- {tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/utils/template.py +0 -0
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/artists/line_plot.py
RENAMED
|
@@ -96,6 +96,100 @@ def auto_ncol(ax: Axes, labels: list[str]) -> int:
|
|
|
96
96
|
return min(ncol, len(labels))
|
|
97
97
|
|
|
98
98
|
|
|
99
|
+
def annotate_last_values(
|
|
100
|
+
ax: Axes,
|
|
101
|
+
plot_data: pd.DataFrame,
|
|
102
|
+
*,
|
|
103
|
+
decimals: int,
|
|
104
|
+
units: str,
|
|
105
|
+
series_styles: dict[str, dict[str, Any]] | None = None,
|
|
106
|
+
annotate_color: str | None = None,
|
|
107
|
+
) -> None:
|
|
108
|
+
"""Label the last non-NaN value of each column on the right.
|
|
109
|
+
|
|
110
|
+
Colour priority (highest first): ``annotate_color``
|
|
111
|
+
(global override), ``series_styles[col]['color']``,
|
|
112
|
+
the Matplotlib line colour.
|
|
113
|
+
|
|
114
|
+
Labels are packed vertically in display space so that
|
|
115
|
+
series ending at nearly the same value do not overlap.
|
|
116
|
+
The x-axis limit is extended by the widest label so
|
|
117
|
+
the text is not clipped by the axes frame.
|
|
118
|
+
"""
|
|
119
|
+
fig = ax.get_figure()
|
|
120
|
+
if fig is None:
|
|
121
|
+
return
|
|
122
|
+
styles = series_styles or {}
|
|
123
|
+
|
|
124
|
+
lines_by_label = {str(line.get_label()): line for line in ax.lines}
|
|
125
|
+
entries: list[tuple[Any, float, str, Any]] = []
|
|
126
|
+
for col in plot_data.columns:
|
|
127
|
+
col_series: pd.Series[float] = plot_data[col].dropna()
|
|
128
|
+
if col_series.empty:
|
|
129
|
+
continue
|
|
130
|
+
last_date = col_series.index[-1]
|
|
131
|
+
last_val = float(col_series.iloc[-1])
|
|
132
|
+
if annotate_color is not None:
|
|
133
|
+
color: Any = annotate_color
|
|
134
|
+
else:
|
|
135
|
+
override = styles.get(col, {}).get("color")
|
|
136
|
+
if override is not None:
|
|
137
|
+
color = override
|
|
138
|
+
else:
|
|
139
|
+
line = lines_by_label.get(col)
|
|
140
|
+
color = line.get_color() if line is not None else "black"
|
|
141
|
+
entries.append((last_date, last_val, col, color))
|
|
142
|
+
|
|
143
|
+
if not entries:
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
fig.canvas.draw() # type: ignore[reportUnknownMemberType]
|
|
147
|
+
renderer = fig.canvas.get_renderer() # type: ignore[reportUnknownMemberType]
|
|
148
|
+
trans = ax.transData
|
|
149
|
+
|
|
150
|
+
sample = ax.text( # type: ignore[reportUnknownMemberType]
|
|
151
|
+
0, 0, "0"
|
|
152
|
+
)
|
|
153
|
+
text_height = (
|
|
154
|
+
sample.get_window_extent(renderer).height # type: ignore[reportUnknownArgumentType]
|
|
155
|
+
* 1.15
|
|
156
|
+
)
|
|
157
|
+
sample.remove()
|
|
158
|
+
|
|
159
|
+
entries.sort(key=lambda e: e[1])
|
|
160
|
+
placements: list[float] = []
|
|
161
|
+
prev_y = -float("inf")
|
|
162
|
+
for _, val, _, _ in entries:
|
|
163
|
+
y_display: float = trans.transform((0, val))[1] # type: ignore[reportUnknownArgumentType]
|
|
164
|
+
if y_display - prev_y < text_height:
|
|
165
|
+
y_display = prev_y + text_height
|
|
166
|
+
placements.append(y_display)
|
|
167
|
+
prev_y = y_display
|
|
168
|
+
|
|
169
|
+
max_label_width = 0.0
|
|
170
|
+
for (date, val, _, color), packed_y in zip(entries, placements):
|
|
171
|
+
orig_y: float = trans.transform((0, val))[1] # type: ignore[reportUnknownArgumentType]
|
|
172
|
+
dy_pts = (packed_y - orig_y) * 72.0 / fig.dpi
|
|
173
|
+
text = format_annotation(val, decimals, units)
|
|
174
|
+
t = ax.annotate( # type: ignore[reportUnknownMemberType]
|
|
175
|
+
text,
|
|
176
|
+
xy=(date, val),
|
|
177
|
+
xytext=(5, dy_pts),
|
|
178
|
+
textcoords="offset points",
|
|
179
|
+
color=color,
|
|
180
|
+
va="center",
|
|
181
|
+
ha="left",
|
|
182
|
+
)
|
|
183
|
+
width = t.get_window_extent(renderer).width # type: ignore[reportUnknownArgumentType]
|
|
184
|
+
max_label_width = max(max_label_width, width)
|
|
185
|
+
|
|
186
|
+
inv = trans.inverted()
|
|
187
|
+
x0: float = inv.transform((0, 0))[0] # type: ignore[reportUnknownArgumentType]
|
|
188
|
+
x1: float = inv.transform((max_label_width + 10, 0))[0] # type: ignore[reportUnknownArgumentType]
|
|
189
|
+
xmin, xmax = ax.get_xlim()
|
|
190
|
+
ax.set_xlim(xmin, xmax + (x1 - x0))
|
|
191
|
+
|
|
192
|
+
|
|
99
193
|
def style_spines(
|
|
100
194
|
ax: Axes,
|
|
101
195
|
decimals: int,
|
|
@@ -290,6 +384,7 @@ class LinePlot:
|
|
|
290
384
|
end_date: datetime.datetime | None = None,
|
|
291
385
|
base_100: bool = False,
|
|
292
386
|
annotate: bool = False,
|
|
387
|
+
annotate_color: str | None = None,
|
|
293
388
|
baseline: bool = False,
|
|
294
389
|
format: Format | None = None,
|
|
295
390
|
legend: Legend | None = None,
|
|
@@ -320,7 +415,8 @@ class LinePlot:
|
|
|
320
415
|
raise ValueError("series is required")
|
|
321
416
|
|
|
322
417
|
self.base_100 = base_100
|
|
323
|
-
self.annotate = annotate
|
|
418
|
+
self.annotate = annotate
|
|
419
|
+
self.annotate_color = annotate_color
|
|
324
420
|
self.format = format
|
|
325
421
|
self.start_date = start_date
|
|
326
422
|
self.end_date = end_date
|
|
@@ -379,10 +475,17 @@ class LinePlot:
|
|
|
379
475
|
else:
|
|
380
476
|
plot_data.plot(ax=ax)
|
|
381
477
|
|
|
382
|
-
if self.annotate: # not implemented yet
|
|
383
|
-
pass
|
|
384
|
-
|
|
385
478
|
assert self.format is not None
|
|
479
|
+
if self.annotate:
|
|
480
|
+
annotate_last_values(
|
|
481
|
+
ax,
|
|
482
|
+
plot_data,
|
|
483
|
+
decimals=self.format.decimals,
|
|
484
|
+
units=self.format.units,
|
|
485
|
+
series_styles=self.series_styles,
|
|
486
|
+
annotate_color=self.annotate_color,
|
|
487
|
+
)
|
|
488
|
+
|
|
386
489
|
style_spines( # maybe make this function accept a Format object
|
|
387
490
|
ax,
|
|
388
491
|
decimals=self.format.decimals,
|
|
@@ -51,6 +51,12 @@ def apply_transformations(
|
|
|
51
51
|
) -> pd.DataFrame:
|
|
52
52
|
"""Apply rules sequentially to a DataFrame.
|
|
53
53
|
|
|
54
|
+
New columns are accumulated in a dict and merged into
|
|
55
|
+
*result* only when a later rule reads from them (or at
|
|
56
|
+
the end). This avoids per-rule fragmentation of the
|
|
57
|
+
DataFrame and pandas' ``PerformanceWarning`` when many
|
|
58
|
+
rules (100+) are applied.
|
|
59
|
+
|
|
54
60
|
Parameters
|
|
55
61
|
----------
|
|
56
62
|
df
|
|
@@ -64,8 +70,10 @@ def apply_transformations(
|
|
|
64
70
|
Copy of *df* with new columns added by the rules.
|
|
65
71
|
"""
|
|
66
72
|
result = df.copy()
|
|
73
|
+
pending: dict[str, pd.Series[float]] = {}
|
|
67
74
|
for rule in rules:
|
|
68
|
-
|
|
75
|
+
available = set(result.columns) | set(pending)
|
|
76
|
+
missing = [d for d in rule.dependencies if d not in available]
|
|
69
77
|
if missing:
|
|
70
78
|
logger.warning(
|
|
71
79
|
"Skipping %s: missing dependencies %s",
|
|
@@ -73,5 +81,21 @@ def apply_transformations(
|
|
|
73
81
|
missing,
|
|
74
82
|
)
|
|
75
83
|
continue
|
|
76
|
-
|
|
84
|
+
# rule.compute() reads from result; flush pending
|
|
85
|
+
# into result if this rule needs any pending column.
|
|
86
|
+
if any(d in pending for d in rule.dependencies):
|
|
87
|
+
result = pd.concat(
|
|
88
|
+
[
|
|
89
|
+
result,
|
|
90
|
+
pd.DataFrame(pending, index=result.index),
|
|
91
|
+
],
|
|
92
|
+
axis=1,
|
|
93
|
+
)
|
|
94
|
+
pending = {}
|
|
95
|
+
pending[rule.output_name] = rule.compute(result)
|
|
96
|
+
if pending:
|
|
97
|
+
result = pd.concat(
|
|
98
|
+
[result, pd.DataFrame(pending, index=result.index)],
|
|
99
|
+
axis=1,
|
|
100
|
+
)
|
|
77
101
|
return result
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/artists/barh_plot.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/artists/type_curve.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
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/assets/fonts/README.md
RENAMED
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/assets/tesoro.mplstyle
RENAMED
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/data_sources/README.md
RENAMED
|
File without changes
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/data_sources/__init__.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/data_sources/debug.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/data_sources/lseg.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/database/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/dependencies/__init__.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/dependencies/node.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/dependencies/resolution.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/pipeline/__init__.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/pipeline/diagnose.py
RENAMED
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/providers/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/render/content/__init__.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/render/content/content.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/render/content/images.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/render/content/section.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/render/content/subtitle.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/render/content/table.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/render/content/text.py
RENAMED
|
File without changes
|
{tesorotools_python-0.0.27 → tesorotools_python-0.0.29}/src/tesorotools/render/content/title.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
|