ebm.areaforecast.s_curve module

original_condition(s_curve_cumulative_demolition, s_curve_renovation, s_curve_renovation_and_small_measure, s_curve_small_measure)[source]

Calculates buildings remaining as original condition by subtracting every other condition

Parameters

s_curve_cumulative_demolition : pandas.Series s_curve_renovation : pandas.Series s_curve_renovation_and_small_measure : pandas.Series s_curve_small_measure : pandas.Series

Returns

pandas.Series

buildings remaining as original condition

small_measure(s_curve_renovation_and_small_measure: Series, s_curve_small_measure_total: Series) Series[source]

Calculates the remaining small measure share by subtracting renovation and small measure values from the total small measure curve.

Parameters

s_curve_renovation_and_small_measure : Series s_curve_small_measure_total : Series

Returns

Series

s_curve_small_measure

Notes

  • This function currently does not implement logic to zero out values before the building year.

  • Assumes both input Series are aligned on the index year.

renovation_and_small_measure(s_curve_renovation: Series, s_curve_renovation_total: Series) Series[source]

Calculates the remaining renovation_and_small_measure share by subtracting renovation from the total renovation total curve.

Parameters

s_curve_renovationpandas.Series

A time series representing the S-curve of exclusive renovation condition.

s_curve_renovation_totalpandas.Series

A time series representing the total S-curve for the total renovation condition.

Returns

pandas.Series

A time series representing the difference between the total and renovation-only S-curves. Values before the building year should be set to 0 (not yet implemented).

Notes

  • This function currently does not implement logic to zero out values before the building year.

  • Assumes both input Series are aligned on index year.

trim_renovation_from_renovation_total(s_curve_renovation: Series, s_curve_renovation_max: Series, s_curve_renovation_total: Series, scurve_total: Series) Series[source]

Adjust the renovation S-curve by incorporating values from the total renovation curve where the total share is less than the maximum renovation share.

This function identifies time points where the total S-curve (scurve_total) is less than the maximum renovation S-curve (s_curve_renovation_max). For those points, it replaces the corresponding values in s_curve_renovation with values from s_curve_renovation_total.

Parameters

s_curve_renovationpandas.Series

The original renovation S-curve to be adjusted.

s_curve_renovation_maxpandas.Series

The maximum allowed values for the renovation S-curve.

s_curve_renovation_totalpandas.Series

The total renovation S-curve including all measures.

scurve_totalpandas.Series

The actual total S-curve values to compare against the max renovation curve.

Returns

pandas.Series

The adjusted renovation S-curve with values merged from the total renovation curve where the total share is less than the maximum renovation share.

Notes

  • Assumes all input Series are aligned on the index year.

renovation_from_small_measure(s_curve_renovation_max: Series, s_curve_small_measure_total: Series) Series[source]

Calculate the renovation S-curve by subtracting small measures from the max renovation curve.

Parameters

s_curve_renovation_maxpandas.Series

The maximum yearly values for the renovation S-curve.

s_curve_small_measure_totalpandas.Series

The yearly total S-curve for small measures.

Returns

pandas.Series

The resulting renovation S-curve with values clipped at 0

total(s_curve_renovation_total: Series, s_curve_small_measure_total: Series) Series[source]

Calculates the yearly sum of renovation and small_measure

Parameters

s_curve_renovation_total : pandas.Series s_curve_small_measure_total : pandas.Series

Returns

pandas.Series

yearly sum of renovation and small_measure

trim_max_value(s_curve_cumulative_small_measure: Series, s_curve_small_measure_max: Series) Series[source]
small_measure_max(s_curve_cumulative_demolition: Series, s_curve_small_measure_never_share: Series)[source]

Calculates the maximum possible value for small_measure condition

Parameters

s_curve_cumulative_demolition : pandas.Series s_curve_small_measure_never_share : pandas.Series

Returns

pandas.Series

Yearly maximum possible value for small_measure

renovation_max(s_curve_cumulative_demolition: Series, s_curve_renovation_never_share: Series)[source]

Calculates the maximum possible value for renovation condition

Parameters

s_curve_cumulative_demolition : pandas.Series s_curve_renovation_never_share : pandas.Series

Returns

pandas.Series

Yearly maximum possible value for renovation

cumulative_renovation(s_curves_with_building_code: Series, years: YearRange) Series[source]

Return the yearly cumulative sum of renovation condition.

Parameters

s_curves_with_building_code : pandas.Series years : pandas.Series

Returns

pandas.Series

cumulative sum of renovation

Notes

NaN values are replaced by float 0.0

cumulative_small_measure(s_curves_with_building_code: Series, years: YearRange) Series[source]

Return the yearly cumulative sum of small_measure condition.

