File size: 12,645 Bytes
48c283c
 
0f4785f
 
 
 
4dd63ab
 
2f95965
 
 
 
 
 
 
 
 
 
 
 
4dd63ab
 
8f60513
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4dd63ab
 
 
4111c89
4dd63ab
 
 
 
 
 
 
 
 
 
8f60513
 
 
 
 
 
4dd63ab
 
 
b27e6a4
4dd63ab
 
 
455ed4c
 
 
 
 
 
4dd63ab
455ed4c
b27e6a4
4dd63ab
b27e6a4
0d99249
b27e6a4
 
db2b096
455ed4c
b27e6a4
 
455ed4c
0f4785f
b27e6a4
 
4dd63ab
455ed4c
4dd63ab
 
 
 
 
 
b27e6a4
0f4785f
b27e6a4
4dd63ab
b27e6a4
0f4785f
455ed4c
b27e6a4
455ed4c
b27e6a4
 
 
455ed4c
b27e6a4
 
 
 
48c283c
 
b27e6a4
455ed4c
 
b27e6a4
48c283c
b27e6a4
455ed4c
 
b27e6a4
0f4785f
bf0cef3
9ab38ae
094d1d2
 
 
 
 
 
 
d0ee391
 
 
 
 
 
bf0cef3
094d1d2
 
 
 
d0ee391
 
 
 
 
 
 
8800587
 
d0ee391
 
8adbcfb
d0ee391
 
 
 
9141e76
 
 
39a0ac0
9141e76
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39a0ac0
 
9141e76
 
 
 
 
 
 
 
 
 
 
 
 
 
0f4785f
d0ee391
0f4785f
 
4dd63ab
5edba5d
 
 
 
 
0e2051f
 
 
 
2f95965
0e2051f
b93eca7
 
 
 
 
 
 
 
 
0e2051f
53461be
12f2cdc
 
 
 
 
 
4dd63ab
5edba5d
 
 
 
 
 
 
80f8ca3
5edba5d
392d9ef
4dd63ab
 
2f95965
0d99249
392d9ef
4dd63ab
392d9ef
 
 
 
54d9d0f
392d9ef
5edba5d
 
8800587
d0ee391
 
392d9ef
b93eca7
8f60513
 
 
0f4785f
331fa49
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
#file_path = "cleaned_bmd_medication_data.xlsx"

import streamlit as st
import pandas as pd
import plotly.graph_objs as go

# Constants from linear regression
REGRESSION_CONSTANTS = {
    'Femoral Neck': {
        'Female': {'mu': 0.916852, 'sigma': 0.120754},
        'Male': {'mu': 0.9687385325352573, 'sigma': 0.121870698023835}
    },
    'Total Hip': {
        'Female': {'mu': 0.955439, 'sigma': 0.125406},
        'Male': {'mu': 0.967924895046735, 'sigma': 0.13081439619361657}
    },
    'Lumbar spine (L1-L4)': {
        'Female': {'mu': 1.131649, 'sigma': 0.139618},
        'Male': {'mu': 1.1309707991669353, 'sigma': 0.1201836924980611}
    }
}

# References content
REFERENCES = [
    "Cosman F, Crittenden DB, Ferrari S, et al. FRAME Study: The Foundation Effect of Building Bone With 1 Year of Romosozumab Leads to Continued Lower Fracture Risk After Transition to Denosumab. J Bone Miner Res. 2018;33(7):1219-1226. doi:10.1002/jbmr.3427",
    "McClung MR, Brown JP, Diez-Perez A, et al. Effects of 24 months of treatment with romosozumab followed by 12 months of denosumab or placebo in postmenopausal women with low bone mineral density: a randomized, double-blind, phase 2, parallel group study. J Bone Miner Res. 2018;33(8):1397–1406.",
    "McClung MR, San Martin J, Miller PD, et al. Opposite bone remodeling effects of teriparatide and alendronate in increasing bone mass [published correction appears in Arch Intern Med. 2005 Oct 10;165(18):2120]. Arch Intern Med. 2005;165(15):1762-1768. doi:10.1001/archinte.165.15.1762",
    "Black DM, Schwartz AV, Ensrud KE, et al. Effects of continuing or stopping alendronate after 5 years of treatment: the Fracture Intervention Trial Long-term Extension (FLEX): a randomized trial. JAMA. 2006;296(24):2927-2938. doi:10.1001/jama.296.24.2927",
    "Black, D.M., et al., Randomised trial of effect of alendronate on risk of fracture in women with existing vertebral fractures. Fracture Intervention Trial Research Group. Lancet, 1996. 348(9041): p. 1535-41.",
    "Harris, S.T., et al., Effects of risedronate treatment on vertebral and nonvertebral fractures in women with postmenopausal osteoporosis: a randomized controlled trial. Vertebral Efficacy With Risedronate Therapy (VERT) Study Group. JAMA, 1999. 282(14): p. 1344-52.",
    "McClung, M.R., et al., Effect of risedronate on the risk of hip fracture in elderly women. Hip Intervention Program Study Group. N Engl J Med, 2001. 344(5): p. 333-40.",
    "Reginster, JY., Minne, H., Sorensen, O. et al. Randomized Trial of the Effects of Risedronate on Vertebral Fractures in Women with Established Postmenopausal Osteoporosis. Osteoporos Int 11, 83–91 (2000).",
    "Chesnut CH 3rd, Skag A, Christiansen C, et al. Effects of oral ibandronate administered daily or intermittently on fracture risk in postmenopausal osteoporosis. J Bone Miner Res. 2004;19(8):1241-1249. doi:10.1359/JBMR.040325",
    "10 years of denosumab treatment in postmenopausal women with osteoporosis: results from the phase 3 randomised FREEDOM trial and open-label extension Bone, Henry G et al. The Lancet Diabetes & Endocrinology, Volume 5, Issue 7, 513 - 523.",
    "Ste-Marie LG, Sod E, Johnson T, Chines A. Five years of treatment with risedronate and its effects on bone safety in women with postmenopausal osteoporosis. Calcif Tissue Int. 2004;75(6):469-476. doi:10.1007/s00223-004-0039-7."
]

