openenergyid 0.1.14__py2.py3-none-any.whl → 0.1.16__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.14"
3
+ __version__ = "0.1.16"
4
4
 
5
5
  from .enums import Granularity
6
6
  from .models import TimeDataFrame, TimeSeries
@@ -1,7 +1,9 @@
1
1
  """Main module for capacity analysis."""
2
2
 
3
3
  import datetime as dt
4
+ import typing
4
5
  import pandas as pd
6
+ import pandera.typing as pdt
5
7
 
6
8
 
7
9
  class CapacityAnalysis:
@@ -21,7 +23,7 @@ class CapacityAnalysis:
21
23
 
22
24
  def __init__(
23
25
  self,
24
- data: pd.Series,
26
+ data: pdt.Series,
25
27
  threshold: float = 2.5,
26
28
  window: str = "MS", # Default to month start
27
29
  x_padding: int = 4,
@@ -50,11 +52,12 @@ class CapacityAnalysis:
50
52
  """
51
53
  # Group by the specified window (default is month start)
52
54
  grouped = self.data.groupby(pd.Grouper(freq=self.window))
55
+
53
56
  # Find the index (timestamp) of the maximum value in each group
54
57
  peak_indices = grouped.idxmax()
58
+
55
59
  # Get the corresponding peak values
56
60
  peaks = self.data.loc[peak_indices][self.data > self.threshold]
57
-
58
61
  return peaks
59
62
 
60
63
  def find_peaks_with_surroundings(
@@ -69,12 +72,20 @@ class CapacityAnalysis:
69
72
  Returns:
70
73
  List[tuple[dt.datetime,float,pd.Series]]: A list of tuples containing peak time, peak value, and surrounding data.
71
74
  """
72
- peaks = self.data.sort_values(ascending=False).head(num_peaks)
75
+ peaks = self.data.nlargest(num_peaks * 2)
73
76
  peaks = peaks[peaks > self.threshold]
74
77
  if peaks.empty:
75
78
  return []
79
+
76
80
  result = []
81
+ window_size = dt.timedelta(minutes=15 * (2 * self.x_padding + 1))
82
+
77
83
  for peak_time, peak_value in peaks.items():
84
+ peak_time = typing.cast(pd.Timestamp, peak_time)
85
+
86
+ if any(abs(peak_time - prev_peak[0]) < window_size for prev_peak in result):
87
+ continue
88
+
78
89
  start_time = peak_time - dt.timedelta(minutes=15 * self.x_padding)
79
90
  end_time = peak_time + dt.timedelta(minutes=15 * (self.x_padding + 1))
80
91
  surrounding_data = self.data[start_time:end_time]
@@ -86,5 +97,6 @@ class CapacityAnalysis:
86
97
  surrounding_data,
87
98
  ]
88
99
  )
89
-
100
+ if len(result) == num_peaks:
101
+ break
90
102
  return result
@@ -10,6 +10,7 @@ class CapacityInput(BaseModel):
10
10
 
11
11
  timezone: str = Field(alias="timeZone")
12
12
  series: TimeSeries
13
+ threshold: float = Field(default=2.5, ge=0)
13
14
 
14
15
 
15
16
  class PeakDetail(BaseModel):
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, field_validator
10
10
 
11
11
 
12
12
  class TimeSeriesBase(BaseModel):
@@ -47,7 +47,7 @@ class TimeSeriesBase(BaseModel):
47
47
 
48
48
  @overload
49
49
  @classmethod
50
- def from_json(cls, path: str, **kwargs) -> Self:
50
+ def from_json(cls, *, path: str, **kwargs) -> Self:
51
51
  """Load from a JSON file."""
52
52
 
53
53
  @classmethod
@@ -63,15 +63,33 @@ class TimeSeriesBase(BaseModel):
63
63
 
64
64
 
65
65
  class TimeSeries(TimeSeriesBase):
66
- """Time series data with a single column."""
66
+ """
67
+ Represents a time series data.
68
+ Attributes:
69
+ name (str | None): The name of the time series.
70
+ data (list[float | None]): The data points of the time series.
71
+ Methods:
72
+ replace_nan_with_none(cls, data: list[float]) -> list[float | None]:
73
+ Replace NaN values with None.
74
+ from_pandas(cls, data: pd.Series) -> Self:
75
+ Create a TimeSeries object from a Pandas Series.
76
+ to_pandas(self, timezone: str = "UTC") -> pd.Series:
77
+ Convert the TimeSeries object to a Pandas Series.
78
+ """
67
79
 
68
80
  name: str | None = None
69
- data: list[float]
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]
70
88
 
71
89
  @classmethod
72
90
  def from_pandas(cls, data: pd.Series) -> Self:
73
91
  """Create from a Pandas Series."""
74
- return cls.model_construct(name=data.name, data=data.tolist(), index=data.index.tolist())
92
+ return cls(name=str(data.name), data=data.tolist(), index=data.index.tolist())
75
93
 
76
94
  def to_pandas(self, timezone: str = "UTC") -> pd.Series:
77
95
  """Convert to a Pandas Series."""
@@ -84,12 +102,18 @@ class TimeDataFrame(TimeSeriesBase):
84
102
  """Time series data with multiple columns."""
85
103
 
86
104
  columns: list[str]
