Skip to content

Implement outlier code and tests #296

@AdamFinkle

Description

@AdamFinkle

We removed this code that would help support derived outliers, should they be chosen to be implemented in a later version.

test_engine.py

@pytest.fixture()
def sample_intermediate_energy_bill_inputs_with_outlier() -> (
    list[engine.IntermediateEnergyBill]
):
    intermediate_energy_bill_inputs = [
        engine.IntermediateEnergyBill(
            _dummy_processed_energy_bill_input,
            [41.7, 41.6, 32, 25.4],
            60,
            AnalysisType.ALLOWED_HEATING_USAGE,
            True,
            False,
        ),
        engine.IntermediateEnergyBill(
            _dummy_processed_energy_bill_input,
            [28, 29, 30, 29],
            50,
            AnalysisType.ALLOWED_HEATING_USAGE,
            True,
            False,
        ),
        engine.IntermediateEnergyBill(
            _dummy_processed_energy_bill_input,
            [32, 35, 35, 38],
            45,
            AnalysisType.ALLOWED_HEATING_USAGE,
            True,
            False,
        ),
        engine.IntermediateEnergyBill(
            _dummy_processed_energy_bill_input,
            [41, 43, 42, 42],
            30,
            AnalysisType.ALLOWED_HEATING_USAGE,
            True,
            False,
        ),
        engine.IntermediateEnergyBill(
            _dummy_processed_energy_bill_input,
            [72, 71, 70, 69],
            0.96,
            AnalysisType.NOT_ALLOWED_IN_CALCULATIONS,
            False,
            False,
        ),
    ]

    return intermediate_energy_bill_inputs

...

def test_bp_ua_with_outlier(
    sample_heat_load_inputs, sample_intermediate_energy_bill_inputs_with_outlier
):
    home = engine.Home.calculate(
        sample_heat_load_inputs,
        sample_intermediate_energy_bill_inputs_with_outlier,
        dhw_input=None,
        initial_balance_point=58,
    )

    # expect that ua_1 is considered an outlier and not used in winter_processed_energy_bills
    ua_2, ua_3, ua_4 = [bill.ua for bill in home.winter_processed_energy_bills]

    assert home.balance_point == 60.5
    assert ua_2 == approx(1455.03, abs=0.01)
    assert ua_3 == approx(1617.65, abs=0.01)
    assert ua_4 == approx(1486.49, abs=0.01)
    assert home.avg_ua == approx(1519.72, abs=1)
    assert home.stdev_pct == approx(0.0463, abs=0.01)

engine.py

        while new_stdev_pct > stdev_pct_max:
            outliers = [
                abs(bill.ua - avg_ua)
                for bill in winter_processed_energy_bills
                if bill.ua is not None
            ]
            biggest_outlier = max(outliers)
            biggest_outlier_idx = outliers.index(biggest_outlier)
            outlier = winter_processed_energy_bills.pop(
                biggest_outlier_idx
            )  # removes the biggest outlier
            outlier.eliminated_as_outlier = True
            uas_i = [
                processed_energy_bill.ua
                for processed_energy_bill in winter_processed_energy_bills
                if processed_energy_bill.ua is not None
            ]
            avg_ua_i = sts.mean(uas_i)
            stdev_pct_i = sts.pstdev(uas_i) / avg_ua_i
            if (
                # the outlier has been removed
                new_stdev_pct - stdev_pct_i
                < max_stdev_pct_diff
            ):  # if it's a small enough change
                # add the outlier back in
                winter_processed_energy_bills.append(
                    outlier
                )  # then it's not worth removing it, and we exit
                outlier.eliminated_as_outlier = False
                break  # may want some kind of warning to be raised as well
            else:
                uas, avg_ua, stdev_pct = uas_i, avg_ua_i, stdev_pct_i

            results = Home._refine_balance_point(
                balance_point=balance_point,
                balance_point_sensitivity=next_balance_point_sensitivity,
                avg_ua=avg_ua,
                stdev_pct=stdev_pct,
                thermostat_set_point=thermostat_set_point,
                winter_processed_energy_bills=winter_processed_energy_bills,
            )

            new_balance_point = results.balance_point
            new_avg_ua = results.avg_ua
            new_stdev_pct = results.stdev_pct
            balance_point_graph_records_extension = (
                results.balance_point_graph_records_extension
            )

            if isinstance(balance_point_graph_records_extension, list):
                balance_point_graph.records.extend(
                    balance_point_graph_records_extension
                )

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions