File size: 3,057 Bytes
9435413
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e743fc5
9435413
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import plotly.graph_objects as go
import pandas as pd
import numpy as np

def build_figure(dataframe: pd.DataFrame) -> go.Figure:
    fig = go.Figure()

    # Condition 1
    fig.add_bar(
        y=dataframe["Condition 1"],
        x=dataframe["CASRN"],
        name="Condition 1",
        orientation="v",
        error_y=dict(
            type="data",
            symmetric=False,
            array=dataframe["Cond1_err_plus"],  # +x direction
            arrayminus=dataframe["Cond1_err_minus"],  # -x direction
            visible=True,
        ),
        marker=dict(color="red"),
    )

    # Condition 2
    fig.add_bar(
        y=dataframe["Condition 2"],
        x=dataframe["CASRN"],
        name="Condition 2",
        orientation="v",
        error_y=dict(
            type="data",
            symmetric=False,
            array=dataframe["Cond2_err_plus"],
            arrayminus=dataframe["Cond2_err_minus"],
            visible=True,
        ),
        marker=dict(color="blue"),
    )

    #fig.add_hline(
    #    y=0.9,
    #    line_width=3,
    #    line_color="green",
    #    line_dash="longdash"
    #)

    # --- Compute data-driven x-range (include error bars) ---
    x1 = dataframe["Condition 1"].to_numpy(dtype=float)
    x2 = dataframe["Condition 2"].to_numpy(dtype=float)

    # --- Compute upper bounds including error bars ---
    x1_max = x1 - dataframe["Cond1_err_minus"].to_numpy(dtype=float)
    x2_max = x2 - dataframe["Cond2_err_minus"].to_numpy(dtype=float)

    # Piecewise maxima across the two conditions (array length = number of CASRN)
    piecewise_max = np.maximum(x1_max, x2_max)

    # Minimum of the piecewise maxima array (ignoring NaNs)
    min_of_piecewise_max = np.nanmin(piecewise_max)

    # Fallback if everything is NaN or non-finite
    if not np.isfinite(min_of_piecewise_max):
        min_of_piecewise_max = 1.0

    # Minimum y = 3 orders of magnitude below that
    y_min = float(min_of_piecewise_max * 1e-1)

    # Ensure strictly positive for log scale
    y_min = float(np.maximum(y_min, np.nextafter(0.0, 1.0)))

    fig.update_layout(
        barmode="group",
        legend=dict(
            orientation="h",
            x=0.5,
            xanchor="center",
            y=1.08,
            font=dict(size=18)  # ← legend text size
        ),
        yaxis=dict(
            title=dict(
                text=r"Fraction released",
                font=dict(size=18)  # ← axis label size
            ),
            type="log",
            range=[np.log10(y_min), np.log10(1.)]
        ),
        xaxis=dict(
            title=dict(
                text=r"Representative boundary chemicals (CAS)",
                standoff=0,  # adjust: try 0–10 depending on how close you want it
                font = dict(size=18)  # ← axis label size
            )
        ),
        margin=dict(l=50, r=50, t=20, b=20),
        autosize=False,
        height=600,
        width=800,
    )

    fig.update_xaxes(tickfont=dict(size=14))
    fig.update_yaxes(tickfont=dict(size=14))

    return fig