Source code for energy_analysis_toolbox.power.basics
"""Applies elementary operations on power timeseries."""
import pandas as pd
from energy_analysis_toolbox.timeseries.extract_features import timestep_durations
[docs]
def integrate_over(
intervals: pd.DataFrame,
power_series: pd.Series,
) -> pd.Series:
"""Return the energy integrated over slices of the power series.
.. warning::
This function only works on slices of the power series.
It cannot be used to integrate the power with smaller
granularity.
Parameters
----------
intervals : pd.DataFrame
A table of overconsumption defined with 'start' and 'end'
columns containing timestamps. **These timestamps are used as slice**
**bounds in the series** with included start and excluded end.
power_series : pd.Series
A timeseries of power in (W). The series must have at least
two elements so that the timestep(s) can be deduced.
Returns
-------
energies : pd.Series
A series of energy in (J) with same index as ``overconsumption``.
.. note::
Proper integration with a resolution independent from the timestep can
be achieved by transforming the power to energy using :py:func:`to_energy`
and then using the |volume_conservative| resampling.
Examples
--------
>>> power = _constant_power(); power
2023-10-29 00:00:00+02:00 1
2023-10-29 01:00:00+02:00 1
...
2023-10-30 22:00:00+01:00 1
2023-10-30 23:00:00+01:00 1
Freq: H, dtype: object
>>> overconsumption = _intervals()
>>> overconsumption
start end
A 2023-10-29 00:00:00+02:00 2023-10-29 01:00:00+02:00
B 2023-10-30 02:00:00+02:00 2023-10-30 02:00:00+01:00
>>> eat.power.integrate_over(overconsumption, power)
A 3600.0
B 3600.0
dtype: float64
Remind that the interval bounds are used to slice the series (with excluded
end). Accordingly, an interval is completely accounted for in the integration
as soon as it falls with the slice :
>>> overconsumption['end'] += pd.Timedelta('10min')
>>> eat.power.integrate_over(overconsumption, power)
A 7200.0
B 7200.0
dtype: float64
"""
timesteps = timestep_durations(power_series)
def integrate(row: pd.Series) -> float:
"""Integrate on a slice with excluded end."""
i_start = timesteps.index.get_slice_bound(row["start"], side="left")
i_end = timesteps.index.get_slice_bound(row["end"], side="left")
return (timesteps.iloc[i_start:i_end] * power_series.iloc[i_start:i_end]).sum()
return intervals.apply(integrate, axis=1)
[docs]
def to_energy(
power_series: pd.Series,
) -> pd.Series:
"""Return a series of energy per timestep.
Parameters
----------
power_series : pd.Series
Timeseries of (avg) power per ``[ti, ti+1[`` timestep in (W).
The series must have at least two elements so that the timestep(s)
can be deduced. The last timestep is assumed to be the same as the
former last one.
Returns
-------
pd.Series :
Timeseries of energy per timestep in (J) with same index
as input series.
"""
timesteps = timestep_durations(power_series)
return timesteps * power_series