87
- data: list[list[float]]
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]
88
112
 
89
113
  @classmethod
90
114
  def from_pandas(cls, data: pd.DataFrame) -> Self:
91
115
  """Create from a Pandas DataFrame."""
92
- return cls.model_construct(
116
+ return cls(
93
117
  columns=data.columns.tolist(), data=data.values.tolist(), index=data.index.tolist()
94
118
  )
95
119
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: openenergyid
3
- Version: 0.1.14
3
+ Version: 0.1.16
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,11 +1,10 @@
1
- openenergyid/__init__.py,sha256=HRG7FGexmhpqDgPipT6vEZrgrPFzuYyrfGWeliceX58,193
1
+ openenergyid/__init__.py,sha256=FfypAMLDwdX1imytBRjsN21Oifo8HrmhShNxSLKjFXo,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=ghH-pAqbEgT13GfnDFC_W7dVr4NjvLz4QqTajqyeUJM,3855
5
- openenergyid/capacity/PowerAnalysis.py,sha256=V3-CupATVMQ95U-GWVumAbhj4IccHW-aAc4-Gsq7kWk,1136
4
+ openenergyid/models.py,sha256=F-7BOB-UxIcmR0Y02NHWWjoM_yWwkVOeRtIwZl72KgI,4877
6
5
  openenergyid/capacity/__init__.py,sha256=1En96HlPV8kd1hOJO9RjRbXNInp5ZSkmjsjp0jfZlcQ,221
7
- openenergyid/capacity/main.py,sha256=RLTPOopVW11vSAp-FxsalnzrinwQRnc2LzhJt1RpLMk,3335
8
- openenergyid/capacity/models.py,sha256=rsxp3tP7_Vk9Qy3EypB9XeTKPpeXk2xRU1GuoT1vUwQ,784
6
+ openenergyid/capacity/main.py,sha256=G6_EtXs1k_W-fxS33pFrCNKajuH81skdI32zp5RX9bI,3674
7
+ openenergyid/capacity/models.py,sha256=qi0IFyF_QOVleSzN8g0U2Fzqcc9ZDfNKt8oteFLY6Q0,832
9
8
  openenergyid/dyntar/__init__.py,sha256=iQXQXrEQOiVNeeF6LRmUf3oOhKlGjMNF7o4T04IWTGA,371
10
9
  openenergyid/dyntar/const.py,sha256=K7X6nHIl9DNyC6hU8jLtvOy3-IBGuYC449evOpImuJE,773
11
10
  openenergyid/dyntar/main.py,sha256=cFfRZuwTVwHhXzR1pjKgQ4uhx4mMTAhjgFHJRMggKF8,5267
@@ -20,7 +19,7 @@ openenergyid/mvlr/helpers.py,sha256=Uzbfrj3IpH26wA206KOl0hNucKE-n9guJNC_EROBVKA,
20
19
  openenergyid/mvlr/main.py,sha256=cn7jZ98cHn2eh-0zG9q8Pad0Ft_FuI-u3a-eeHeF8jA,1304
21
20
  openenergyid/mvlr/models.py,sha256=XvkViOLlYqi0ffgF3AD4Jvk3yL05gsoKdKgBAsGJ7L4,8581
22
21
  openenergyid/mvlr/mvlr.py,sha256=F7WvWnZQtqUmK1vsguemsn9n8pDDk3tQ1weOlv-bo0c,18626
23
- openenergyid-0.1.14.dist-info/METADATA,sha256=OEFjFlga0hHK7snhPa8OnGAzdzcAtqfECmXzLJwY_7k,2477
24
- openenergyid-0.1.14.dist-info/WHEEL,sha256=fl6v0VwpzfGBVsGtkAkhILUlJxROXbA3HvRL6Fe3140,105
25
- openenergyid-0.1.14.dist-info/licenses/LICENSE,sha256=NgRdcNHwyXVCXZ8sJwoTp0DCowThJ9LWWl4xhbV1IUY,1074
26
- openenergyid-0.1.14.dist-info/RECORD,,
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,,
@@ -1,34 +0,0 @@
1
- import datetime
2
-
3
- from openenergyid.capacity.models import CapacityInput
4
-
5
-
6
- class PowerPeakAnalysis:
7
- """analysis
8
- This class is used to analyze the power peaks of a given time series.
9
- The analysis is based on the following parameters:
10
- - min_peak_value: The minimum value of a peak to be considered a peak.
11
- - num_peaks: The number of peaks to be returned.
12
- - from_date: The start date of the analysis.
13
- - to_date: The end date of the analysis.
14
- - x_padding: The number of days to be added to the start and end date to
15
- ensure that the peaks are not cut off.
16
- - capacity_input: The input data for the analysis.
17
- """
18
-
19
- def __init__(
20
- self,
21
- min_peak_value: float,
22
- num_peaks: int,
23
- from_date: datetime,
24
- to_date: datetime,
25
- x_padding: int = 2,
26
- capacity_input=CapacityInput,
27
- ):
28
- self.data = input.get_series()
29
- self.timezone = capacity_input.timezone
30
- self.min_peak_value = min_peak_value
31
- self.num_peaks = num_peaks
32
- self.from_date = from_date
33
- self.to_date = to_date
34
- self.x_padding = x_padding