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
|