# Load medication data
@st.cache_data
def load_medication_data():
    file_path = "cleaned_bmd_medication_data.xlsx"
    return pd.read_excel(file_path)

# Calculate predicted BMD after medication
def calculate_bmd(bmd, percentage_increase):
    return bmd * (1 + percentage_increase)

# Convert BMD to T-score
def calculate_tscore(bmd, mu, sigma):
    return (bmd - mu) / sigma

# Display References
def display_references():
    with st.expander("References"):
        for i, ref in enumerate(REFERENCES, start=1):
            st.markdown(f"{i}. {ref}")

# Generate prediction table for all drugs
def generate_predictions(medication_data, site, bmd, mu, sigma):
    site_data = medication_data[medication_data['Site'] == site]
    all_results = []
    
    for _, row in site_data.iterrows():
        drug = row['Medication']
        predictions = {
            'Year': ['0'], 
            'Year Index': [0],  # Numeric x-axis for plotting
            'Predicted BMD': [round(bmd, 3)], 
            'Predicted T-score': [round(calculate_tscore(bmd, mu, sigma), 1)]
        }
        
        year_index = 1
        for year in row.index[1:-1]:  # Skip 'Medication' and 'Site' columns
            if not pd.isna(row[year]):
                percentage_increase = row[year]
                predicted_bmd = bmd * (1 + percentage_increase)
                predicted_tscore = calculate_tscore(predicted_bmd, mu, sigma)
                
                predictions['Year'].append(year.replace(" Year", ""))  # Simplify year label
                predictions['Year Index'].append(year_index)  # Numeric x-axis
                predictions['Predicted BMD'].append(round(predicted_bmd, 3))
                predictions['Predicted T-score'].append(round(predicted_tscore, 1))
                year_index += 1
        
        all_results.append({'Drug': drug, 'Predictions': predictions})
    return all_results


# Display results as table and plots
def display_results(predictions, site):
    st.subheader(f"Predictions for {site}")
    
    for result in predictions:
        drug = result['Drug']
        predictions = result['Predictions']
        
        # Display table
        st.write(f"### {drug}")
        st.dataframe(pd.DataFrame(predictions))
        
        # Plot BMD and T-score using Year Index
        bmd_plot = go.Scatter(
            x=predictions['Year Index'], y=predictions['Predicted BMD'], mode='lines+markers',
            name='Predicted BMD', line=dict(color='blue')
        )
        tscore_plot = go.Scatter(
            x=predictions['Year Index'], y=predictions['Predicted T-score'], mode='lines+markers',
            name='Predicted T-score', line=dict(color='green')
        )
        
        # Combine plots in a single row
        col1, col2 = st.columns(2)
        with col1:
            st.plotly_chart(go.Figure(data=[bmd_plot], layout=go.Layout(
                title=f"{drug} - Predicted BMD", xaxis_title="Year", yaxis_title="BMD (g/cm²)",
                xaxis=dict(tickmode='array', tickvals=predictions['Year Index'], ticktext=predictions['Year'])
            )))
        with col2:
            st.plotly_chart(go.Figure(data=[tscore_plot], layout=go.Layout(
                title=f"{drug} - Predicted T-score", xaxis_title="Year", yaxis_title="T-score",
                xaxis=dict(tickmode='array', tickvals=predictions['Year Index'], ticktext=predictions['Year'])
            )))