Parameters

s_curves_with_building_code : pandas.Series years : YearRange

Returns

pandas.Series

cumulative sum of small_measure

Notes

NaN values are replaced by float 0.0

transform_demolition(demolition: Series, years: YearRange) Series[source]

Filter yearly demolition for years Parameters ———- demolition : pandas.Series years : YearRange

Returns

demolition for years

transform_to_cumulative_demolition(cumulative_demolition: DataFrame, years: YearRange) Series[source]

Filter yearly cumulative demolition for years Parameters ———- cumulative_demolition : pandas.DataFrame years : YearRange

Returns

pandas.Series

cumulative demolition for years

pad_s_curve_age(s_curves: DataFrame, scurve_parameters: DataFrame) DataFrame[source]

Transform scurve_parameters with s_curve to never_share. Parameters ———- s_curves : pandas.DataFrame scurve_parameters : pandas.DataFrame

Returns

pandas.DataFrame

Notes

Age is padded from -max age to 0

scurve_from_s_curve_parameters(scurve_parameters: DataFrame) DataFrame[source]
Create scurve new dataframe from scurve_parameters using ebm.model.area.building_condition_scurves and

ebm.model.area.building_condition_accumulated_scurves

Each row represent a building_category and building_condition at a certain age.

Parameters

scurve_parameters : pandas.DataFrame

Notes

Filters out age greater than 130 when last_age is not 150 for backwards compatability. Subject to change.

Returns

pandas.DataFrame

accumulate_demolition(s_curves_long: DataFrame, years: YearRange) DataFrame[source]

Sets demolition in year 0 (2020) to 0.0 and sums up the yearly demolition using years

Parameters

s_curves_long : pandas.DataFrame years : YearRange

Returns

pandas.DataFrame

merge_s_curves_and_building_code(s_curves: DataFrame, df_never_share: DataFrame, building_code_parameters: DataFrame) DataFrame[source]

Cross merge s_curves and df_never_share with all building_code in building_code_parameters

Parameters

s_curves : pandas.DataFrame df_never_share : pandas.DataFrame building_code_parameters : pandas.DataFrame

Returns

pandas.DataFrame

rates_grouped_by_period(rates: Series) DataFrame[source]
transform_to_dataframe(s_curve_cumulative_demolition: Series, s_curve_original_condition: Series, s_curve_renovation: Series, s_curve_renovation_and_small_measure: Series, s_curve_small_measure: Series, s_curve_demolition: Series) DataFrame[source]

Creates a pandas DataFrame from the parameters

Parameters

s_curve_cumulative_demolition : pandas.Series s_curve_original_condition : pandas.Series s_curve_renovation : pandas.Series s_curve_renovation_and_small_measure : pandas.Series s_curve_small_measure : pandas.Series s_curve_demolition : pandas.Series

Returns

pandas.DataFrame

transform_to_long(s_curves_by_condition: DataFrame) DataFrame[source]

Parameters

s_curves_by_condition : pandas.DataFrame

Returns

pandas.DataFrame

transformed to long, on condition for each row

calculate_s_curves(scurve_parameters: DataFrame, building_code_parameters: DataFrame, years: YearRange, **kwargs: DataFrame | Series) DataFrame[source]
normalize_scurve_conditions(s_curves_with_building_code, years, **kwargs)[source]
calculate_scurves_with_building_code(building_code_parameters, scurve_parameters, years, **kwargs)[source]
make_s_curve_parameters(earliest_age: int | None = None, average_age: int | None = None, last_age: int | None = None, rush_years: int | None = None, rush_share: float | None = None, never_share: float | None = None, building_lifetime: int = 130, building_category: str | None = 'unknown', condition: str | None = 'unknown') DataFrame[source]
translate_scurve_parameter_to_shortform(df: DataFrame) DataFrame[source]
scurve_rates_to_long(scurve_rates: DataFrame) DataFrame[source]
scurve_rates_with_age(df: DataFrame) DataFrame[source]
scurve_rates(s_curve_parameters: DataFrame) DataFrame[source]

Calculate s-curve rate from dataframe.

Parameters

s_curve_parameterspd.DataFrame

A pandas dataframe with average_age, earliest_age, rush_period, last_age, rush_share and never_share

Returns

pd.DataFrame

With columns rate, total_share indexed by building_category, building_condition and age

pause_building_condition_rates(s_curve_building_code: DataFrame, period: YearRange | tuple[int, int] | int, conditions: list[str] | None = None) DataFrame[source]

Apply a pause (deferral) to selected building condition rates and accumulate the results.

This function is a convenience wrapper that first shifts the specified building condition rate columns forward in time for a given pause period—simulating a deferral of activity—and then recomputes cumulative (running) totals using accumulate_building_condition_rates.

