Spaces:
Sleeping
(FEAT)[Add Spectrum Modality Validation in Input]:
Browse files- Integrated `validate_spectrum_modality` to check if input spectrum matches selected modality.
- Added warnings and user confirmation for modality-spectrum mismatch in `render_input_column`.
- Users can stop or continue with mismatched data.
(FEAT)[Spectrum Modality Validation in Comparison Tab]:
- Added validation before comparison analysis; displays errors if modality does not match spectrum.
- Prevents further processing upon validation failure.
(REFACTOR)[Independent Modality State for Comparison Tab]:
- Changed modality selector key to "`comparison_tab_modality`" for isolated state.
- Avoids unintended overwrites of main modality session state.
(FIX)[Graceful Handling of Empty Comparison Data]:
- Added warnings for missing confidence/performance data in comparison tab.
- Visualization/stats are now only shown when data is present.
- modules/ui_components.py +139 -98
@@ -27,7 +27,7 @@ from core_logic import (
|
|
27 |
)
|
28 |
from utils.results_manager import ResultsManager
|
29 |
from utils.multifile import process_multiple_files
|
30 |
-
from utils.preprocessing import resample_spectrum
|
31 |
from utils.confidence import calculate_softmax_confidence
|
32 |
|
33 |
|
@@ -406,6 +406,30 @@ def render_input_column():
|
|
406 |
else:
|
407 |
try:
|
408 |
x_raw, y_raw = parse_spectrum_data(st.session_state["input_text"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
409 |
x_resampled, y_resampled = resample_spectrum(x_raw, y_raw, TARGET_LEN)
|
410 |
st.session_state.update(
|
411 |
{
|
@@ -932,18 +956,10 @@ def render_results_column():
|
|
932 |
|
933 |
##### Supported Data Format
|
934 |
|
935 |
-
- **File Type:**
|
936 |
- **Content:** Must contain two columns: `wavenumber` and `intensity`.
|
937 |
- **Separators:** Values can be separated by spaces or commas.
|
938 |
- **Preprocessing:** Your spectrum will be automatically resampled to 500 data points to match the model's input requirements.
|
939 |
-
|
940 |
-
---
|
941 |
-
|
942 |
-
##### Example Applications
|
943 |
-
- 🔬 Research on polymer degradation
|
944 |
-
- ♻️ Recycling feasibility assessment
|
945 |
-
- 🌱 Sustainability impact studies
|
946 |
-
- 🏭 Quality control in manufacturing
|
947 |
"""
|
948 |
)
|
949 |
else:
|
@@ -963,18 +979,10 @@ def render_results_column():
|
|
963 |
|
964 |
##### Supported Data Format
|
965 |
|
966 |
-
- **File Type:**
|
967 |
- **Content:** Must contain two columns: `wavenumber` and `intensity`.
|
968 |
- **Separators:** Values can be separated by spaces or commas.
|
969 |
- **Preprocessing:** Your spectrum will be automatically resampled to 500 data points to match the model's input requirements.
|
970 |
-
|
971 |
-
---
|
972 |
-
|
973 |
-
##### Example Applications
|
974 |
-
- 🔬 Research on polymer degradation
|
975 |
-
- ♻️ Recycling feasibility assessment
|
976 |
-
- 🌱 Sustainability impact studies
|
977 |
-
- 🏭 Quality control in manufacturing
|
978 |
"""
|
979 |
)
|
980 |
|
@@ -1004,19 +1012,18 @@ def render_comparison_tab():
|
|
1004 |
"Compare predictions across different AI models for comprehensive analysis."
|
1005 |
)
|
1006 |
|
1007 |
-
# Modality selector
|
1008 |
col_mod1, col_mod2 = st.columns([1, 2])
|
1009 |
with col_mod1:
|
|
|
|
|
1010 |
modality = st.selectbox(
|
1011 |
"Select Modality",
|
1012 |
["raman", "ftir"],
|
1013 |
-
index=0,
|
1014 |
help="Choose the spectroscopy modality for analysis",
|
1015 |
-
key="
|
1016 |
-
)
|
1017 |
-
# Don't override existing session state
|
1018 |
-
if "modality_select" not in st.session_state:
|
1019 |
-
st.session_state["modality_select"] = modality
|
1020 |
|
1021 |
with col_mod2:
|
1022 |
# Filter models by modality
|
@@ -1123,6 +1130,19 @@ def render_comparison_tab():
|
|
1123 |
str(input_text), filename or "unknown_filename"
|
1124 |
)
|
1125 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1126 |
# Preprocess spectrum once
|
1127 |
_, y_processed = preprocess_spectrum(
|
1128 |
x_raw, y_raw, modality=modality, target_len=500
|
@@ -1272,52 +1292,68 @@ def render_comparison_tab():
|
|
1272 |
for m in models
|
1273 |
]
|
1274 |
|
1275 |
-
|
1276 |
-
|
1277 |
-
|
1278 |
-
|
1279 |
-
|
1280 |
-
|
1281 |
-
|
1282 |
-
|
1283 |
-
|
1284 |
-
)
|
1285 |
|
1286 |
-
|
1287 |
-
|
1288 |
-
|
1289 |
-
|
1290 |
-
|
1291 |
-
height + 0.01,
|
1292 |
-
f"{conf:.3f}",
|
1293 |
-
ha="center",
|
1294 |
-
va="bottom",
|
1295 |
)
|
1296 |
|
1297 |
-
|
1298 |
-
|
1299 |
-
|
1300 |
-
|
1301 |
-
|
1302 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1303 |
|
1304 |
with col2:
|
1305 |
# Confidence distribution
|
1306 |
st.markdown("**Confidence Statistics**")
|
1307 |
-
|
1308 |
-
|
1309 |
-
|
1310 |
-
|
1311 |
-
|
1312 |
-
|
1313 |
-
|
1314 |
-
|
1315 |
-
|
1316 |
-
|
1317 |
-
|
1318 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
1319 |
st.error(f"Error rendering results: {e}")
|
1320 |
-
|
|
|
1321 |
st.error(f"Error rendering results: {e}")
|
1322 |
st.error(f"Error in Confidence Analysis tab: {e}")
|
1323 |
|
@@ -1328,45 +1364,50 @@ def render_comparison_tab():
|
|
1328 |
successful_results[m]["processing_time"]
|
1329 |
for m in models
|
1330 |
]
|
1331 |
-
|
1332 |
-
|
1333 |
-
|
1334 |
-
with perf_col1:
|
1335 |
-
# Processing time comparison
|
1336 |
-
fig, ax = plt.subplots(figsize=(8, 5))
|
1337 |
-
bars = ax.bar(
|
1338 |
-
models, times, alpha=0.8, color="skyblue"
|
1339 |
)
|
|
|
|
|
|
|
1340 |
|
1341 |
-
|
1342 |
-
|
1343 |
-
ax.
|
1344 |
-
|
1345 |
-
|
1346 |
-
f"{time_val:.3f}s",
|
1347 |
-
ha="center",
|
1348 |
-
va="bottom",
|
1349 |
)
|
1350 |
|
1351 |
-
|
1352 |
-
|
1353 |
-
|
1354 |
-
|
1355 |
-
|
1356 |
-
|
1357 |
-
|
1358 |
-
|
1359 |
-
|
1360 |
-
perf_stats = {
|
1361 |
-
"Fastest Model": models[np.argmin(times)],
|
1362 |
-
"Slowest Model": models[np.argmax(times)],
|
1363 |
-
"Total Time": f"{np.sum(times):.3f}s",
|
1364 |
-
"Average Time": f"{np.mean(times):.3f}s",
|
1365 |
-
"Speed Difference": f"{np.max(times) - np.min(times):.3f}s",
|
1366 |
-
}
|
1367 |
|
1368 |
-
|
1369 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1370 |
|
1371 |
with tab3:
|
1372 |
# Detailed breakdown
|
|
|
27 |
)
|
28 |
from utils.results_manager import ResultsManager
|
29 |
from utils.multifile import process_multiple_files
|
30 |
+
from utils.preprocessing import resample_spectrum, validate_spectrum_modality
|
31 |
from utils.confidence import calculate_softmax_confidence
|
32 |
|
33 |
|
|
|
406 |
else:
|
407 |
try:
|
408 |
x_raw, y_raw = parse_spectrum_data(st.session_state["input_text"])
|
409 |
+
|
410 |
+
# Validate that spectrum matches selected modality
|
411 |
+
selected_modality = st.session_state.get("modality_select", "raman")
|
412 |
+
is_valid, issues = validate_spectrum_modality(
|
413 |
+
x_raw, y_raw, selected_modality
|
414 |
+
)
|
415 |
+
|
416 |
+
if not is_valid:
|
417 |
+
st.warning("⚠️ **Spectrum-Modality Mismatch Detected**")
|
418 |
+
for issue in issues:
|
419 |
+
st.warning(f"• {issue}")
|
420 |
+
|
421 |
+
# Ask user if they want to continue
|
422 |
+
st.info(
|
423 |
+
"💡 **Suggestion**: Check if the correct modality is selected in the sidebar, or verify your data file."
|
424 |
+
)
|
425 |
+
|
426 |
+
if st.button("⚠️ Continue Anyway", key="continue_with_mismatch"):
|
427 |
+
st.warning(
|
428 |
+
"Proceeding with potentially mismatched data. Results may be unreliable."
|
429 |
+
)
|
430 |
+
else:
|
431 |
+
st.stop() # Stop processing until user confirms
|
432 |
+
|
433 |
x_resampled, y_resampled = resample_spectrum(x_raw, y_raw, TARGET_LEN)
|
434 |
st.session_state.update(
|
435 |
{
|
|
|
956 |
|
957 |
##### Supported Data Format
|
958 |
|
959 |
+
- **File Type(s):** `.txt`, `.csv`, `.json`
|
960 |
- **Content:** Must contain two columns: `wavenumber` and `intensity`.
|
961 |
- **Separators:** Values can be separated by spaces or commas.
|
962 |
- **Preprocessing:** Your spectrum will be automatically resampled to 500 data points to match the model's input requirements.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
963 |
"""
|
964 |
)
|
965 |
else:
|
|
|
979 |
|
980 |
##### Supported Data Format
|
981 |
|
982 |
+
- **File Type(s):** `.txt`, `.csv`, `.json`
|
983 |
- **Content:** Must contain two columns: `wavenumber` and `intensity`.
|
984 |
- **Separators:** Values can be separated by spaces or commas.
|
985 |
- **Preprocessing:** Your spectrum will be automatically resampled to 500 data points to match the model's input requirements.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
986 |
"""
|
987 |
)
|
988 |
|
|
|
1012 |
"Compare predictions across different AI models for comprehensive analysis."
|
1013 |
)
|
1014 |
|
1015 |
+
# Modality selector - Use independant state for comparison tab
|
1016 |
col_mod1, col_mod2 = st.columns([1, 2])
|
1017 |
with col_mod1:
|
1018 |
+
# Get the current sidebar modality but don't try to sync back
|
1019 |
+
current_modality = st.session_state.get("modality_select", "raman")
|
1020 |
modality = st.selectbox(
|
1021 |
"Select Modality",
|
1022 |
["raman", "ftir"],
|
1023 |
+
index=0 if current_modality == "raman" else 1,
|
1024 |
help="Choose the spectroscopy modality for analysis",
|
1025 |
+
key="comparison_tab_modality", # Independant key for session state to avoid duplication of UI elements
|
1026 |
+
) # Note: Intentially not synching back to avoid state conflicts
|
|
|
|
|
|
|
1027 |
|
1028 |
with col_mod2:
|
1029 |
# Filter models by modality
|
|
|
1130 |
str(input_text), filename or "unknown_filename"
|
1131 |
)
|
1132 |
|
1133 |
+
# Validate spectrum modality
|
1134 |
+
is_valid, issues = validate_spectrum_modality(
|
1135 |
+
x_raw, y_raw, modality
|
1136 |
+
)
|
1137 |
+
if not is_valid:
|
1138 |
+
st.error("**Spectrum-Modality Mismatch in Comparison**")
|
1139 |
+
for issue in issues:
|
1140 |
+
st.error(f"• {issue}")
|
1141 |
+
st.info(
|
1142 |
+
"Please check the selected modality or verify your data file."
|
1143 |
+
)
|
1144 |
+
return # Exit comparison if validation fails
|
1145 |
+
|
1146 |
# Preprocess spectrum once
|
1147 |
_, y_processed = preprocess_spectrum(
|
1148 |
x_raw, y_raw, modality=modality, target_len=500
|
|
|
1292 |
for m in models
|
1293 |
]
|
1294 |
|
1295 |
+
if len(confidences) == 0:
|
1296 |
+
st.warning(
|
1297 |
+
"No confidence data available for visualization."
|
1298 |
+
)
|
1299 |
+
else:
|
1300 |
+
fig, ax = plt.subplots(figsize=(8, 5))
|
1301 |
+
colors = plt.cm.Set3(
|
1302 |
+
np.linspace(0, 1, len(models))
|
1303 |
+
)
|
|
|
1304 |
|
1305 |
+
bars = ax.bar(
|
1306 |
+
models,
|
1307 |
+
confidences,
|
1308 |
+
alpha=0.8,
|
1309 |
+
color=colors,
|
|
|
|
|
|
|
|
|
1310 |
)
|
1311 |
|
1312 |
+
# Add value labels on bars
|
1313 |
+
for bar, conf in zip(bars, confidences):
|
1314 |
+
height = bar.get_height()
|
1315 |
+
ax.text(
|
1316 |
+
bar.get_x()
|
1317 |
+
+ bar.get_width() / 2.0,
|
1318 |
+
height + 0.01,
|
1319 |
+
f"{conf:.3f}",
|
1320 |
+
ha="center",
|
1321 |
+
va="bottom",
|
1322 |
+
)
|
1323 |
+
|
1324 |
+
ax.set_ylabel("Confidence")
|
1325 |
+
ax.set_title(
|
1326 |
+
"Model Confidence Comparison"
|
1327 |
+
)
|
1328 |
+
ax.set_ylim(0, 1.1)
|
1329 |
+
plt.xticks(rotation=45)
|
1330 |
+
plt.tight_layout()
|
1331 |
+
st.pyplot(fig)
|
1332 |
|
1333 |
with col2:
|
1334 |
# Confidence distribution
|
1335 |
st.markdown("**Confidence Statistics**")
|
1336 |
+
if len(confidences) == 0:
|
1337 |
+
st.warning(
|
1338 |
+
"No confidence data available for statistics."
|
1339 |
+
)
|
1340 |
+
else:
|
1341 |
+
conf_stats = {
|
1342 |
+
"Mean": np.mean(confidences),
|
1343 |
+
"Std Dev": np.std(confidences),
|
1344 |
+
"Min": np.min(confidences),
|
1345 |
+
"Max": np.max(confidences),
|
1346 |
+
"Range": np.max(confidences)
|
1347 |
+
- np.min(confidences),
|
1348 |
+
}
|
1349 |
+
|
1350 |
+
for stat, value in conf_stats.items():
|
1351 |
+
st.metric(stat, f"{value:.4f}")
|
1352 |
+
|
1353 |
+
except ValueError as e:
|
1354 |
st.error(f"Error rendering results: {e}")
|
1355 |
+
|
1356 |
+
except ValueError as e:
|
1357 |
st.error(f"Error rendering results: {e}")
|
1358 |
st.error(f"Error in Confidence Analysis tab: {e}")
|
1359 |
|
|
|
1364 |
successful_results[m]["processing_time"]
|
1365 |
for m in models
|
1366 |
]
|
1367 |
+
if len(times) == 0:
|
1368 |
+
st.warning(
|
1369 |
+
"No performance data available for visualization"
|
|
|
|
|
|
|
|
|
|
|
1370 |
)
|
1371 |
+
else:
|
1372 |
+
|
1373 |
+
perf_col1, perf_col2 = st.columns(2)
|
1374 |
|
1375 |
+
with perf_col1:
|
1376 |
+
# Processing time comparison
|
1377 |
+
fig, ax = plt.subplots(figsize=(8, 5))
|
1378 |
+
bars = ax.bar(
|
1379 |
+
models, times, alpha=0.8, color="skyblue"
|
|
|
|
|
|
|
1380 |
)
|
1381 |
|
1382 |
+
for bar, time_val in zip(bars, times):
|
1383 |
+
height = bar.get_height()
|
1384 |
+
ax.text(
|
1385 |
+
bar.get_x() + bar.get_width() / 2.0,
|
1386 |
+
height + 0.001,
|
1387 |
+
f"{time_val:.3f}s",
|
1388 |
+
ha="center",
|
1389 |
+
va="bottom",
|
1390 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1391 |
|
1392 |
+
ax.set_ylabel("Processing Time (s)")
|
1393 |
+
ax.set_title("Model Processing Time Comparison")
|
1394 |
+
plt.xticks(rotation=45)
|
1395 |
+
plt.tight_layout()
|
1396 |
+
st.pyplot(fig)
|
1397 |
+
|
1398 |
+
with perf_col2:
|
1399 |
+
# Performance statistics
|
1400 |
+
st.markdown("**Performance Statistics**")
|
1401 |
+
perf_stats = {
|
1402 |
+
"Fastest Model": models[np.argmin(times)],
|
1403 |
+
"Slowest Model": models[np.argmax(times)],
|
1404 |
+
"Total Time": f"{np.sum(times):.3f}s",
|
1405 |
+
"Average Time": f"{np.mean(times):.3f}s",
|
1406 |
+
"Speed Difference": f"{np.max(times) - np.min(times):.3f}s",
|
1407 |
+
}
|
1408 |
+
|
1409 |
+
for stat, value in perf_stats.items():
|
1410 |
+
st.write(f"**{stat}**: {value}")
|
1411 |
|
1412 |
with tab3:
|
1413 |
# Detailed breakdown
|