From 63ae42ca571e448ef0168c5a8077a4f1b2277faf Mon Sep 17 00:00:00 2001 From: "christoph.von.oy" <christoph.von.oy@rwth-aachen.de> Date: Thu, 16 May 2024 13:34:18 +0200 Subject: [PATCH] Extended Dynamic interface 2 --- dynamics.py | 95 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 21 deletions(-) diff --git a/dynamics.py b/dynamics.py index e991542..5a0992f 100644 --- a/dynamics.py +++ b/dynamics.py @@ -40,9 +40,19 @@ class Dynamic(abc.ABC): def shape(self) -> int: pass - # returns the length that a ndarray needs in order to hold state values for this dynamic + # returns the length that a ndarray needs in order to hold the first state values for this dynamic @abc.abstractmethod - def state_shape(self) -> int: + def first_state_shape(self) -> int: + pass + + # returns the index that a pandas Series or DataFrame needs in order to hold values for this dynamic + @abc.abstractmethod + def pandas_index(self) -> pd.Index: + pass + + # returns the index that a pandas Series or DataFrame needs in order to hold first state values for this dynamic + @abc.abstractmethod + def first_state_pandas_index(self) -> pd.Index: pass # returns the length of the time step at the given position in hours @@ -108,8 +118,14 @@ class TrivialDynamic(Dynamic): def shape(self) -> int: return len(self.d_steps) - def state_shape(self) -> int: - return len(self.d_steps) + 1 + def first_state_shape(self) -> int: + return 1 + + def pandas_index(self) -> pd.Index: + return pd.Index(np.arange(0, len(self.d_steps), dtype=int), dtype=int) + + def first_state_pandas_index(self) -> pd.Index: + return pd.Index([-1], dtype=int) def step_size(self, position) -> float: return self.d_steps[position] / 3600.0 @@ -269,8 +285,14 @@ class RootDynamic(TreeDynamic): def shape(self) -> int: return len(self.d_steps) - def state_shape(self) -> int: - return len(self.d_steps) + 1 + def first_state_shape(self) -> int: + return 1 + + def pandas_index(self) -> pd.Index: + return pd.Index(np.arange(0, len(self.d_steps), dtype=int), dtype=int) + + def first_state_pandas_index(self) -> pd.Index: + return pd.Index([-1], dtype=int) def step_size(self, position) -> float: return self.d_steps[position] / 3600.0 @@ -339,8 +361,14 @@ class BackedDynamic(TreeDynamic): def shape(self) -> int: return len(self.indices) - 1 - def state_shape(self) -> int: - return len(self.indices) + def first_state_shape(self) -> int: + return 1 + + def pandas_index(self) -> pd.Index: + return pd.Index(self.indices[:-1], dtype=int) + + def first_state_pandas_index(self) -> pd.Index: + return pd.Index([-1], dtype=int) def step_size(self, position) -> float: if position < 0 or len(self.indices) <= position: @@ -458,8 +486,14 @@ class PartialDynamic(TreeDynamic): def shape(self) -> int: return self.end - self.start - def state_shape(self) -> int: - return self.end - self.start + 1 + def first_state_shape(self) -> int: + return 1 + + def pandas_index(self) -> pd.Index: + return pd.Index(self.reference._all_indices()[self.start : self.end], dtype=int) + + def first_state_pandas_index(self) -> pd.Index: + return pd.Index([-1], dtype=int) def step_size(self, position) -> float: if position < 0 or self.end - self.start < position: @@ -1250,7 +1284,7 @@ class AggregatedDynamic(Dynamic): self.period_dynamics = [] self.n_p = [] running_index = 0 - self.offsets_ = [] + self.offsets = [] for i, period in enumerate(periods): index = 0 period_indices = [0] @@ -1265,12 +1299,12 @@ class AggregatedDynamic(Dynamic): self.n_p.append( sum(1 for period_index in period_order if period_index == i) ) - self.offsets_.append((running_index, running_index + len(period))) + self.offsets.append((running_index, running_index + len(period))) running_index += len(period) self.period_order = period_order self.n = len(period_order) self.shape_ = running_index - self.state_shape_ = running_index + len(periods) + self.frist_state_shape_ = len(periods) # sanity check 5: all elements in period order are valid period indices (above n_p[i] is the number of times that period index i appears in period order -> sum(n_p) == len(period_order)) if sum(n_p for n_p in self.n_p) != len(period_order): @@ -1281,8 +1315,8 @@ class AggregatedDynamic(Dynamic): def number_of_periods(self) -> int: return len(self.period_dynamics) - def offsets(self, period_index) -> Tuple[int]: - return self.offsets_[period_index] + def offset(self, period_index) -> Tuple[int]: + return self.offsets[period_index] def number_of_steps(self) -> int: return self.dynamic.number_of_steps() @@ -1290,8 +1324,21 @@ class AggregatedDynamic(Dynamic): def shape(self) -> int: return self.shape_ - def state_shape(self) -> int: - return self.state_shape_ + def first_state_shape(self) -> int: + return self.frist_state_shape_ + + def pandas_index(self) -> pd.Index: + first_level = np.empty(self.shape(), dtype=int) + second_level = np.empty(self.shape(), dtype=int) + for i, (start, end) in enumerate(self.offsets): + first_level[start:end] = i + second_level[start:end] = np.arange(0, end - start, dtype=int) + return pd.MultiIndex.from_arrays([first_level, second_level]) + + def first_state_pandas_index(self) -> pd.Index: + first_level = np.arange(0, self.number_of_periods(), dtype=int) + second_level = np.full(self.first_state_shape(), -1, dtype=int) + return pd.MultiIndex.from_arrays([first_level, second_level]) def step_size(self, position) -> float: return self.dynamic.step_size(position) @@ -1323,8 +1370,14 @@ class PeriodDynamic(Dynamic): def shape(self) -> int: return self.dynamic.shape() - def state_shape(self) -> int: - return self.dynamic.state_shape() + def first_state_shape(self) -> int: + return self.dynamic.first_state_shape() + + def pandas_index(self) -> pd.Index: + return self.dynamic.pandas_index() + + def first_state_pandas_index(self) -> pd.Index: + return self.dynamic.first_state_pandas_index() def step_size(self, position) -> float: return self.dynamic.step_size(position) @@ -1377,7 +1430,7 @@ def resample_aggregation_down( raise ValueError( f"The period dynamic has to be part of the aggregated dynamic!" ) - offset = dynamic.offsets(target_dynamic.period_index) + offset = dynamic.offset(target_dynamic.period_index) return values[:, offset[0] : offset[1]] @@ -1422,7 +1475,7 @@ def resample_aggregation_up( raise ValueError( f"The period dynamic has to be part of the aggregated dynamic!" ) - offset = target_dynamic.offsets(dynamic.period_index) + offset = target_dynamic.offset(dynamic.period_index) target_values[:, offset[0] : offset[1]] = values -- GitLab