File size: 3,866 Bytes
9253c67
 
 
 
 
 
fa58ff0
 
 
 
 
 
 
 
 
9253c67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fa58ff0
 
 
 
 
 
 
 
 
9253c67
 
 
 
 
fa58ff0
 
 
 
 
9253c67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
637bd04
fa58ff0
637bd04
 
 
 
 
fa58ff0
 
 
 
 
 
 
637bd04
9253c67
637bd04
 
 
 
 
9253c67
 
 
fa58ff0
 
 
 
 
 
 
 
 
 
 
9253c67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import pandas as pd
import streamlit as st
from config.config import CRRA_COLORS, CRRA_FONT, PLOTLY_TEMPLATE


def apply_chart_style(fig, height=None):
    """Apply the standard CRRA theme (font, colours, margins) to a Plotly figure.

    Args:
        fig: Plotly figure object.
        height (int | None): optional fixed height in pixels.

    Returns:
        fig: the same figure with updated layout.
    """
    fig.update_layout(
        template=PLOTLY_TEMPLATE,
        font=dict(family=CRRA_FONT, size=12, color=CRRA_COLORS["black"]),
        margin=dict(t=40, b=40, l=40, r=20),
        legend=dict(
            orientation="v",
            yanchor="middle", y=0.5,
            xanchor="left", x=1.02,
        ),
    )
    if height:
        fig.update_layout(height=height)
    return fig


def register_chart_data(name: str, df: pd.DataFrame):
    """Store a chart's underlying DataFrame in the session-level registry for bulk export.

    Args:
        name (str): unique chart label used as the dict key and export sheet name.
        df (pd.DataFrame): data behind the chart.

    Returns:
        None
    """
    st.session_state.setdefault("chart_data_registry", {})
    st.session_state["chart_data_registry"][name] = df.copy()


def show_bar():
    """Render the CRRA four-colour decorative bar below page headings.

    Returns:
        None
    """
    st.markdown(
        """
        <div class="et_pb_module">
            <div style="display:flex;width:100%;height:12px;margin:0 auto 0 0;">
                <span style="flex:1;background:#FF5F4D;"></span>
                <span style="flex:1;background:#A0A0A0;"></span>
                <span style="flex:1;background:#D9E210;"></span>
                <span style="flex:1;background:#55C95B;"></span>
            </div>
        </div>
        """,
        unsafe_allow_html=True,
    )


def smart_display_format(val):
    """Format a numeric value showing all significant digits, no rounding.

    Uses repr() which gives the shortest decimal string that round-trips
    exactly to the same float — avoids floating-point noise like
    462961.099999999976717 when the value is actually 462961.1.
    Falls back to fixed-decimal notation when repr() produces scientific
    notation (very small numbers such as 1e-11).

    Args:
        val (float): numeric value to format.

    Returns:
        str: formatted string, or "" for near-zero values.
    """
    if abs(val) < 1e-14:
        return ""
    s = repr(val)
    if "e" not in s.lower():
        return s
    # Scientific notation → convert to fixed decimal (e.g. 1e-11 → 0.00000000001)
    return f"{val:.15f}".rstrip("0").rstrip(".")


def format_efficiency_summary(df: pd.DataFrame, df_default: pd.DataFrame):
    """Build a styled Pandas Styler for the resource-efficiency coefficient summary table.

    Cells whose value differs from the default median are highlighted in red.

    Args:
        df (pd.DataFrame): current coefficient values (methods × resources).
        df_default (pd.DataFrame): default median values for the same shape.

    Returns:
        pandas.io.formats.style.Styler: styled dataframe ready for st.dataframe().
    """
    df_numeric = df.copy()
    df_display = df_numeric.map(smart_display_format)

    def highlight_changes(data):
        styled = pd.DataFrame('', index=data.index, columns=data.columns)
        for m in data.index:
            for r in data.columns:
                try:
                    current = df_numeric.at[m, r]
                    default = df_default.at[m, r]
                    if abs(current - default) > 1e-6:
                        styled.at[m, r] = 'background-color: #ff5f4d'
                except:
                    continue
        return styled

    return (
        df_display
        .style
        .apply(highlight_changes, axis=None)
    )