fix to stats when no json file is defined

This commit is contained in:
2026-08-03 00:01:21 -07:00
parent 61a9ea2f34
commit 7c456e31e7
7 changed files with 122 additions and 135 deletions
+28 -7
View File
@@ -152,8 +152,8 @@ class CrossGroupStatsWidget(CrossGroupUIMixin, FlaresBaseWidget):
df_ind_dict: dict[str, DataFrame],
design_matrix_dict: dict[str, DataFrame],
contrast_results_dict: dict[str, dict[str, Any]],
roi_channel_map_dict: dict[str, dict[str, str]],
group_dict: dict[str, str],
json_location: str | Path
) -> None:
super().__init__("CrossGroupStats")
@@ -163,14 +163,14 @@ class CrossGroupStatsWidget(CrossGroupUIMixin, FlaresBaseWidget):
self.df_ind_dict = df_ind_dict
self.design_matrix_dict = design_matrix_dict
self.contrast_results_dict = contrast_results_dict
# self.group_dict = group_dict
self.json_location = json_location
self.roi_channel_map_dict = roi_channel_map_dict
self.group_dict = group_dict
self.setup_cross_group_ui(["0 (Raw ROI Comparison)", "1 (Laterality Comparison)", "2 (Contrast Comparison)",], placeholder_text=DESCRIPTION)
def process_request(self):
request = self.get_common_request_data(PARAMETERIZED_INDEXES, self.json_location, self.contrast_results_dict)
request = self.get_common_request_data(PARAMETERIZED_INDEXES, self.df_ind_dict, self.contrast_results_dict)
if request is None:
return
@@ -200,6 +200,12 @@ class CrossGroupStatsWidget(CrossGroupUIMixin, FlaresBaseWidget):
target_chroma = params.get("target_chroma", "hbo")
threshold_topo = params.get("threshold_topo", False)
selected_roi_maps = {
fp: self.roi_channel_map_dict[fp]
for fp in (file_paths_a + file_paths_b)
if fp in self.roi_channel_map_dict
}
run_cross_group_second_level_analysis(
df_roi_all=df_ind_combined, # Individual stats dataframe
file_paths_a=file_paths_a,
@@ -213,7 +219,7 @@ class CrossGroupStatsWidget(CrossGroupUIMixin, FlaresBaseWidget):
correction_method=correction_method,
target_chroma=target_chroma,
selected_event=selected_event,
roi_config=self.json_location,
roi_channel_maps=selected_roi_maps,
threshold_topo=threshold_topo # Shows the raw difference map (Unthresholded)
)
elif idx == 1:
@@ -317,12 +323,27 @@ class CrossGroupStatsWidget(CrossGroupUIMixin, FlaresBaseWidget):
if df_contrasts_a.empty or df_contrasts_b.empty:
print("No contrast data found for one or both groups.")
continue
roi_maps_a = {
fp: self.roi_channel_map_dict[fp]
for fp in file_paths_a
if fp in self.roi_channel_map_dict
}
roi_maps_b = {
fp: self.roi_channel_map_dict[fp]
for fp in file_paths_b
if fp in self.roi_channel_map_dict
}
if not roi_maps_a or not roi_maps_b:
print("No channel-to-ROI mapping available for one or both groups.")
continue
run_cross_group_contrast_analysis(
df_contrasts_a=df_contrasts_a,
df_contrasts_b=df_contrasts_b,
contrast_name=contrast_name,
roi_json_path=self.json_location,
roi_channel_maps_a=roi_maps_a,
roi_channel_maps_b=roi_maps_b,
group_a_name=self.group_a_dropdown.currentText(),
group_b_name=self.group_b_dropdown.currentText(),
target_chroma=target_chroma,
+14 -6
View File
@@ -165,8 +165,8 @@ class InterGroupStatsWidget(InterGroupUIMixin, FlaresBaseWidget):
df_ind_dict: dict[str, DataFrame],
design_matrix_dict: dict[str, DataFrame],
contrast_results_dict: dict[str, dict[str, Any]],
roi_channel_map_dict: dict[str, dict[str, str]],
group_dict: dict[str, str],
json_location: str | Path
) -> None:
super().__init__("InterGroupStats")
@@ -176,14 +176,14 @@ class InterGroupStatsWidget(InterGroupUIMixin, FlaresBaseWidget):
self.df_ind_dict = df_ind_dict
self.design_matrix_dict = design_matrix_dict
self.contrast_results_dict = contrast_results_dict
self.roi_channel_map_dict = roi_channel_map_dict
self.group_dict = group_dict
self.json_location = json_location
self.setup_inter_group_ui(["0 (ROI vs. Zero)", "1 (Paired ROI Contrast)", "2 (Joint Contrast, ROI-Aggregated)"], placeholder_text=DESCRIPTION)
def process_request(self):
request = self.get_common_request_data(PARAMETERIZED_INDEXES, self.json_location, self.contrast_results_dict)
request = self.get_common_request_data(PARAMETERIZED_INDEXES, self.df_ind_dict, self.contrast_results_dict)
if request is None:
return
@@ -276,7 +276,6 @@ class InterGroupStatsWidget(InterGroupUIMixin, FlaresBaseWidget):
correction_method=correction_method,
target_chroma=target_chroma,
graph_bounds=graph_bounds if graph_bounds > 0.0 else None,
roi_config=self.json_location
)
elif idx == 1:
@@ -358,11 +357,20 @@ class InterGroupStatsWidget(InterGroupUIMixin, FlaresBaseWidget):
continue
df_contrasts = pd.concat(all_contrasts, ignore_index=True)
selected_roi_maps = {
fp: self.roi_channel_map_dict[fp]
for fp in selected_file_paths
if fp in self.roi_channel_map_dict
}
if not selected_roi_maps:
print("No channel-to-ROI mapping available for selected participants.")
continue
try:
roi_theta = aggregate_channel_contrasts_to_roi(
df_contrasts,
roi_json_path=self.json_location,
roi_channel_maps=selected_roi_maps,
weighted=weighted,
)
+24 -30
View File
@@ -11,6 +11,8 @@ import json
from pathlib import Path
from typing import Sequence, Any
import pandas as pd
from pandas import DataFrame
from PySide6.QtWidgets import QApplication, QComboBox, QDialog, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QListView, QMessageBox, QPushButton, QScrollArea, QVBoxLayout, QWidget, QFrame, QSpinBox
from PySide6.QtGui import QStandardItemModel, QStandardItem, QPixmap, QIntValidator, QDoubleValidator
from PySide6.QtCore import QEvent, QSize, Qt
@@ -1380,7 +1382,7 @@ class CrossGroupUIMixin:
def get_common_request_data(
self,
parameterized_indexes: dict[int, list[dict[str, Any]]],
json_location: str | Path | None = None,
df_ind_dict: dict[str, DataFrame] | None = None,
contrast_dfs: dict[str, dict[str, Any]] | None = None,
) -> tuple[str | None, list[str], list[str], list[str], list[int], dict[str, Any]] | None:
@@ -1423,19 +1425,15 @@ class CrossGroupUIMixin:
dynamic_rois = []
# 1. Check for the JSON file and parse ROI names
if os.path.exists(json_location):
try:
with open(json_location, 'r', encoding='utf-8') as f:
regions_data = json.load(f)
# Extract "name" from each region under "regions_of_interest"
regions_list = regions_data.get("regions_of_interest", [])
dynamic_rois = [region["name"] for region in regions_list if "name" in region]
except Exception as e:
# Safe log if JSON is corrupted or unreadable
print(f"Error reading ROI configurations from {json_location}: {e}")
if df_ind_dict:
roi_set = set()
for fp in all_selected_paths:
df_roi = df_ind_dict.get(fp)
if isinstance(df_roi, pd.DataFrame) and "ROI" in df_roi.columns:
roi_set.update(df_roi["ROI"].dropna().unique())
if roi_set:
dynamic_rois = sorted(list(roi_set))
# Fallback to prevent UI crashes if JSON file doesn't exist or is empty
if not dynamic_rois:
@@ -1580,7 +1578,7 @@ class InterGroupUIMixin:
self.layout.addLayout(self.top_bar)
self.group_to_paths = {}
for file_path, group_name in self.group.items():
for file_path, group_name in self.group_dict.items():
self.group_to_paths.setdefault(group_name, []).append(file_path)
self.group_names = sorted(self.group_to_paths.keys())
@@ -1632,7 +1630,7 @@ class InterGroupUIMixin:
def get_common_request_data(
self,
parameterized_indexes: dict[int, list[dict[str, Any]]],
json_location: str | Path | None = None,
df_ind_dict: dict[str, DataFrame] | None = None,
contrast_dfs: dict[str, dict[str, Any]] | None = None,
) -> tuple[str | None, list[str], list[int], dict[str, Any]] | None:
@@ -1679,20 +1677,16 @@ class InterGroupUIMixin:
dynamic_rois = []
# 1. Check for the JSON file and parse ROI names
if json_location is not None and os.path.exists(json_location):
try:
with open(json_location, 'r', encoding='utf-8') as f:
regions_data = json.load(f)
# Extract "name" from each region under "regions_of_interest"
regions_list = regions_data.get("regions_of_interest", [])
dynamic_rois = [region["name"] for region in regions_list if "name" in region]
except Exception as e:
# Safe log if JSON is corrupted or unreadable
print(f"Error reading ROI configurations from {json_location}: {e}")
if df_ind_dict:
roi_set = set()
for fp in selected_file_paths:
df_roi = df_ind_dict.get(fp)
if isinstance(df_roi, pd.DataFrame) and "ROI" in df_roi.columns:
roi_set.update(df_roi["ROI"].dropna().unique())
if roi_set:
dynamic_rois = sorted(list(roi_set))
# Fallback to prevent UI crashes if JSON file doesn't exist or is empty
if not dynamic_rois:
dynamic_rois = ["Option 1", "Option 2"]
+3 -3
View File
@@ -24,7 +24,7 @@ from src.shared.shareddata import APP_NAME
class ViewerLauncherWidget(QWidget):
def __init__(self, haemo_dict, epochs_dict, cha_dict, df_ind_dict, design_matrix_dict, config_dict, fig_bytes_dict, contrast_results_dict, folding_bypass, json_location):
def __init__(self, haemo_dict, epochs_dict, cha_dict, df_ind_dict, design_matrix_dict, config_dict, fig_bytes_dict, contrast_results_dict, roi_channel_map_dict, folding_bypass):
super().__init__()
self.setWindowTitle(f"Viewer Launcher - {APP_NAME.upper()}")
@@ -36,8 +36,8 @@ class ViewerLauncherWidget(QWidget):
("Participant Fold Channels Viewer", ParticipantFoldChannelsWidget, [haemo_dict, cha_dict], False),
("Participant Functional Connectivity Viewer [BETA]", ParticipantFunctionalConnectivityWidget, [haemo_dict, epochs_dict], True),
("Inter-Group Functional Connectivity Viewer [BETA]", InterGroupFunctionalConnectivityWidget, [haemo_dict, group_dict, config_dict], True),
("Inter-Group Stats Viewer", InterGroupStatsWidget, [haemo_dict, cha_dict, df_ind_dict, design_matrix_dict, contrast_results_dict, group_dict, json_location], True),
("Cross-Group Stats Viewer", CrossGroupStatsWidget, [haemo_dict, cha_dict, df_ind_dict, design_matrix_dict, contrast_results_dict, group_dict, json_location], True),
("Inter-Group Stats Viewer", InterGroupStatsWidget, [haemo_dict, cha_dict, df_ind_dict, design_matrix_dict, contrast_results_dict, roi_channel_map_dict, group_dict], True),
("Cross-Group Stats Viewer", CrossGroupStatsWidget, [haemo_dict, cha_dict, df_ind_dict, design_matrix_dict, contrast_results_dict, roi_channel_map_dict, group_dict], True),
("Inter-Group Brain and Image Viewer", InterGroupBrainImageWidget, [haemo_dict, cha_dict, df_ind_dict, design_matrix_dict, contrast_results_dict, group_dict], True),
("Cross-Group Brain and Image Viewer", CrossGroupBrainImageWidget, [haemo_dict, df_ind_dict, design_matrix_dict, contrast_results_dict, group_dict], True),
("Export To CSV Viewer", ExportToCSVWidget, [haemo_dict, cha_dict, df_ind_dict, design_matrix_dict, contrast_results_dict, group_dict, config_dict], True)