Internally, it applies two operations:

  1. shift_building_condition_rates: Shifts selected condition rate columns forward by the length of the pause period for all years greater than or equal to period.start, within each group of the first two index levels.

  2. accumulate_building_condition_rates: Recomputes cumulative totals for the condition rate columns and their corresponding accumulator columns.

Parameters

s_curve_building_codepandas.DataFrame

A DataFrame indexed by a MultiIndex with at least three levels, where index level 2 represents the year. Must contain the condition rate columns referenced in conditions and the accumulator columns required by accumulate_building_condition_rates.

periodYearRange | tuple[int, int] | int

The period defining the pause: - If a tuple[int, int]: interpreted as (start_year, end_year), inclusive. - If an int: treated as the pause start year, with the end year taken

as the maximum year present in the DataFrame.

  • If a YearRange: used directly.

The pause length is determined as the number of years in period.

conditionslist[str] or None, default None

Column names representing the building condition rates to pause. If None, defaults to: ['small_measure', 'renovation', 'demolition'].

Returns

pandas.DataFrame

A DataFrame where: - Values in the selected condition columns have been shifted forward

according to the pause period.

  • Accumulator columns have been updated to reflect the new running totals.

The presence and structure of the columns match the output of accumulate_building_condition_rates.

Notes

  • This function does not mutate the input DataFrame; it returns a new one.

  • All shifting is done within each group of the first two index levels.

  • Years are expected to be integers.

  • See shift_building_condition_rates and accumulate_building_condition_rates for detailed behavior of the underlying operations.

Examples

Pause condition rates starting from 2028 and recompute accumulations:

>>> out = pause_building_condition_rates(df, period=2028)

Pause explicitly from 2026 through 2029:

>>> out = pause_building_condition_rates(df, period=YearRange(2026, 2029))

Pause only renovation rates:

>>> out = pause_building_condition_rates(
...     df, period=(2025, 2027), conditions=['renovation']
... )
shift_building_condition_rates(s_curve_building_code: DataFrame, period: YearRange | tuple[int, int] | int, conditions: list[str] | None = None) DataFrame[source]

Shift building condition rates over a given period forward in time.

This function takes a copy of the input DataFrame and, for the specified building conditions, shifts values occurring from period.start onwards by the length of the period (i.e., the number of years in period). The shift is done within each group defined by the first two levels of the index. Newly created gaps are filled with 0.

The intended use is to simulate a pause (deferral) in certain building-related condition rates (e.g., small measures, renovation, demolition) for a span of years, and resume them after the pause by moving the corresponding values forward.

Parameters

s_curve_building_codepandas.DataFrame

A DataFrame indexed by a MultiIndex with at least three levels where the third level (index level number 2) is an integer year. The DataFrame must contain columns whose names match those provided in conditions. The function operates on a copy of this DataFrame and does not mutate the original.

periodYearRange | tuple[int, int] | int

The pause period: - If a tuple[int, int], it is interpreted as (start_year, end_year) inclusive.

The start will be clamped up to the minimum year present in the DataFrame.

  • If an int, it is treated as the start year; the end year is set to the maximum year present in the DataFrame. The start will be clamped up to the minimum year present in the DataFrame.

conditionslist[str] | None, default None

Column names representing the building condition rates to pause. If None, defaults to [‘small_measure’, ‘renovation’, ‘demolition’].

Returns

pandas.DataFrame

A new DataFrame (copy of the input) where, for rows with year >= period.start and columns listed in conditions, values are shifted forward by the pause length within each group of the first two index levels. Any values shifted beyond the available years are dropped by the shift operation, and any newly introduced gaps in the target slice are filled with 0.

Raises

ValueError

If period is not a YearRange, an int, or a valid (start_year, end_year) tuple where start_year <= end_year.

Notes

  • The function assumes: * The DataFrame index has a third level (at position 2) representing the year. * Years are integers and sortable. * The columns listed in conditions exist in the DataFrame.

  • The operation: 1. Determines the effective YearRange for the pause, clamping the start

    to the minimum year in the data if needed.

    1. Computes pause_length = len(period).

    2. For year >= period.start, shifts the conditions columns by pause_length within each group of the first two index levels.

    3. Fills NaN introduced by the shift with 0 in the affected slice.

Examples

Suppose df has a MultiIndex (region, building_code, year) and includes columns ‘small_measure’, ‘renovation’, and ‘demolition’.

Pause from 2028 through the max available year: >>> out = shift_building_condition_rates(df, period=2028)

Pause explicitly from 2026 to 2029 (inclusive): >>> out = shift_building_condition_rates(df, period=(2026, 2029))

