Source code for energy_analysis_toolbox.timeseries.resample._facade

from collections.abc import Callable
from typing import Literal

import pandas as pd

from .conservative import (
    flow_rate_to_freq,
    volume_to_freq,
)
from .index_transformation import index_to_freq
from .interpolate import (
    piecewise_affine,
    piecewise_constant,
)

resampling_methods = (
    Literal[
        "piecewise_affine",
        "piecewise_constant",
        "volume_conservative",
        "flow_rate_conservative",
    ]
    | Callable[[pd.Series, pd.DatetimeIndex], pd.Series]
)


[docs] def to_freq( timeseries: "pd.Series[float]", freq: str, origin: Literal["floor", "ceil"] | pd.Timestamp | None = None, last_step_duration: float | None = None, method: resampling_methods = "piecewise_affine", **kwargs, ) -> "pd.Series[float]": """Return a timeseries resampled at a given frequency. Parameters ---------- timeseries : pd.Series Series of values of a function of time, indexed using DateTimeIndex. freq : str Frequency of the resampled series. See :py:func:`pandas.Series.resample` for a list of possible values. origin : {None, 'floor', 'ceil', pd.Timestamp}, optional Origin of the resampling period. see :py:func:`.index_to_freq` for details. last_step_duration : {None, float}, optional Duration of the last time-step in `timeseries` in (s). See :py:func:`.index_to_freq` for details. method : str or callable, optional Method used to interpolate the values of the resampled series. The accepted values are: * 'piecewise_affine': uses :py:func:`.piecewise_affine`, assume the values a straight line between two points. The default method. * 'piecewise_constant': uses :py:func:`.piecewise_constant`, assume the values constant until the next point. * 'volume_conservative': uses :py:func:`.volume_to_freq`, conserve the quantity of the values. Best to use it for energy timeseries. * 'flow_rate_conservative': uses :py:func:`.flow_rate_to_freq`, conserve the values time the duration between two points. Best to use it for power timeseries. If a callable is passed, it must take a :py:class:`pandas.Series` as first argument and a :py:class:`pandas.DatetimeIndex` as second argument. See the interface of :py:func:`piecewise_affine` function. The default is 'piecewise_affine'. .. important:: The various methods may manage extrapolation differently, so this situation should be avoided or managed with special care. Returns ------- new_series : pd.Series Values of the series resampled at the given frequency. Examples -------- This function can be used to resample timeseries of different physical nature with the right method depending on the physical quantity. For example, a timeseries of pointwise temperature can be resampled using piecewise affine interpolation: >>> new_temp = eat.timeseries.resample.to_freq(temperature, '1min', ... method='piecewise_affine') The same is true for an energy index : >>> new_index = eat.timeseries.resample.to_freq(temperature, '1min', ... method='piecewise_affine') Regarding a quantity that is conserved over time, such as a volume or a flow rate, the resampling should be done using a conservative method. For power and energy, dedicated functions exists, but this function can also be used: >>> new_volume = eat.timeseries.resample.to_freq(volume, '1min', ... method='volume_conservative') >>> new_flow_rate = eat.timeseries.resample.to_freq(flow_rate, '1min', ... method='flow_rate_conservative') See more examples of use in :doc:`/user_guide/Resampling_time_series`. """ # Directly apply the method if it is a conservative method for which an # integrated method exists integrated_methods = { "volume_conservative": volume_to_freq, "flow_rate_conservative": flow_rate_to_freq, } try: method = integrated_methods[method] except KeyError: pass else: return method( timeseries, freq, origin=origin, last_step_duration=last_step_duration, ) # Resample target_instants = index_to_freq( timeseries.index, freq, origin=origin, last_step_duration=last_step_duration, ) # Ensure each method receives only its required parameters if method == "piecewise_constant": new_series = piecewise_constant( timeseries, target_instants, left_pad=kwargs.get("left_pad"), ) elif method == "piecewise_affine": new_series = piecewise_affine(timeseries, target_instants) else: kwargs["freq"] = freq kwargs["origin"] = origin kwargs["last_step_duration"] = last_step_duration new_series = method(timeseries, target_instants, **kwargs) new_series.index.name = timeseries.index.name return new_series
[docs] def trim_out_of_bounds( data: pd.DataFrame | pd.Series, resampled_data: pd.DataFrame | pd.Series, fill_value: dict[str, any] | None = None, ) -> pd.DataFrame | pd.Series: """Fill resampled data with NA outside the boundaries of initial index. Parameters ---------- data : pd.DataFrame or pd.Series The table of original data which has been resampled. resampled_data : pd.DataFrame or pd.Series The result of the resampling. fill_value : dict, optional A dictionary where keys are column names, and values are the placeholders used for samples outside the boundaries of the original index. If None, defaults to ``{'value': pd.NA}``. Returns ------- resampled_data : pd.DataFrame or Series The passed resample data with placeholders set **inplace**. """ if fill_value is None: fill_value = {"value": pd.NA} if resampled_data.index[0] < data.index[0]: for col, value in fill_value.items(): resampled_data.loc[resampled_data.index < data.index[0], col] = value if resampled_data.index[-1] > data.index[-1]: for col, value in fill_value.items(): resampled_data.loc[resampled_data.index > data.index[-1], col] = value return resampled_data