openenergyid 0.1.17__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 CHANGED
@@ -1,6 +1,6 @@
1
1
  """Open Energy ID Python SDK."""
2
2
 
3
- __version__ = "0.1.17"
3
+ __version__ = "0.1.18"
4
4
 
5
5
  from .enums import Granularity
6
6
  from .models import TimeDataFrame, TimeSeries
@@ -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"
@@ -26,6 +26,9 @@ from .const import (
26
26
  HEATMAP_DELIVERED,
27
27
  HEATMAP_EXPORTED,
28
28
  HEATMAP_TOTAL,
29
+ HEATMAP_DELIVERED_DESCRIPTION,
30
+ HEATMAP_EXPORTED_DESCRIPTION,
31
+ HEATMAP_TOTAL_DESCRIPTION,
29
32
  )
30
33
 
31
34
 
@@ -147,8 +150,7 @@ def extend_dataframe_with_heatmap(df: pd.DataFrame, inplace: bool = False) -> pd
147
150
 
148
151
  # Invert scores so that positive values indicate a positive impact
149
152
  heatmap_score_delivered = -heatmap_score_delivered
150
- heatmap_score_exported = -heatmap_score_exported
151
- heatmap_score_combined = heatmap_score_delivered - heatmap_score_exported
153
+ heatmap_score_combined = heatmap_score_delivered + heatmap_score_exported
152
154
 
153
155
  df[HEATMAP_DELIVERED] = heatmap_score_delivered
154
156
  df[HEATMAP_EXPORTED] = heatmap_score_exported
@@ -159,6 +161,116 @@ def extend_dataframe_with_heatmap(df: pd.DataFrame, inplace: bool = False) -> pd
159
161
  return None
160
162
 
161
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
+
162
274
  def calculate_dyntar_columns(df: pd.DataFrame, inplace: bool = False) -> pd.DataFrame | None:
163
275
  """Calculate all columns required for the dynamic tariff analysis."""
164
276
  if not inplace:
@@ -168,6 +280,7 @@ def calculate_dyntar_columns(df: pd.DataFrame, inplace: bool = False) -> pd.Data
168
280
  extend_dataframe_with_costs(df, inplace=True)
169
281
  extend_dataframe_with_weighted_prices(df, inplace=True)
170
282
  extend_dataframe_with_heatmap(df, inplace=True)
283
+ extend_dataframe_with_heatmap_description(df, inplace=True)
171
284
 
172
285
  if not inplace:
173
286
  return df
@@ -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=float,
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, 0.0, 0.0, 0.0, 0.0, 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(item_type=float, min_length=1, max_length=len(OutputColumns.__args__)) # type: ignore
66
- ] = Field(examples=[[0.0, 0.0, 0.0, 0.0, 0.0, 0.0]])
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, field_validator
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 | None]
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 | None]]
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,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: openenergyid
3
- Version: 0.1.17
3
+ Version: 0.1.18
4
4
  Summary: Open Source Python library for energy analytics and simulations
5
5
  Project-URL: Homepage, https://energyid.eu
6
6
  Project-URL: Repository, https://github.com/EnergieID/OpenEnergyID
@@ -1,14 +1,14 @@
1
- openenergyid/__init__.py,sha256=lFC4Rf6_0_VJqZZEGDBxHt7gMCsy39hvF-0pUBErI80,193
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=F-7BOB-UxIcmR0Y02NHWWjoM_yWwkVOeRtIwZl72KgI,4877
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=K7X6nHIl9DNyC6hU8jLtvOy3-IBGuYC449evOpImuJE,773
10
- openenergyid/dyntar/main.py,sha256=Q8_0vizBe__GZIcfOK5HqYTncRQHMjGJ5bk_TEBwuA8,5673
11
- openenergyid/dyntar/models.py,sha256=BbGdHj7eUOMepblWJNsNd21xULluv6m7TtdhSggvVbY,1873
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.17.dist-info/METADATA,sha256=MBxbfpJfYtdTtxCJy6MeysE_NAc6Q1r1ICFg5gc3j6I,2477
23
- openenergyid-0.1.17.dist-info/WHEEL,sha256=fl6v0VwpzfGBVsGtkAkhILUlJxROXbA3HvRL6Fe3140,105
24
- openenergyid-0.1.17.dist-info/licenses/LICENSE,sha256=NgRdcNHwyXVCXZ8sJwoTp0DCowThJ9LWWl4xhbV1IUY,1074
25
- openenergyid-0.1.17.dist-info/RECORD,,
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,,