josh-dcosta commited on
Commit
6e35d3a
·
verified ·
1 Parent(s): b73bdfd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +224 -0
app.py CHANGED
@@ -0,0 +1,224 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import joblib
3
+ import os
4
+ import pandas as pd
5
+
6
+ # Dictionary containing the model names and corresponding pickle file names
7
+ model_paths = {
8
+ 'AdaBoost': 'pjas-thyroid-AdaBoost.pkl',
9
+ 'Decision Tree': 'pjas-thyroid-Decision Tree.pkl',
10
+ 'Gaussian Naive Bayes': 'pjas-thyroid-Gaussian Naive Bayes.pkl',
11
+ 'Gradient Boosting': 'pjas-thyroid-Gradient Boosting.pkl',
12
+ 'K-Nearest Neighbors': 'pjas-thyroid-K-Nearest Neighbors.pkl',
13
+ 'Logistic Regression': 'pjas-thyroid-Logistic Regression.pkl',
14
+ 'Random Forest': 'pjas-thyroid-Random Forest.pkl',
15
+ 'Support Vector Machine': 'pjas-thyroid-Support Vector Machine.pkl',
16
+ 'XGBoost': 'pjas-thyroid-XGBoost.pkl'
17
+ }
18
+
19
+ # Example: Hard-coded dictionary of accuracies per model.
20
+ model_accuracies = {
21
+ 'AdaBoost': 97.13,
22
+ 'Logistic Regression': 96.87,
23
+ 'Gaussian Naive Bayes': 95.3,
24
+ 'Gradient Boosting': 98.96,
25
+ 'Support Vector Machine': 96.34,
26
+ 'Decision Tree': 97.91,
27
+ 'K-Nearest Neighbors': 97.13,
28
+ 'Random Forest': 98.96,
29
+ 'XGBoost': 98.43
30
+ }
31
+
32
+ # We assume:
33
+ # 0 -> Cancer cannot recur
34
+ # 1 -> Cancer can recur
35
+
36
+ response_details = {
37
+ "0": """**Excellent Response**
38
+ Negative imaging studies and suppressed thyroglobulin levels
39
+ (below 0.2 ng/mL or stimulated Tg below 1 ng/mL).""",
40
+ "1": """**Indeterminate Response**
41
+ Nonspecific findings on imaging studies, making it difficult
42
+ to confidently classify as benign or malignant,
43
+ with potentially low thyroglobulin levels.""",
44
+ "2": """**Biochemical Incomplete**
45
+ Negative imaging but elevated thyroglobulin levels
46
+ (suppressed Tg above 1 ng/mL or stimulated Tg above 10 ng/mL)
47
+ or rising anti-Tg antibody levels.""",
48
+ "3": """**Structural Incomplete**
49
+ Presence of identifiable structural disease on imaging,
50
+ regardless of thyroglobulin level."""
51
+ }
52
+
53
+ def predict_cancer(age, gender, response, tumor_size, lymph_node_spread, focality):
54
+ """
55
+ Generates a Markdown table with emoji icons for each model's prediction,
56
+ including 'TumorSize', 'LymphNodeSpread', and 'Focality' as additional features.
57
+ """
58
+ # 1. Load your pre-fitted scaler from disk
59
+ scaler_file = "model/pjas-thyroid-scaler.pkl"
60
+ if not os.path.exists(scaler_file):
61
+ return "Error: Scaler file not found. Please check your path or name."
62
+
63
+ scaler = joblib.load(scaler_file)
64
+
65
+ # 2. Simple encodings for demonstration
66
+ gender_val = 0 if gender == "Female" else 1
67
+ response_val = int(response)
68
+ tumor_val = int(tumor_size) # Convert Tumor Size dropdown value to int
69
+ lymph_val = int(lymph_node_spread) # Convert Lymph Node Spread dropdown value to int
70
+ focality_val = int(focality) # Convert Focality dropdown value to int
71
+
72
+ # 3. Create a DataFrame for the features
73
+ features = pd.DataFrame({
74
+ 'Age': [age],
75
+ 'Gender': [gender_val],
76
+ 'T': [tumor_val],
77
+ 'N': [lymph_val],
78
+ 'Focality': [focality_val],
79
+ 'Response': [response_val]
80
+ })
81
+
82
+ # 4. Remove any NaN
83
+ features = features.dropna()
84
+
85
+ # 5. Scale the 'Age' column
86
+ # If you have more features to scale, include them here accordingly
87
+ features[['Age']] = scaler.transform(features[['Age']])
88
+
89
+ # 6. Sort models by accuracy (descending)
90
+ sorted_model_names = sorted(
91
+ model_paths.keys(),
92
+ key=lambda m: model_accuracies[m],
93
+ reverse=True
94
+ )
95
+
96
+ # 7. Build a Markdown table
97
+ table_header = (
98
+ "| **Model** | **Accuracy** | **Prediction** |\n"
99
+ "|-----------------------------|--------------|--------------------------------|\n"
100
+ )
101
+ table_rows = []
102
+
103
+ # 8. Emojis for predictions
104
+ can_recur_emoji = "🔴" # "Cancer can recur"
105
+ cannot_recur_emoji = "🟢" # "Cancer cannot-recur"
106
+
107
+ # 9. Iterate through each model and make predictions
108
+ for model_name in sorted_model_names:
109
+ pickle_file = model_paths[model_name]
110
+ model_file_path = os.path.join("model", pickle_file)
111
+
112
+ if not os.path.exists(model_file_path):
113
+ row = f"| {model_name} | N/A | **Error**: file not found |"
114
+ table_rows.append(row)
115
+ continue
116
+
117
+ model = joblib.load(model_file_path)
118
+ prediction = model.predict(features) # e.g., [0] or [1]
119
+ pred_value = prediction[0]
120
+
121
+ # 10. Convert numeric prediction to icon + text
122
+ if pred_value == 1:
123
+ pred_text = f"{can_recur_emoji} Sorry, Your Cancer can recur"
124
+ else:
125
+ pred_text = f"{cannot_recur_emoji} Great News! Your Cancer cannot-recur"
126
+
127
+ accuracy = model_accuracies.get(model_name, "N/A")
128
+ row = f"| {model_name} | {accuracy}% | {pred_text} |"
129
+ table_rows.append(row)
130
+
131
+ # 11. Combine into a single Markdown table
132
+ md_table = table_header + "\n".join(table_rows)
133
+ return md_table
134
+
135
+ def clear_md():
136
+ """Clears the Markdown output."""
137
+ return ""
138
+
139
+ with gr.Blocks() as demo:
140
+ gr.Markdown("# Thyroid Cancer Recurrence Predictor")
141
+
142
+ # Existing inputs
143
+ age_slider = gr.Slider(
144
+ minimum=1,
145
+ maximum=100,
146
+ step=1,
147
+ label="Age",
148
+ value=44,
149
+ interactive=True
150
+ )
151
+
152
+ gender_radio = gr.Radio(
153
+ choices=["Female", "Male"],
154
+ value="Female",
155
+ label="Gender",
156
+ interactive=True
157
+ )
158
+
159
+ # New Tumor Size dropdown with descriptive text
160
+ tumor_size_dropdown = gr.Dropdown(
161
+ choices=[
162
+ ("T1a (≤1 cm, confined to the thyroid)", "0"),
163
+ ("T1b (>1 cm and ≤2 cm, confined to the thyroid)", "1"),
164
+ ("T2 (>2 cm and ≤4 cm, confined to the thyroid)", "2"),
165
+ ("T3a (>4 cm, confined to the thyroid)", "3"),
166
+ ("T3b (Minimal extrathyroidal extension)", "4"),
167
+ ("T4a (Moderate extrathyroidal extension, operable)", "5"),
168
+ ("T4b (Extensive extrathyroidal extension, inoperable)", "6")
169
+ ],
170
+ value="0", # Default T1a
171
+ label="Tumor Size",
172
+ interactive=True
173
+ )
174
+
175
+ # New Lymph Node Spread dropdown with descriptive text
176
+ lymph_node_dropdown = gr.Dropdown(
177
+ choices=[
178
+ ("N0 (No spread to nearby lymph nodes)", "0"),
179
+ ("N1a (Spread to lymph nodes in the neck close to the thyroid)", "1"),
180
+ ("N1b (Spread to lymph nodes in the neck farther from the thyroid or upper chest)", "2")
181
+ ],
182
+ value="0", # Default N0
183
+ label="Lymph Node Spread",
184
+ interactive=True
185
+ )
186
+
187
+ # New Focality of Differential Thyroid Cancer dropdown with descriptive text
188
+ focality_dropdown = gr.Dropdown(
189
+ choices=[
190
+ ("Uni-focal (Single focus of thyroid cancer)", "1"),
191
+ ("Multi-focal (Multiple foci of thyroid cancer)", "0")
192
+ ],
193
+ value="1", # Default Uni-focal
194
+ label="Focality of Differential Thyroid Cancer",
195
+ interactive=True
196
+ )
197
+
198
+ response_dropdown = gr.Dropdown(
199
+ choices=[
200
+ ("✅ Excellent Response - Negative imaging studies and suppressed thyroglobulin levels (below 0.2 ng/mL or stimulated Tg below 1 ng/mL)", "0"),
201
+ ("❓ Indeterminate Response - Nonspecific findings on imaging studies with potentially low thyroglobulin levels", "1"),
202
+ ("⚠️ Biochemical Incomplete - Negative imaging but elevated thyroglobulin levels or rising anti-Tg antibody levels", "2"),
203
+ ("❌ Structural Incomplete - Presence of identifiable structural disease on imaging, regardless of thyroglobulin level", "3")
204
+ ],
205
+ value="0", # Default
206
+ label="Response",
207
+ interactive=True
208
+ )
209
+
210
+ # Predict button
211
+ predict_button = gr.Button(
212
+ value="Predict",
213
+ variant="primary"
214
+ )
215
+ prediction_output = gr.Markdown(label="Prediction Results")
216
+
217
+ # Include all dropdowns as inputs for the predict function
218
+ predict_button.click(
219
+ fn=predict_cancer,
220
+ inputs=[age_slider, gender_radio, response_dropdown, tumor_size_dropdown, lymph_node_dropdown, focality_dropdown],
221
+ outputs=prediction_output
222
+ )
223
+
224
+ demo.launch()