Using a custom set of conditions: >>> out = shift_building_condition_rates(df, period=(2025, 2027), … conditions=[‘renovation’])

accumulate_building_condition_rates(building_condition_rates: DataFrame) DataFrame[source]

Accumulate annual building condition rates into running totals per building category and code.

This function takes a DataFrame indexed by a MultiIndex with at least three levels, where the third level (index level 2) represents the year. For three predefined condition columns—small_measure, renovation, and demolition—it computes cumulative sums across years within each group of the first two index levels.

The accumulation uses temporary columns: - For the first year (hard‑coded as 2020), values are taken from the corresponding

*_acc accumulator columns (defaulting to 0 where missing).

  • For all later years (from 2021 onward), values are taken from the annual condition rate columns.

These temporary values are cumulatively summed per group, and the resulting running totals populate the *_acc columns.

Parameters

building_condition_ratespandas.DataFrame

Input DataFrame with a MultiIndex where index level 2 is an integer year. Must contain the columns: 'small_measure', 'renovation', 'demolition', and their accumulator counterparts: 'small_measure_acc', 'renovation_acc', 'demolition_acc'.

Returns

pandas.DataFrame

A DataFrame containing only the six columns: ``[‘small_measure’, ‘renovation’, ‘demolition’,

‘small_measure_acc’, ‘renovation_acc’, ‘demolition_acc’]``.

The accumulator columns contain cumulative sums over years within each group of index levels 0 and 1.

Notes

  • The function does not modify the input DataFrame; it works on a copy.

  • The first year is assumed to be 2020.

  • Accumulators for 2020 come from the existing *_acc columns.

  • From 2021 onward, annual values from the condition columns are used for accumulation.

Examples

Given a MultiIndex of (region, building_code, year):

>>> out = accumulate_building_condition_rates(df)
>>> out.columns
Index(['small_measure', 'renovation', 'demolition',
       'small_measure_acc', 'renovation_acc', 'demolition_acc'], dtype='object')

The returned accumulator columns represent cumulative totals per (region, building_code) across increasing years.

freeze_scurves_from_year(s_curves: DataFrame, years: int | YearRange | tuple[int, int], condition_columns: list[str] | None = None) DataFrame[source]

Freeze building condition rate columns over a specified year range and resume the original trajectory after the freeze with a forward time shift.

This function operates on a MultiIndex DataFrame where the year is at index level 2. For the specified freeze period (inclusive), the values of the condition columns (as defined by list(BuildingCondition)) are set equal to their values at the first freeze year (start). For the years following the freeze period, the original (unfrozen) time series is continued but shifted forward by the length of the freeze, preserving the trajectory shape while delaying it in time.

Parameters

condition_columns : s_curves : pandas.DataFrame

Input DataFrame with a MultiIndex row index. The year must be at level 2 of the index. The DataFrame must contain one column for each member of BuildingCondition (e.g., an Enum listing condition rate columns).

yearsint or YearRange or tuple of (int, int)

Specification of the freeze period.

  • If YearRange: uses [years.start, years.end] (inclusive). start is clamped to the minimum year present in the DataFrame.

  • If int: interpreted as start=years (clamped to the minimum year) and end = max_year present in the DataFrame.

  • If (start, end) tuple: uses the inclusive range after validating that start <= end and clamping start to the minimum year.

Returns

pandas.DataFrame

A copy of s_curves where: - For all index keys, the condition columns in [start, end] are set to

their values at year == start (i.e., frozen).

  • For years > end, the original (unfrozen) values are applied but shifted forward by (end - start) years to avoid a discontinuity.

Raises

ValueError

If years is not a supported type (YearRange, int, or 2-tuple of int with start <= end).

Notes

  • The function assumes the year is at index level 2 and uses pd.IndexSlice[:, :, ...] to select by year range.

  • Only columns listed in list(BuildingCondition) are modified; other columns remain unchanged.

  • The freezing is inclusive of both start and end years.

  • Post-freeze continuation uses:

    post_freeze_rates.shift(end - start).iloc[1:]

    to align the shifted series; this implicitly drops the first shifted row to guard against misalignment. If you require stricter alignment guarantees, consider reindexing by explicit year keys instead of relying on .iloc[1:].

Examples

Freeze from a specific year to the end of the dataset:

>>> df_frozen = freeze_scurves_from_year(s_curves, 2022)

Freeze across an explicit range:

>>> df_frozen = freeze_scurves_from_year(s_curves, (2021, 2023))

Freeze using a YearRange object:

>>> rng = YearRange(start=2020, end=2022)
>>> df_frozen = freeze_scurves_from_year(s_curves, rng)
main() None[source]