openenergyid 0.1.16__py2.py3-none-any.whl → 0.1.18__py2.py3-none-any.whl
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.
Potentially problematic release.
This version of openenergyid might be problematic. Click here for more details.
- openenergyid/__init__.py +1 -1
- openenergyid/dyntar/const.py +4 -0
- openenergyid/dyntar/main.py +134 -13
- openenergyid/dyntar/models.py +12 -5
- openenergyid/models.py +3 -15
- {openenergyid-0.1.16.dist-info → openenergyid-0.1.18.dist-info}/METADATA +1 -1
- {openenergyid-0.1.16.dist-info → openenergyid-0.1.18.dist-info}/RECORD +9 -9
- {openenergyid-0.1.16.dist-info → openenergyid-0.1.18.dist-info}/WHEEL +0 -0
- {openenergyid-0.1.16.dist-info → openenergyid-0.1.18.dist-info}/licenses/LICENSE +0 -0
openenergyid/__init__.py
CHANGED
openenergyid/dyntar/const.py
CHANGED
|
@@ -16,3 +16,7 @@ SPP_WEIGHTED_PRICE_EXPORTED = "spp_weighted_price_exported"
|
|
|
16
16
|
HEATMAP_DELIVERED = "heatmap_delivered"
|
|
17
17
|
HEATMAP_EXPORTED = "heatmap_exported"
|
|
18
18
|
HEATMAP_TOTAL = "heatmap_total"
|
|
19
|
+
|
|
20
|
+
HEATMAP_DELIVERED_DESCRIPTION = "heatmap_delivered_description"
|
|
21
|
+
HEATMAP_EXPORTED_DESCRIPTION = "heatmap_exported_description"
|
|
22
|
+
HEATMAP_TOTAL_DESCRIPTION = "heatmap_total_description"
|
openenergyid/dyntar/main.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Main module of the DynTar package."""
|
|
2
2
|
|
|
3
|
+
import numpy as np
|
|
3
4
|
import pandas as pd
|
|
4
5
|
|
|
5
6
|
from openenergyid.const import (
|
|
@@ -25,6 +26,9 @@ from .const import (
|
|
|
25
26
|
HEATMAP_DELIVERED,
|
|
26
27
|
HEATMAP_EXPORTED,
|
|
27
28
|
HEATMAP_TOTAL,
|
|
29
|
+
HEATMAP_DELIVERED_DESCRIPTION,
|
|
30
|
+
HEATMAP_EXPORTED_DESCRIPTION,
|
|
31
|
+
HEATMAP_TOTAL_DESCRIPTION,
|
|
28
32
|
)
|
|
29
33
|
|
|
30
34
|
|
|
@@ -122,25 +126,31 @@ def extend_dataframe_with_heatmap(df: pd.DataFrame, inplace: bool = False) -> pd
|
|
|
122
126
|
if not inplace:
|
|
123
127
|
df = df.copy()
|
|
124
128
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
129
|
+
normalized_energy_delta_delivered = (
|
|
130
|
+
df[ELECTRICITY_DELIVERED_SMR2] - df[ELECTRICITY_DELIVERED_SMR3]
|
|
131
|
+
) / df[ELECTRICITY_DELIVERED_SMR2]
|
|
132
|
+
normalized_price_delta_delivered = (
|
|
133
|
+
df[RLP_WEIGHTED_PRICE_DELIVERED] - df[PRICE_ELECTRICITY_DELIVERED]
|
|
134
|
+
) / df[RLP_WEIGHTED_PRICE_DELIVERED]
|
|
135
|
+
heatmap_score_delivered = normalized_energy_delta_delivered * normalized_price_delta_delivered
|
|
136
|
+
|
|
137
|
+
normalized_energy_delta_exported = (
|
|
138
|
+
df[ELECTRICITY_EXPORTED_SMR2] - df[ELECTRICITY_EXPORTED_SMR3]
|
|
139
|
+
) / df[ELECTRICITY_EXPORTED_SMR2]
|
|
140
|
+
normalized_energy_delta_exported = normalized_energy_delta_exported.replace(
|
|
141
|
+
[np.inf, -np.inf], np.nan
|
|
136
142
|
)
|
|
143
|
+
normalized_price_delta_exported = (
|
|
144
|
+
df[SPP_WEIGHTED_PRICE_EXPORTED] - df[PRICE_ELECTRICITY_EXPORTED]
|
|
145
|
+
) / df[SPP_WEIGHTED_PRICE_EXPORTED]
|
|
146
|
+
heatmap_score_exported = normalized_energy_delta_exported * normalized_price_delta_exported
|
|
147
|
+
|
|
137
148
|
heatmap_score_delivered.fillna(0, inplace=True)
|
|
138
149
|
heatmap_score_exported.fillna(0, inplace=True)
|
|
139
150
|
|
|
140
151
|
# Invert scores so that positive values indicate a positive impact
|
|
141
152
|
heatmap_score_delivered = -heatmap_score_delivered
|
|
142
|
-
|
|
143
|
-
heatmap_score_combined = heatmap_score_delivered - heatmap_score_exported
|
|
153
|
+
heatmap_score_combined = heatmap_score_delivered + heatmap_score_exported
|
|
144
154
|
|
|
145
155
|
df[HEATMAP_DELIVERED] = heatmap_score_delivered
|
|
146
156
|
df[HEATMAP_EXPORTED] = heatmap_score_exported
|
|
@@ -151,6 +161,116 @@ def extend_dataframe_with_heatmap(df: pd.DataFrame, inplace: bool = False) -> pd
|
|
|
151
161
|
return None
|
|
152
162
|
|
|
153
163
|
|
|
164
|
+
def extend_dataframe_with_heatmap_description(
|
|
165
|
+
df: pd.DataFrame, inplace: bool = False
|
|
166
|
+
) -> pd.DataFrame | None:
|
|
167
|
+
"""Extend a DataFrame with the heatmap description columns."""
|
|
168
|
+
if not inplace:
|
|
169
|
+
df = df.copy()
|
|
170
|
+
|
|
171
|
+
# Delivered
|
|
172
|
+
|
|
173
|
+
# Where Heatmap is 0, we put a desription of 0 (No impact)
|
|
174
|
+
df[HEATMAP_DELIVERED_DESCRIPTION] = df[HEATMAP_DELIVERED].apply(
|
|
175
|
+
lambda x: 0 if x == 0 else float("NaN")
|
|
176
|
+
)
|
|
177
|
+
# When the energy delta is positive, and the price delta is positive, we put a description of 1 (high consumption, high price)
|
|
178
|
+
df[HEATMAP_DELIVERED_DESCRIPTION] = df.apply(
|
|
179
|
+
lambda x: 1
|
|
180
|
+
if x[PRICE_ELECTRICITY_DELIVERED] > x[RLP_WEIGHTED_PRICE_DELIVERED]
|
|
181
|
+
and x[ELECTRICITY_DELIVERED_SMR3] > x[ELECTRICITY_DELIVERED_SMR2]
|
|
182
|
+
else x[HEATMAP_DELIVERED_DESCRIPTION],
|
|
183
|
+
axis=1,
|
|
184
|
+
)
|
|
185
|
+
# When the energy delta is negative, and the price delta is positive, we put a description of 2 (low consumption, high price)
|
|
186
|
+
df[HEATMAP_DELIVERED_DESCRIPTION] = df.apply(
|
|
187
|
+
lambda x: 2
|
|
188
|
+
if x[PRICE_ELECTRICITY_DELIVERED] > x[RLP_WEIGHTED_PRICE_DELIVERED]
|
|
189
|
+
and x[ELECTRICITY_DELIVERED_SMR3] < x[ELECTRICITY_DELIVERED_SMR2]
|
|
190
|
+
else x[HEATMAP_DELIVERED_DESCRIPTION],
|
|
191
|
+
axis=1,
|
|
192
|
+
)
|
|
193
|
+
# When the energy delta is positive, and the price delta is negative, we put a description of 3 (high consumption, low price)
|
|
194
|
+
df[HEATMAP_DELIVERED_DESCRIPTION] = df.apply(
|
|
195
|
+
lambda x: 3
|
|
196
|
+
if x[PRICE_ELECTRICITY_DELIVERED] < x[RLP_WEIGHTED_PRICE_DELIVERED]
|
|
197
|
+
and x[ELECTRICITY_DELIVERED_SMR3] > x[ELECTRICITY_DELIVERED_SMR2]
|
|
198
|
+
else x[HEATMAP_DELIVERED_DESCRIPTION],
|
|
199
|
+
axis=1,
|
|
200
|
+
)
|
|
201
|
+
# When the energy delta is negative, and the price delta is negative, we put a description of 4 (low consumption, low price)
|
|
202
|
+
df[HEATMAP_DELIVERED_DESCRIPTION] = df.apply(
|
|
203
|
+
lambda x: 4
|
|
204
|
+
if x[PRICE_ELECTRICITY_DELIVERED] < x[RLP_WEIGHTED_PRICE_DELIVERED]
|
|
205
|
+
and x[ELECTRICITY_DELIVERED_SMR3] < x[ELECTRICITY_DELIVERED_SMR2]
|
|
206
|
+
else x[HEATMAP_DELIVERED_DESCRIPTION],
|
|
207
|
+
axis=1,
|
|
208
|
+
)
|
|
209
|
+
# All other cases are put as 0
|
|
210
|
+
df[HEATMAP_DELIVERED_DESCRIPTION] = df[HEATMAP_DELIVERED_DESCRIPTION].replace(np.nan, 0)
|
|
211
|
+
|
|
212
|
+
# Exported
|
|
213
|
+
|
|
214
|
+
# Where Heatmap is 0, we put a desription of 0 (No impact)
|
|
215
|
+
df[HEATMAP_EXPORTED_DESCRIPTION] = df[HEATMAP_EXPORTED].apply(
|
|
216
|
+
lambda x: 0 if x == 0 else float("NaN")
|
|
217
|
+
)
|
|
218
|
+
# When the energy delta is positive, and the price delta is positive, we put a description of 5 (high injection, high price)
|
|
219
|
+
df[HEATMAP_EXPORTED_DESCRIPTION] = df.apply(
|
|
220
|
+
lambda x: 5
|
|
221
|
+
if x[PRICE_ELECTRICITY_EXPORTED] > x[SPP_WEIGHTED_PRICE_EXPORTED]
|
|
222
|
+
and x[ELECTRICITY_EXPORTED_SMR3] > x[ELECTRICITY_EXPORTED_SMR2]
|
|
223
|
+
else x[HEATMAP_EXPORTED_DESCRIPTION],
|
|
224
|
+
axis=1,
|
|
225
|
+
)
|
|
226
|
+
# When the energy delta is negative, and the price delta is positive, we put a description of 6 (low injection, high price)
|
|
227
|
+
df[HEATMAP_EXPORTED_DESCRIPTION] = df.apply(
|
|
228
|
+
lambda x: 6
|
|
229
|
+
if x[PRICE_ELECTRICITY_EXPORTED] > x[SPP_WEIGHTED_PRICE_EXPORTED]
|
|
230
|
+
and x[ELECTRICITY_EXPORTED_SMR3] < x[ELECTRICITY_EXPORTED_SMR2]
|
|
231
|
+
else x[HEATMAP_EXPORTED_DESCRIPTION],
|
|
232
|
+
axis=1,
|
|
233
|
+
)
|
|
234
|
+
# When the energy delta is positive, and the price delta is negative, we put a description of 7 (high injection, low price)
|
|
235
|
+
df[HEATMAP_EXPORTED_DESCRIPTION] = df.apply(
|
|
236
|
+
lambda x: 7
|
|
237
|
+
if x[PRICE_ELECTRICITY_EXPORTED] < x[SPP_WEIGHTED_PRICE_EXPORTED]
|
|
238
|
+
and x[ELECTRICITY_EXPORTED_SMR3] > x[ELECTRICITY_EXPORTED_SMR2]
|
|
239
|
+
else x[HEATMAP_EXPORTED_DESCRIPTION],
|
|
240
|
+
axis=1,
|
|
241
|
+
)
|
|
242
|
+
# When the energy delta is negative, and the price delta is negative, we put a description of 8 (low injection, low price)
|
|
243
|
+
df[HEATMAP_EXPORTED_DESCRIPTION] = df.apply(
|
|
244
|
+
lambda x: 8
|
|
245
|
+
if x[PRICE_ELECTRICITY_EXPORTED] < x[SPP_WEIGHTED_PRICE_EXPORTED]
|
|
246
|
+
and x[ELECTRICITY_EXPORTED_SMR3] < x[ELECTRICITY_EXPORTED_SMR2]
|
|
247
|
+
else x[HEATMAP_EXPORTED_DESCRIPTION],
|
|
248
|
+
axis=1,
|
|
249
|
+
)
|
|
250
|
+
# All other cases are put as 0
|
|
251
|
+
df[HEATMAP_EXPORTED_DESCRIPTION] = df[HEATMAP_EXPORTED_DESCRIPTION].replace(np.nan, 0)
|
|
252
|
+
|
|
253
|
+
# Total
|
|
254
|
+
|
|
255
|
+
# We see which of the individual heatmaps has the highest absolute value
|
|
256
|
+
# We put the description of the highest absolute value
|
|
257
|
+
df[HEATMAP_TOTAL_DESCRIPTION] = df.apply(
|
|
258
|
+
lambda x: x[HEATMAP_DELIVERED_DESCRIPTION]
|
|
259
|
+
if abs(x[HEATMAP_DELIVERED]) > abs(x[HEATMAP_EXPORTED])
|
|
260
|
+
else x[HEATMAP_EXPORTED_DESCRIPTION],
|
|
261
|
+
axis=1,
|
|
262
|
+
)
|
|
263
|
+
# Where Heatmap is 0, we put a desription of 0 (No impact)
|
|
264
|
+
df[HEATMAP_TOTAL_DESCRIPTION] = df.apply(
|
|
265
|
+
lambda x: 0 if x[HEATMAP_TOTAL] == 0 else x[HEATMAP_TOTAL_DESCRIPTION], axis=1
|
|
266
|
+
)
|
|
267
|
+
# All other cases are put as 0
|
|
268
|
+
df[HEATMAP_TOTAL_DESCRIPTION] = df[HEATMAP_TOTAL_DESCRIPTION].replace(np.nan, 0)
|
|
269
|
+
|
|
270
|
+
if not inplace:
|
|
271
|
+
return df
|
|
272
|
+
|
|
273
|
+
|
|
154
274
|
def calculate_dyntar_columns(df: pd.DataFrame, inplace: bool = False) -> pd.DataFrame | None:
|
|
155
275
|
"""Calculate all columns required for the dynamic tariff analysis."""
|
|
156
276
|
if not inplace:
|
|
@@ -160,6 +280,7 @@ def calculate_dyntar_columns(df: pd.DataFrame, inplace: bool = False) -> pd.Data
|
|
|
160
280
|
extend_dataframe_with_costs(df, inplace=True)
|
|
161
281
|
extend_dataframe_with_weighted_prices(df, inplace=True)
|
|
162
282
|
extend_dataframe_with_heatmap(df, inplace=True)
|
|
283
|
+
extend_dataframe_with_heatmap_description(df, inplace=True)
|
|
163
284
|
|
|
164
285
|
if not inplace:
|
|
165
286
|
return df
|
openenergyid/dyntar/models.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Models for dynamic tariff analysis."""
|
|
2
2
|
|
|
3
3
|
from typing import Literal
|
|
4
|
-
from pydantic import Field, conlist
|
|
4
|
+
from pydantic import Field, conlist, confloat
|
|
5
5
|
|
|
6
6
|
from openenergyid.models import TimeDataFrame
|
|
7
7
|
|
|
@@ -33,6 +33,9 @@ OutputColumns = Literal[
|
|
|
33
33
|
"heatmap_delivered",
|
|
34
34
|
"heatmap_exported",
|
|
35
35
|
"heatmap_total",
|
|
36
|
+
"heatmap_delivered_description",
|
|
37
|
+
"heatmap_exported_description",
|
|
38
|
+
"heatmap_total_description",
|
|
36
39
|
]
|
|
37
40
|
|
|
38
41
|
|
|
@@ -46,11 +49,11 @@ class DynamicTariffAnalysisInput(TimeDataFrame):
|
|
|
46
49
|
)
|
|
47
50
|
data: list[
|
|
48
51
|
conlist(
|
|
49
|
-
item_type=
|
|
52
|
+
item_type=confloat(allow_inf_nan=True),
|
|
50
53
|
min_length=len(RequiredColumns.__args__),
|
|
51
54
|
max_length=len(RequiredColumns.__args__),
|
|
52
55
|
) # type: ignore
|
|
53
|
-
] = Field(examples=[[0.0
|
|
56
|
+
] = Field(examples=[[0.0] * len(RequiredColumns.__args__)])
|
|
54
57
|
|
|
55
58
|
|
|
56
59
|
class DynamicTariffAnalysisOutput(TimeDataFrame):
|
|
@@ -62,5 +65,9 @@ class DynamicTariffAnalysisOutput(TimeDataFrame):
|
|
|
62
65
|
examples=[OutputColumns.__args__],
|
|
63
66
|
)
|
|
64
67
|
data: list[
|
|
65
|
-
conlist(
|
|
66
|
-
|
|
68
|
+
conlist(
|
|
69
|
+
item_type=confloat(allow_inf_nan=True),
|
|
70
|
+
min_length=1,
|
|
71
|
+
max_length=len(OutputColumns.__args__),
|
|
72
|
+
) # type: ignore
|
|
73
|
+
] = Field(examples=[[0.0] * len(OutputColumns.__args__)])
|
openenergyid/models.py
CHANGED
|
@@ -6,7 +6,7 @@ from typing import overload
|
|
|
6
6
|
from typing import Self
|
|
7
7
|
|
|
8
8
|
import pandas as pd
|
|
9
|
-
from pydantic import BaseModel
|
|
9
|
+
from pydantic import BaseModel
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class TimeSeriesBase(BaseModel):
|
|
@@ -78,13 +78,7 @@ class TimeSeries(TimeSeriesBase):
|
|
|
78
78
|
"""
|
|
79
79
|
|
|
80
80
|
name: str | None = None
|
|
81
|
-
data: list[float
|
|
82
|
-
|
|
83
|
-
@field_validator("data")
|
|
84
|
-
@classmethod
|
|
85
|
-
def replace_nan_with_none(cls, data: list[float]) -> list[float | None]:
|
|
86
|
-
"""Replace NaN values with None."""
|
|
87
|
-
return [None if pd.isna(value) else value for value in data]
|
|
81
|
+
data: list[float]
|
|
88
82
|
|
|
89
83
|
@classmethod
|
|
90
84
|
def from_pandas(cls, data: pd.Series) -> Self:
|
|
@@ -102,13 +96,7 @@ class TimeDataFrame(TimeSeriesBase):
|
|
|
102
96
|
"""Time series data with multiple columns."""
|
|
103
97
|
|
|
104
98
|
columns: list[str]
|
|
105
|
-
data: list[list[float
|
|
106
|
-
|
|
107
|
-
@field_validator("data")
|
|
108
|
-
@classmethod
|
|
109
|
-
def replace_nan_with_none(cls, data: list[list[float]]) -> list[list[float | None]]:
|
|
110
|
-
"""Replace NaN values with None."""
|
|
111
|
-
return [[None if pd.isna(value) else value for value in row] for row in data]
|
|
99
|
+
data: list[list[float]]
|
|
112
100
|
|
|
113
101
|
@classmethod
|
|
114
102
|
def from_pandas(cls, data: pd.DataFrame) -> Self:
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
openenergyid/__init__.py,sha256=
|
|
1
|
+
openenergyid/__init__.py,sha256=ntL_zbXBUmsAcqrx7QRz8_g3CNWyjWhlg9osouG9a-k,193
|
|
2
2
|
openenergyid/const.py,sha256=D-xUnUyVuLmphClkePgxpFP6z0RDhw_6m7rX0BHBgrw,823
|
|
3
3
|
openenergyid/enums.py,sha256=jdw4CB1gkisx0re_SesrTEyh_T-UxYp6uieE7iYlHdA,357
|
|
4
|
-
openenergyid/models.py,sha256=
|
|
4
|
+
openenergyid/models.py,sha256=CO6VdthCOQ9hNXqVSan_4IOBpiQvOix-ea3U6TR7Vgc,4343
|
|
5
5
|
openenergyid/capacity/__init__.py,sha256=1En96HlPV8kd1hOJO9RjRbXNInp5ZSkmjsjp0jfZlcQ,221
|
|
6
6
|
openenergyid/capacity/main.py,sha256=G6_EtXs1k_W-fxS33pFrCNKajuH81skdI32zp5RX9bI,3674
|
|
7
7
|
openenergyid/capacity/models.py,sha256=qi0IFyF_QOVleSzN8g0U2Fzqcc9ZDfNKt8oteFLY6Q0,832
|
|
8
8
|
openenergyid/dyntar/__init__.py,sha256=iQXQXrEQOiVNeeF6LRmUf3oOhKlGjMNF7o4T04IWTGA,371
|
|
9
|
-
openenergyid/dyntar/const.py,sha256=
|
|
10
|
-
openenergyid/dyntar/main.py,sha256=
|
|
11
|
-
openenergyid/dyntar/models.py,sha256=
|
|
9
|
+
openenergyid/dyntar/const.py,sha256=17qL0-S0SImsqrDEDrGS2GLyJYcJRw6GmmcTiML7tR0,956
|
|
10
|
+
openenergyid/dyntar/main.py,sha256=z_F520eCsLcSYTo9e0iJBGpzoMo1G_TEECVCNi5rA_M,10732
|
|
11
|
+
openenergyid/dyntar/models.py,sha256=FZq7HI1F-3nVeHwPkuB38-8u32JzdvsZaCzrhirFD2g,2094
|
|
12
12
|
openenergyid/energysharing/__init__.py,sha256=A4JfrUYf-hBCzhUm0qL1GGlNMvpO8OwXJo80dJxFIvw,274
|
|
13
13
|
openenergyid/energysharing/const.py,sha256=X2zEPtTlsmZ66w6RmLS_h8NmdzObAEi5N6-0yrLN5V4,219
|
|
14
14
|
openenergyid/energysharing/data_formatting.py,sha256=Kwuhyn6ao_8Brdm9frlA6VzYOqimNYZsRbYwNXnE7yc,2583
|
|
@@ -19,7 +19,7 @@ openenergyid/mvlr/helpers.py,sha256=Uzbfrj3IpH26wA206KOl0hNucKE-n9guJNC_EROBVKA,
|
|
|
19
19
|
openenergyid/mvlr/main.py,sha256=cn7jZ98cHn2eh-0zG9q8Pad0Ft_FuI-u3a-eeHeF8jA,1304
|
|
20
20
|
openenergyid/mvlr/models.py,sha256=XvkViOLlYqi0ffgF3AD4Jvk3yL05gsoKdKgBAsGJ7L4,8581
|
|
21
21
|
openenergyid/mvlr/mvlr.py,sha256=F7WvWnZQtqUmK1vsguemsn9n8pDDk3tQ1weOlv-bo0c,18626
|
|
22
|
-
openenergyid-0.1.
|
|
23
|
-
openenergyid-0.1.
|
|
24
|
-
openenergyid-0.1.
|
|
25
|
-
openenergyid-0.1.
|
|
22
|
+
openenergyid-0.1.18.dist-info/METADATA,sha256=d5UuJ8gqzwzSFx7aokbArsVr7RqetZoL8fJ3TwRuGlc,2477
|
|
23
|
+
openenergyid-0.1.18.dist-info/WHEEL,sha256=fl6v0VwpzfGBVsGtkAkhILUlJxROXbA3HvRL6Fe3140,105
|
|
24
|
+
openenergyid-0.1.18.dist-info/licenses/LICENSE,sha256=NgRdcNHwyXVCXZ8sJwoTp0DCowThJ9LWWl4xhbV1IUY,1074
|
|
25
|
+
openenergyid-0.1.18.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|