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 CHANGED
@@ -1,6 +1,6 @@
1
1
  """Open Energy ID Python SDK."""
2
2
 
3
- __version__ = "0.1.16"
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"
@@ -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
- heatmap_score_delivered = (
126
- (df[ELECTRICITY_DELIVERED_SMR2] - df[ELECTRICITY_DELIVERED_SMR3])
127
- / df[ELECTRICITY_DELIVERED_SMR2]
128
- * (df[RLP_WEIGHTED_PRICE_DELIVERED] - df[PRICE_ELECTRICITY_DELIVERED])
129
- / df[RLP_WEIGHTED_PRICE_DELIVERED]
130
- )
131
- heatmap_score_exported = (
132
- (df[ELECTRICITY_EXPORTED_SMR2] - df[ELECTRICITY_EXPORTED_SMR3])
133
- / df[ELECTRICITY_EXPORTED_SMR2]
134
- * (df[SPP_WEIGHTED_PRICE_EXPORTED] - df[PRICE_ELECTRICITY_EXPORTED])
135
- / df[SPP_WEIGHTED_PRICE_EXPORTED]
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
- heatmap_score_exported = -heatmap_score_exported
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
@@ -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.16
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=FfypAMLDwdX1imytBRjsN21Oifo8HrmhShNxSLKjFXo,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=cFfRZuwTVwHhXzR1pjKgQ4uhx4mMTAhjgFHJRMggKF8,5267
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.16.dist-info/METADATA,sha256=Mt5G-168EyZ95DV5UCsCE_E3Ns1DOb6TmqA-a4apOmg,2477
23
- openenergyid-0.1.16.dist-info/WHEEL,sha256=fl6v0VwpzfGBVsGtkAkhILUlJxROXbA3HvRL6Fe3140,105
24
- openenergyid-0.1.16.dist-info/licenses/LICENSE,sha256=NgRdcNHwyXVCXZ8sJwoTp0DCowThJ9LWWl4xhbV1IUY,1074
25
- openenergyid-0.1.16.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,,