Source code for energy_analysis_toolbox.timeseries.profiles.thresholds.hybrid_rel_std
"""Compute hybrid profiles based on deviations from the mean profile.
This module provides tools to calculate threshold profiles driven by a combination
of standard deviation and relative thresholds derived from historical data.
The thresholds are designed to adapt to variability in the data, balancing stability
with tolerance for variability in consumption patterns.
"""
import pandas as pd
from energy_analysis_toolbox.timeseries.profiles.mean_profile import MeanProfile
[docs]
class HybridThreshold(MeanProfile):
"""Returns the deviation from mean profile (hybrid std and relative thresholds)."""
def __init__(
self,
offset_std: float = 3,
offset_relative: float = 0.5,
**kwargs,
) -> None:
"""Initialize HybridThreshold.
Parameters
----------
period : str, optional
A pandas period string which specifies the kind of period on which
the profile is realized.
offset_std : float, optional
Number of standard deviations VS the computed reference to obtain
the threshold profile. Default is 3 (profile is 3 standard deviations
from reference)
offset_relative : float, optional
Relative difference VS the computed reference to obtain
the threshold profile. Default is 0.5 (profile is 150%
of reference)
"""
super().__init__(**kwargs)
self.offset_std = offset_std
self.offset_rel = offset_relative
[docs]
def compute(
self,
history: pd.Series,
time: pd.Timestamp,
**kwargs,
) -> pd.Series:
"""Return a threshold profile.
The threshold profile is obtained using a user-defined relative variation
from the mean profile built from history.
Parameters
----------
history : pd.Series
Consumption history used to computed the reference
profile.
time : pd.Timestamp
The time at which the profile is of interest. Only
the information about the date is used in the passed
timestamp.
Returns
-------
profile : pd.Series
Profile threshold with same resolution as the history data
Notes
-----
The profile threshold is obtained as an hybrid between the std and
arbitrary tshd profiles :
- an average profile is computed from the rolling max of the history
on a window centered on each slot with size ``window``.
- a std profile is computed from the history (without rolling max)
- the returned profile is obtained as the max between :
* the profile based on ``tshd_std`` standard deviation over the
mean profile (the std is computed from the data without rolling
aggregation),
* the profile based on ``(1 + tshd_rel)`` times the mean profile.
The idea behind this profile is to base the threshold on the data variability,
but to still have a certain tolerance when the consumption is very stable in
the (short) history.
The rolling max also artificially increases the mean value around slots where
the consumption is high in order to account for the correlation between the
consumption on consecutive slots : a certain "horizontal" dispersion of the
consumption is relatively normal.
.. note:
In ``self.is_max == False``, replace "max" by "min" in the text above.
"""
# rel and ref on smoothed data
smooth_mean_profile = super().compute(history, time, **kwargs)
rel_profile = smooth_mean_profile * self.offset_rel
# std on unsmoothed data
std_profile = self.group(history).std() * self.offset_std
std_profile.index = rel_profile.index
profile_deviations = pd.DataFrame.from_dict(
{"std": std_profile, "tshd": rel_profile},
)
if self.is_max:
var_profile = profile_deviations.max(axis=1)
else:
var_profile = profile_deviations.min(axis=1)
return smooth_mean_profile + var_profile