def test_household_waterfall_chart(situation, reform):
    baseline = situation(IndividualSim())
    reformed = situation(IndividualSim(reform))
    baseline.vary("employment_income")
    reformed.vary("employment_income")
    household_waterfall_chart((reform, ), ["Reform"], situation, baseline,
                              reformed)
Beispiel #2
0
def situation_reform():
    start_time = time()
    app.logger.info("Situation reform request received")
    params = {**request.args, **(request.json or {})}
    request_id = "situation-" + dict_to_string(params) + "-" + VERSION
    blob = bucket.blob(request_id + ".json")
    if blob.exists() and USE_CACHE:
        app.logger.info("Returning cached response")
        result = json.loads(blob.download_as_string())
        return result
    app.logger.info("Creating situation")
    situation = create_situation(params)
    app.logger.info("Creating reform")
    reform, subreform_labels = create_reform(params, return_names=True)
    baseline_config = use_current_parameters(), add_LVT()
    reform_config = use_current_parameters(), reform
    app.logger.info("Creating baseline individualsim")
    baseline = situation(IndividualSim(baseline_config, year=2021))
    app.logger.info("Creating reform individualsim")
    reformed = situation(IndividualSim(reform_config, year=2021))
    app.logger.info("Headline figures")
    headlines = headline_figures(baseline, reformed)
    app.logger.info("waterfall")
    waterfall = household_waterfall_chart(reform, subreform_labels, situation,
                                          baseline, reformed)
    app.logger.info("Varying baseline")
    baseline.vary("employment_income", step=10)
    app.logger.info("Varying reform")
    reformed.vary("employment_income", step=10)
    app.logger.info("Budget")
    budget = budget_chart(baseline, reformed)
    app.logger.info("mtr chart")
    mtr = mtr_chart(baseline, reformed)
    del situation
    del reform
    del baseline
    del reformed
    result = dict(
        **headlines,
        waterfall_chart=waterfall,
        budget_chart=budget,
        mtr_chart=mtr,
    )
    if USE_CACHE:
        blob.upload_from_string(json.dumps(result))
    gc.collect()
    duration = time() - start_time
    app.logger.info(f"Situation reform completed ({round(duration, 2)}s)")
    return result
Beispiel #3
0
def mtr_chart(baseline: IndividualSim, reformed: IndividualSim) -> str:
    """Produces line chart with employment income on the x axis and marginal
    tax rate on the y axis, for baseline and reform simulations.
    :param baseline: Baseline simulation.
    :type baseline: IndividualSim
    :param reformed: Reform simulation.
    :type reformed: IndividualSim
    :return: Representation of the marginal tax rate plotly chart as a JSON
        string.
    :rtype: str
    """
    earnings = baseline.calc("employment_income").sum(axis=0)
    baseline_net = baseline.calc("net_income").sum(axis=0)
    reform_net = reformed.calc("net_income").sum(axis=0)

    def get_mtr(x, y):
        return 1 - ((y[1:] - y[:-1]) / (x[1:] - x[:-1]))

    baseline_mtr = get_mtr(earnings, baseline_net)
    reform_mtr = get_mtr(earnings, reform_net)
    df = pd.DataFrame(
        {
            "Employment income": earnings[:-1],
            "Baseline": baseline_mtr,
            "Reform": reform_mtr,
        }
    )
    graph = px.line(
        df,
        x="Employment income",
        y=["Baseline", "Reform"],
        labels={"variable": "Policy", "value": "Effective MTR"},
        color_discrete_map=COLOR_MAP,
        line_shape="hv",
    )
    return json.loads(
        format_fig(graph, show=False)
        .update_layout(
            title="Effective marginal tax rate by employment income",
            xaxis_title="Employment income",
            xaxis_tickprefix="£",
            yaxis_tickformat="%",
            yaxis_title="Effective MTR",
            legend_title=None,
        )
        .to_json()
    )
Beispiel #4
0
def budget_chart(baseline: IndividualSim, reformed: IndividualSim) -> str:
    """Produces line chart with employment income on the x axis and net income
    on the y axis, for baseline and reform simulations.
    :param baseline: Baseline simulation.
    :type baseline: IndividualSim
    :param reformed: Reform simulation.
    :type reformed: IndividualSim
    :return: Representation of the budget plotly chart as a JSON string.
    :rtype: str
    """
    df = pd.DataFrame(
        {
            "Employment income": baseline.calc("employment_income").sum(
                axis=0
            ),
            "Baseline": baseline.calc("net_income").sum(axis=0),
            "Reform": reformed.calc("net_income").sum(axis=0),
        }
    )
    graph = px.line(
        df,
        x="Employment income",
        y=["Baseline", "Reform"],
        labels={"variable": "Policy", "value": "Net income"},
        color_discrete_map=COLOR_MAP,
    )
    return json.loads(
        format_fig(graph, show=False)
        .update_layout(
            title="Net income by employment income",
            xaxis_title="Employment income",
            yaxis_title="Household net income",
            yaxis_tickprefix="£",
            xaxis_tickprefix="£",
            legend_title=None,
        )
        .to_json()
    )
