pyproforma 0.3.1__tar.gz → 0.3.2__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.
- {pyproforma-0.3.1/pyproforma.egg-info → pyproforma-0.3.2}/PKG-INFO +9 -1
- {pyproforma-0.3.1 → pyproforma-0.3.2}/README.md +6 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/charts/chart_def.py +9 -2
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/charts/charts.py +114 -5
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/explorer/components.py +2 -1
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/table/excel.py +2 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2/pyproforma.egg-info}/PKG-INFO +9 -1
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma.egg-info/requires.txt +3 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproject.toml +4 -1
- {pyproforma-0.3.1 → pyproforma-0.3.2}/LICENSE +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/MANIFEST.in +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/__init__.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/chart/__init__.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/chart/chart.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/chart/renderers/__init__.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/chart/renderers/base.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/chart/renderers/matplotlib_renderer.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/charts/__init__.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/cli.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/compare/__init__.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/compare/model_comparison.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/engine/__init__.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/engine/calculation_engine.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/engine/line_item_values.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/engine/model_namespace.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/explorer/__init__.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/explorer/app.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/explorer/compare_defs.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/explorer/config.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/explorer/scenario_app.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/explorer/templates/base.html +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/explorer/templates/chart_view.html +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/explorer/templates/compare_item.html +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/explorer/templates/compare_items.html +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/explorer/templates/compare_overview.html +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/explorer/templates/index.html +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/explorer/templates/line_item.html +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/explorer/templates/table_view.html +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/explorer/templates/view.html +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/proforma_model.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/reserved_words.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/results/__init__.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/results/line_item_result.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/results/line_item_selection.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/results/line_item_stat.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/results/scalar_result.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/results/tags_namespace.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/specs/__init__.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/specs/debt_line.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/specs/fixed_line.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/specs/formula_line.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/specs/input_line.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/specs/line_item.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/specs/scalar_input_line.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/specs/scalar_line.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/table/__init__.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/table/bootstrap_html_renderer.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/table/col_widths.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/table/colors.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/table/format_value.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/table/html_renderer.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/table/table_class.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/tables/__init__.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/tables/row_types.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/tables/table_def.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma/tables/tables.py +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma.egg-info/SOURCES.txt +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma.egg-info/dependency_links.txt +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma.egg-info/entry_points.txt +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/pyproforma.egg-info/top_level.txt +0 -0
- {pyproforma-0.3.1 → pyproforma-0.3.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyproforma
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: A Python package for financial modeling and reporting
|
|
5
5
|
Author-email: Robert Hannay <rhannay@gmail.com>
|
|
6
6
|
Maintainer-email: Robert Hannay <rhannay@gmail.com>
|
|
@@ -33,6 +33,8 @@ Requires-Dist: ipython>=7.0.0; extra == "notebook"
|
|
|
33
33
|
Provides-Extra: explorer
|
|
34
34
|
Requires-Dist: flask>=2.0.0; extra == "explorer"
|
|
35
35
|
Requires-Dist: PyYAML>=6.0.0; extra == "explorer"
|
|
36
|
+
Provides-Extra: all
|
|
37
|
+
Requires-Dist: pyproforma[charts,excel,explorer,notebook,pandas]; extra == "all"
|
|
36
38
|
Provides-Extra: dev
|
|
37
39
|
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
38
40
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
@@ -230,6 +232,12 @@ model.charts.line_item("net_income", chart_type="bar").show()
|
|
|
230
232
|
model.charts.line_items(["revenue", "gross_profit", "net_income"]).show()
|
|
231
233
|
```
|
|
232
234
|
|
|
235
|
+
Compare items with different units or scales by rebasing them all to 100 at a common period:
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
model.charts.indexed_line_items(["revenue", "headcount"]).show()
|
|
239
|
+
```
|
|
240
|
+
|
|
233
241
|
Charts return a `ChartSpec` which can also render to a matplotlib `Figure`:
|
|
234
242
|
|
|
235
243
|
```python
|
|
@@ -188,6 +188,12 @@ model.charts.line_item("net_income", chart_type="bar").show()
|
|
|
188
188
|
model.charts.line_items(["revenue", "gross_profit", "net_income"]).show()
|
|
189
189
|
```
|
|
190
190
|
|
|
191
|
+
Compare items with different units or scales by rebasing them all to 100 at a common period:
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
model.charts.indexed_line_items(["revenue", "headcount"]).show()
|
|
195
|
+
```
|
|
196
|
+
|
|
191
197
|
Charts return a `ChartSpec` which can also render to a matplotlib `Figure`:
|
|
192
198
|
|
|
193
199
|
```python
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
ChartDef — declarative definition of a chart to build from a model.
|
|
3
3
|
|
|
4
4
|
Used with model.charts.build() to produce a Chart. Holds the line item names,
|
|
5
|
-
chart type, title,
|
|
6
|
-
form (IDE autocomplete, validation)
|
|
5
|
+
chart type, title, optional per-series colors, and an optional value transform
|
|
6
|
+
("indexed"). Accepts either the dataclass form (IDE autocomplete, validation)
|
|
7
|
+
or a plain dict (JSON-serializable configs).
|
|
7
8
|
"""
|
|
8
9
|
|
|
9
10
|
from dataclasses import dataclass
|
|
@@ -24,15 +25,19 @@ class ChartDef:
|
|
|
24
25
|
>>> ChartDef(names=["revenue", "expenses"])
|
|
25
26
|
>>> ChartDef(names=["revenue"], chart_type="bar", title="Revenue")
|
|
26
27
|
>>> ChartDef(names=["revenue", "expenses"], colors=["#206bc4", "#d63939"])
|
|
28
|
+
>>> ChartDef(names=["revenue", "headcount"], transform="indexed")
|
|
27
29
|
|
|
28
30
|
The dict form is equivalent:
|
|
29
31
|
>>> {"names": ["revenue"], "chart_type": "bar", "colors": ["#206bc4"]}
|
|
32
|
+
>>> {"names": ["revenue", "headcount"], "transform": "indexed"}
|
|
30
33
|
"""
|
|
31
34
|
|
|
32
35
|
names: list[str]
|
|
33
36
|
chart_type: ChartType = "line"
|
|
34
37
|
title: str | None = None
|
|
35
38
|
colors: list[str] | None = None
|
|
39
|
+
transform: str | None = None
|
|
40
|
+
base_period: int | None = None
|
|
36
41
|
|
|
37
42
|
@classmethod
|
|
38
43
|
def from_dict(cls, data: dict) -> "ChartDef":
|
|
@@ -41,4 +46,6 @@ class ChartDef:
|
|
|
41
46
|
chart_type=data.get("chart_type", "line"),
|
|
42
47
|
title=data.get("title"),
|
|
43
48
|
colors=data.get("colors"),
|
|
49
|
+
transform=data.get("transform"),
|
|
50
|
+
base_period=data.get("base_period"),
|
|
44
51
|
)
|
|
@@ -10,14 +10,17 @@ This layer knows about ProformaModel; the Chart class beneath it does not.
|
|
|
10
10
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
|
-
from typing import TYPE_CHECKING
|
|
13
|
+
from typing import TYPE_CHECKING, Literal
|
|
14
14
|
|
|
15
15
|
from pyproforma.chart.chart import Chart, ChartSeries, ChartType
|
|
16
|
+
from pyproforma.table import Format
|
|
16
17
|
|
|
17
18
|
if TYPE_CHECKING:
|
|
18
19
|
from pyproforma.charts.chart_def import ChartDef
|
|
19
20
|
from pyproforma.proforma_model import ProformaModel
|
|
20
21
|
|
|
22
|
+
Transform = Literal["indexed"]
|
|
23
|
+
|
|
21
24
|
|
|
22
25
|
class Charts:
|
|
23
26
|
"""
|
|
@@ -92,6 +95,8 @@ class Charts:
|
|
|
92
95
|
chart_type: ChartType = "line",
|
|
93
96
|
title: str | None = None,
|
|
94
97
|
value_format=None,
|
|
98
|
+
transform: Transform | None = None,
|
|
99
|
+
base_period: int | None = None,
|
|
95
100
|
) -> Chart:
|
|
96
101
|
"""
|
|
97
102
|
Build a chart with one series per line item.
|
|
@@ -104,12 +109,25 @@ class Charts:
|
|
|
104
109
|
chart_type: One of "line", "bar", "stacked_bar". Defaults to "line".
|
|
105
110
|
title: Chart title. Defaults to None (no title).
|
|
106
111
|
value_format: Override the auto-detected format for the y-axis.
|
|
112
|
+
Defaults to Format.NO_DECIMALS when transform="indexed".
|
|
113
|
+
transform: Optional value transform applied to every series before
|
|
114
|
+
charting. "indexed" rebases each series to 100 at base_period:
|
|
115
|
+
value[t] / value[base_period] * 100 — useful for comparing
|
|
116
|
+
items with different units or scales (e.g. revenue vs.
|
|
117
|
+
headcount) on a common axis. Defaults to None (raw values).
|
|
118
|
+
base_period: Reference period for transform="indexed". Defaults to
|
|
119
|
+
the model's first period. Raising if set without a transform
|
|
120
|
+
catches the likely mistake of forgetting transform="indexed".
|
|
107
121
|
|
|
108
122
|
Returns:
|
|
109
123
|
Chart ready for rendering.
|
|
110
124
|
|
|
111
125
|
Raises:
|
|
112
|
-
ValueError: If any line item doesn't exist in the model
|
|
126
|
+
ValueError: If any line item doesn't exist in the model, transform
|
|
127
|
+
is not a recognized value, base_period is set without
|
|
128
|
+
transform, base_period is not one of the model's periods, or
|
|
129
|
+
(for transform="indexed") a series' base_period value is None
|
|
130
|
+
or 0.
|
|
113
131
|
|
|
114
132
|
Examples:
|
|
115
133
|
>>> model.charts.line_items(["revenue", "expenses"]).show()
|
|
@@ -117,32 +135,114 @@ class Charts:
|
|
|
117
135
|
>>> model.charts.line_items( # noqa: E501
|
|
118
136
|
... ["revenue", "expenses"], value_format=Format.MILLIONS_M
|
|
119
137
|
... ).show()
|
|
138
|
+
>>> # Compare growth trajectories on a common scale
|
|
139
|
+
>>> model.charts.line_items(["revenue", "headcount"], transform="indexed").show()
|
|
120
140
|
"""
|
|
121
141
|
for name in names:
|
|
122
142
|
self._validate_line_item(name)
|
|
123
143
|
|
|
144
|
+
if transform is not None and transform != "indexed":
|
|
145
|
+
raise ValueError(f"Unrecognized transform {transform!r}. Valid values: 'indexed'.")
|
|
146
|
+
if base_period is not None and transform is None:
|
|
147
|
+
raise ValueError(
|
|
148
|
+
"base_period is only valid with transform='indexed'. "
|
|
149
|
+
"Did you forget to pass transform='indexed'?"
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
resolved_base_period = base_period
|
|
153
|
+
if transform == "indexed" and resolved_base_period is None:
|
|
154
|
+
resolved_base_period = self._model.periods[0]
|
|
155
|
+
|
|
124
156
|
series = []
|
|
125
157
|
for name in names:
|
|
126
158
|
result = self._model[name]
|
|
159
|
+
y_values = [result[p] for p in self._model.periods]
|
|
160
|
+
if transform == "indexed":
|
|
161
|
+
y_values = self._index_values(name, y_values, resolved_base_period)
|
|
127
162
|
series.append(
|
|
128
163
|
ChartSeries(
|
|
129
164
|
label=result.label or name,
|
|
130
165
|
x_values=list(self._model.periods),
|
|
131
|
-
y_values=
|
|
166
|
+
y_values=y_values,
|
|
132
167
|
)
|
|
133
168
|
)
|
|
134
169
|
|
|
135
170
|
if value_format is None:
|
|
136
|
-
|
|
137
|
-
|
|
171
|
+
if transform == "indexed":
|
|
172
|
+
value_format = Format.NO_DECIMALS
|
|
173
|
+
else:
|
|
174
|
+
formats = [self._model[n].value_format for n in names]
|
|
175
|
+
value_format = formats[0] if len(set(formats)) == 1 else None
|
|
176
|
+
|
|
177
|
+
y_label = f"Index (Base = 100, {resolved_base_period})" if transform == "indexed" else None
|
|
138
178
|
|
|
139
179
|
return Chart(
|
|
140
180
|
series=series,
|
|
141
181
|
chart_type=chart_type,
|
|
142
182
|
title=title,
|
|
143
183
|
value_format=value_format,
|
|
184
|
+
y_label=y_label,
|
|
144
185
|
)
|
|
145
186
|
|
|
187
|
+
def indexed_line_items(
|
|
188
|
+
self,
|
|
189
|
+
names: list[str],
|
|
190
|
+
base_period: int | None = None,
|
|
191
|
+
title: str | None = None,
|
|
192
|
+
value_format=None,
|
|
193
|
+
) -> Chart:
|
|
194
|
+
"""
|
|
195
|
+
Build a line chart with every series rebased to 100 at base_period.
|
|
196
|
+
|
|
197
|
+
Convenience wrapper for line_items(names, transform="indexed", ...).
|
|
198
|
+
Lets you compare line items with different units or scales (e.g.
|
|
199
|
+
revenue in dollars vs. headcount) on a common relative axis.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
names: List of line item names to include as series.
|
|
203
|
+
base_period: Reference period; value[t] / value[base_period] * 100.
|
|
204
|
+
Defaults to the model's first period.
|
|
205
|
+
title: Chart title. Defaults to None (no title).
|
|
206
|
+
value_format: Override the default (Format.NO_DECIMALS) y-axis format.
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
Chart ready for rendering.
|
|
210
|
+
|
|
211
|
+
Raises:
|
|
212
|
+
ValueError: If any line item doesn't exist in the model, base_period
|
|
213
|
+
is not one of the model's periods, or a series' base_period
|
|
214
|
+
value is None or 0.
|
|
215
|
+
|
|
216
|
+
Examples:
|
|
217
|
+
>>> model.charts.indexed_line_items(["revenue", "headcount"]).show()
|
|
218
|
+
>>> model.charts.indexed_line_items(["revenue"], base_period=2025).show()
|
|
219
|
+
"""
|
|
220
|
+
return self.line_items(
|
|
221
|
+
names,
|
|
222
|
+
title=title,
|
|
223
|
+
value_format=value_format,
|
|
224
|
+
transform="indexed",
|
|
225
|
+
base_period=base_period,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
def _index_values(
|
|
229
|
+
self, name: str, y_values: list[float | None], base_period: int
|
|
230
|
+
) -> list[float | None]:
|
|
231
|
+
"""Rebase a series to 100 at base_period. None stays None (a gap)."""
|
|
232
|
+
try:
|
|
233
|
+
base_index = self._model.periods.index(base_period)
|
|
234
|
+
except ValueError:
|
|
235
|
+
raise ValueError(
|
|
236
|
+
f"base_period {base_period} is not in model periods {self._model.periods}"
|
|
237
|
+
) from None
|
|
238
|
+
base_value = y_values[base_index]
|
|
239
|
+
if not base_value:
|
|
240
|
+
raise ValueError(
|
|
241
|
+
f"Cannot index '{name}': base period {base_period} value is "
|
|
242
|
+
f"{base_value!r} (must be a non-zero number)."
|
|
243
|
+
)
|
|
244
|
+
return [v / base_value * 100 if v is not None else None for v in y_values]
|
|
245
|
+
|
|
146
246
|
def build(self, template: "ChartDef | dict") -> Chart:
|
|
147
247
|
"""
|
|
148
248
|
Build a Chart from a ChartDef or equivalent dict.
|
|
@@ -155,6 +255,10 @@ class Charts:
|
|
|
155
255
|
- names (list[str]): Line item names to include as series.
|
|
156
256
|
- chart_type (str, optional): "line", "bar", or "stacked_bar". Defaults to "line".
|
|
157
257
|
- title (str, optional): Chart title.
|
|
258
|
+
- transform (str, optional): "indexed" to rebase every series to
|
|
259
|
+
100 at base_period. Defaults to None (raw values).
|
|
260
|
+
- base_period (int, optional): Reference period for
|
|
261
|
+
transform="indexed". Defaults to the model's first period.
|
|
158
262
|
|
|
159
263
|
Returns:
|
|
160
264
|
Chart ready for rendering.
|
|
@@ -162,6 +266,9 @@ class Charts:
|
|
|
162
266
|
Examples:
|
|
163
267
|
>>> model.charts.from_template(ChartDef(names=["revenue", "expenses"]))
|
|
164
268
|
>>> model.charts.from_template({"names": ["revenue"], "chart_type": "bar"})
|
|
269
|
+
>>> model.charts.from_template(
|
|
270
|
+
... {"names": ["revenue", "headcount"], "transform": "indexed"}
|
|
271
|
+
... )
|
|
165
272
|
"""
|
|
166
273
|
from pyproforma.charts.chart_def import ChartDef
|
|
167
274
|
if isinstance(template, dict):
|
|
@@ -170,6 +277,8 @@ class Charts:
|
|
|
170
277
|
names=template.names,
|
|
171
278
|
chart_type=template.chart_type,
|
|
172
279
|
title=template.title,
|
|
280
|
+
transform=template.transform,
|
|
281
|
+
base_period=template.base_period,
|
|
173
282
|
)
|
|
174
283
|
if template.colors:
|
|
175
284
|
for series, color in zip(chart_spec.series, template.colors):
|
|
@@ -20,7 +20,8 @@ class StatCard:
|
|
|
20
20
|
Args:
|
|
21
21
|
name: Line item name.
|
|
22
22
|
label: Display label. Defaults to the line item's own label.
|
|
23
|
-
aggregation: One of "min", "max", "latest", "first", "sum", "avg", "cagr".
|
|
23
|
+
aggregation: One of "min", "max", "latest", "first", "sum", "avg", "cagr".
|
|
24
|
+
Defaults to "latest".
|
|
24
25
|
value_format: Optional format override. Uses the line item's format if not set.
|
|
25
26
|
start: Optional start period for the aggregation range.
|
|
26
27
|
end: Optional end period for the aggregation range.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyproforma
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: A Python package for financial modeling and reporting
|
|
5
5
|
Author-email: Robert Hannay <rhannay@gmail.com>
|
|
6
6
|
Maintainer-email: Robert Hannay <rhannay@gmail.com>
|
|
@@ -33,6 +33,8 @@ Requires-Dist: ipython>=7.0.0; extra == "notebook"
|
|
|
33
33
|
Provides-Extra: explorer
|
|
34
34
|
Requires-Dist: flask>=2.0.0; extra == "explorer"
|
|
35
35
|
Requires-Dist: PyYAML>=6.0.0; extra == "explorer"
|
|
36
|
+
Provides-Extra: all
|
|
37
|
+
Requires-Dist: pyproforma[charts,excel,explorer,notebook,pandas]; extra == "all"
|
|
36
38
|
Provides-Extra: dev
|
|
37
39
|
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
38
40
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
@@ -230,6 +232,12 @@ model.charts.line_item("net_income", chart_type="bar").show()
|
|
|
230
232
|
model.charts.line_items(["revenue", "gross_profit", "net_income"]).show()
|
|
231
233
|
```
|
|
232
234
|
|
|
235
|
+
Compare items with different units or scales by rebasing them all to 100 at a common period:
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
model.charts.indexed_line_items(["revenue", "headcount"]).show()
|
|
239
|
+
```
|
|
240
|
+
|
|
233
241
|
Charts return a `ChartSpec` which can also render to a matplotlib `Figure`:
|
|
234
242
|
|
|
235
243
|
```python
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pyproforma"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.2"
|
|
8
8
|
description = "A Python package for financial modeling and reporting"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -55,6 +55,9 @@ explorer = [
|
|
|
55
55
|
"flask>=2.0.0",
|
|
56
56
|
"PyYAML>=6.0.0",
|
|
57
57
|
]
|
|
58
|
+
all = [
|
|
59
|
+
"pyproforma[pandas,excel,charts,notebook,explorer]",
|
|
60
|
+
]
|
|
58
61
|
dev = [
|
|
59
62
|
"pytest>=6.0",
|
|
60
63
|
"pytest-cov",
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|