# Generate summary of medications reaching the target T-score
def generate_goal_summary(predictions, target_tscore=-2.4):
    def year_to_int(year):
        # Convert "1st", "2nd", "3rd", etc., to numeric values
        try:
            return int(year.rstrip("stndrdth"))  # Remove suffixes like "st", "nd", "rd", "th"
        except ValueError:
            return 0  # Default to 0 if year cannot be converted

    goal_reached = []
    
    for result in predictions:
        drug = result['Drug']
        predictions_data = result['Predictions']
        
        for year, tscore in zip(predictions_data['Year'], predictions_data['Predicted T-score']):
            if tscore >= target_tscore:
                # Convert year to an integer using helper function
                numeric_year = year_to_int(year)
                goal_reached.append({'Medication': drug, 'Year': numeric_year})
                break  # Stop checking further years for this drug

    # Sort by year to prioritize earlier achievement
    goal_reached_sorted = sorted(goal_reached, key=lambda x: x['Year'])
    return goal_reached_sorted

# Display summary of goal-reaching medications
def display_goal_summary(goal_summary, target_tscore):
    st.subheader(f"Goal Treatment Summary (T-score ≥ {target_tscore:.1f})")
    
    if not goal_summary:
        st.info("No medications reach the target T-score.")
    else:
        summary_table = pd.DataFrame(goal_summary)
        st.table(summary_table)

# Medication Selection with Collapsible Categories
def select_medications():
    st.subheader("Select Medications to Display")
    show_all = st.checkbox("Show All Medications", key="show_all")
    
    selected_medications = []
    if not show_all:
        # Define categories and medications
        categories = {
            "Bisphosphonates": [
                "Alendronate", "Risedronate", "Ibandronate oral", 
                "Zoledronate", "Ibandronate IV (3mg)"
            ],
            "RANK Ligand Inhibitors": [
                "Denosumab", "Denosumab + Teriparatide"
            ],
            "Anabolic Agents": [
                "Teriparatide", "Teriparatide + Denosumab"
            ],
            "Sclerostin Inhibitors": [
                "Romosozumab", "Romosozumab + Denosumab", 
                "Romosozumab + Alendronate", "Romosozumab + Ibandronate", 
                "Romosozumab + Zoledronate"
            ]
        }
        
        # Create collapsible sections
        for category, medications in categories.items():
            with st.expander(category):
                for med in medications:
                    # Use a unique key for each checkbox
                    if st.checkbox(med, key=f"{category}_{med}"):
                        selected_medications.append(med)
    else:
        # Include all medications if "Show All" is selected
        selected_medications = [
            "Alendronate", "Risedronate", "Ibandronate oral", 
            "Zoledronate", "Ibandronate IV (3mg)", "Denosumab", 
            "Denosumab + Teriparatide", "Teriparatide", 
            "Teriparatide + Denosumab", "Romosozumab", 
            "Romosozumab + Denosumab", "Romosozumab + Alendronate", 
            "Romosozumab + Ibandronate", "Romosozumab + Zoledronate"
        ]
    
    return selected_medications

# Streamlit UI
# Main function
def main():
    st.title("BMD and T-score Prediction Tool")
    
    # Add a descriptive line for the tool
    st.markdown(
        "### A tool for initiating and evaluating osteoporosis treatment to achieve individualized T-score goals."
    )
    
    # DEXA Machine Selection
    dexa_machine = st.selectbox("DEXA Machine", ["LUNAR"])
    
    # Gender Selection
    gender = st.selectbox("Gender", ["Female", "Male"])
    
    # Location (Site) Selection with Mapping
    site_mapping = {
        'Lumbar spine (L1-L4)': 'LS',
        'Femoral Neck': 'FN',
        'Total Hip': 'TH'
    }
    site_options = list(site_mapping.keys())
    selected_site = st.selectbox("Select Region (Site)", site_options)
    site = site_mapping[selected_site]  # Map to the actual value in the dataset
    
    # Input patient data
    bmd_patient = st.number_input(
        "Initial BMD",
        min_value=0.000, max_value=2.000,
        value=0.800, step=0.001,
        format="%.3f"
    )
    
    # Input for personalized T-score goal
    treatment_goal_tscore = st.number_input(
        "Targeted T-score Goal (default: -2.4):",
        min_value=-2.4, max_value=-1.0, step=0.1, value=-2.4,
        format="%.1f"
    )
    
    # Medication Selection
    selected_medications = select_medications()
    
    # Load constants and medication data
    medication_data = load_medication_data()
    constants = REGRESSION_CONSTANTS[selected_site][gender]

    # Generate and display predictions for selected medications
    if st.button("Predict"):
        all_predictions = generate_predictions(medication_data, site, bmd_patient, constants['mu'], constants['sigma'])
        filtered_predictions = [pred for pred in all_predictions if pred['Drug'] in selected_medications]
        
        if not filtered_predictions:
            st.warning("No medications selected. Please select at least one medication or use the 'Show All' option.")
        else:
            # Generate and display goal treatment summary using the specified T-score goal
            goal_summary = generate_goal_summary(filtered_predictions, target_tscore=treatment_goal_tscore)
            display_goal_summary(goal_summary, target_tscore=treatment_goal_tscore)
            
            # Display individual medication results
            display_results(filtered_predictions, selected_site)

    # References always visible at the bottom
    display_references()
    
if __name__ == "__main__":
    main()