def budget_chart(baseline: IndividualSim, reformed: IndividualSim) -> str:
    """Produces line chart with employment income on the x axis and net income
    on the y axis, for baseline and reform simulations.
    :param baseline: Baseline simulation.
    :type baseline: IndividualSim
    :param reformed: Reform simulation.
    :type reformed: IndividualSim
    :return: Representation of the budget plotly chart as a JSON string.
    :rtype: str
    """
    variable_values = {}
    for explaining_variable in (
            "total_income",
            "tax",
            "benefits",
    ):
        variable_values[explaining_variable +
                        "_baseline"] = baseline.calc(explaining_variable).sum(
                            axis=0)
        variable_values[explaining_variable +
                        "_reform"] = reformed.calc(explaining_variable).sum(
                            axis=0)
    df = pd.DataFrame({
        "employment_income":
        baseline.calc("employment_income").sum(axis=0),
        "Baseline":
        baseline.calc("net_income").sum(axis=0),
        "Reform":
        reformed.calc("net_income").sum(axis=0),
        **variable_values,
    })
    df["hover"] = df.apply(
        lambda x: budget_hover_label(
            x.employment_income,
            x.Baseline,
            x.Reform,
            x.total_income_baseline,
            x.total_income_reform,
            x.tax_baseline,
            x.tax_reform,
            x.benefits_baseline,
            x.benefits_reform,
        ),
        axis=1,
    )
    fig = px.line(
        df.round(0),
        x="employment_income",
        y=["Baseline", "Reform"],
        labels=dict(LABELS, value="Net income"),
        color_discrete_map=COLOR_MAP,
        custom_data=["hover"],
    )
    charts.add_custom_hovercard(fig)
    fig.update_layout(
        title="Net income by employment income",
        xaxis_title="Employment income",
        yaxis_title="Household net income",
        yaxis_tickprefix="£",
        xaxis_tickprefix="£",
        legend_title=None,
    )
    return charts.formatted_fig_json(fig)
def mtr_chart(baseline: IndividualSim, reformed: IndividualSim) -> str:
    """Produces line chart with employment income on the x axis and marginal
    tax rate on the y axis, for baseline and reform simulations.
    :param baseline: Baseline simulation.
    :type baseline: IndividualSim
    :param reformed: Reform simulation.
    :type reformed: IndividualSim
    :return: Representation of the marginal tax rate plotly chart as a JSON
        string.
    :rtype: str
    """
    earnings = baseline.calc("employment_income").sum(axis=0)
    baseline_net = baseline.calc("net_income").sum(axis=0)
    reform_net = reformed.calc("net_income").sum(axis=0)

    def get_mtr(x, y):
        return 1 - ((y[1:] - y[:-1]) / (x[1:] - x[:-1]))

    baseline_mtr = get_mtr(earnings, baseline_net)
    reform_mtr = get_mtr(earnings, reform_net)
    variable_mtrs = {}
    for explaining_variable, inverted in zip(
        (
            "tax",
            "benefits",
        ),
        (False, True),
    ):
        baseline_values = baseline.calc(explaining_variable).sum(axis=0)
        reform_values = reformed.calc(explaining_variable).sum(axis=0)
        multiplier = 1 if inverted else -1
        addition = -1 if inverted else 1
        variable_mtrs[explaining_variable + "_baseline"] = (
            get_mtr(earnings, baseline_values) * multiplier + addition)
        variable_mtrs[explaining_variable + "_reform"] = (
            get_mtr(earnings, reform_values) * multiplier + addition)
    df = pd.DataFrame({
        "employment_income": earnings[:-1].round(0),
        "Baseline": baseline_mtr,
        "Reform": reform_mtr,
        **variable_mtrs,
    })
    df["hover"] = df.apply(
        lambda x: mtr_hover_label(
            x.employment_income,
            x.Baseline,
            x.Reform,
            x.tax_baseline,
            x.tax_reform,
            x.benefits_baseline,
            x.benefits_reform,
        ),
        axis=1,
    )
    fig = px.line(
        df,
        x="employment_income",
        y=["Baseline", "Reform"],
        labels=dict(LABELS, value="Effective MTR"),
        color_discrete_map=COLOR_MAP,
        line_shape="hv",
        custom_data=["hover"],
    )
    charts.add_custom_hovercard(fig)
    fig.update_layout(
        title="Effective marginal tax rate by employment income",
        xaxis_title="Employment income",
        xaxis_tickprefix="£",
        yaxis_tickformat="%",
        yaxis_title="Effective MTR",
        legend_title=None,
    )
    return charts.formatted_fig_json(fig)
def test_mtr_chart(situation, reform):
    baseline = situation(IndividualSim())
    reformed = situation(IndividualSim(reform))
    baseline.vary("employment_income")
    reformed.vary("employment_income")
    mtr_chart(baseline, reformed)