El_Chaderino

Clinical Insight Engine

Sep 1st, 2025 (edited)
327
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 30.82 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. EEG Paradox Clinical Insight  Report System v1.0
  5. Advanced EDF Processing
  6.  
  7. Copyright (C) 2024 EEG Paradox Project
  8. This program is free software: you can redistribute it and/or modify
  9. it under the terms of the GNU General Public License as published by
  10. the Free Software Foundation, either version 3 of the License, or
  11. (at your option) any later version.
  12.  
  13. Specialized tool for processing EDF files to generate clinical Insight
  14. focused on Executive Control Network (Fz, Cz, Pz) and symptom domain analysis.
  15. """
  16.  
  17. import os
  18. import sys
  19. import numpy as np
  20. import pandas as pd
  21. from pathlib import Path
  22. import json
  23. import logging
  24. from datetime import datetime
  25. from typing import Dict, List, Tuple, Optional, Union
  26. from dataclasses import dataclass, asdict
  27. from enum import Enum
  28. import warnings
  29.  
  30. # Try to import MNE for advanced EEG processing
  31. try:
  32.     import mne
  33.     from mne import create_info
  34.     from mne.io import read_raw_edf
  35.     MNE_AVAILABLE = True
  36. except ImportError:
  37.     MNE_AVAILABLE = False
  38.     print("⚠️  MNE-Python not available - Limited functionality")
  39.  
  40. # Try to import additional analysis libraries
  41. try:
  42.     from scipy import signal, stats
  43.     from scipy.fft import fft, fftfreq
  44.     SCIPY_AVAILABLE = True
  45. except ImportError:
  46.     SCIPY_AVAILABLE = False
  47.     print("⚠️  SciPy not available - Limited spectral analysis")
  48.  
  49. try:
  50.     import matplotlib.pyplot as plt
  51.     import seaborn as sns
  52.     MATPLOTLIB_AVAILABLE = True
  53. except ImportError:
  54.     MATPLOTLIB_AVAILABLE = False
  55.     print("⚠️  Matplotlib not available - No visualization")
  56.  
  57. # Configure logging
  58. logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
  59. logger = logging.getLogger(__name__)
  60.  
  61. class SymptomDomain(Enum):
  62.     """Clinical symptom domains for Insight generation"""
  63.     IMPULSIVITY_AROUSAL = "impulsivity_arousal_regulation"
  64.     ATTENTION_EXECUTIVE = "attention_executive_function"
  65.     SOCIAL_INTERPERSONAL = "social_interpersonal_function"
  66.     MOOD_EMOTIONAL = "mood_emotional_regulation"
  67.     COGNITIVE_PROCESSING = "cognitive_processing"
  68.     SENSORY_MOTOR = "sensory_motor_function"
  69.     SLEEP_VIGILANCE = "sleep_vigilance"
  70.     AUTONOMIC_REGULATION = "autonomic_regulation"
  71.  
  72. class EEGSite(Enum):
  73.     """Key EEG sites for Executive Control Network analysis"""
  74.     FZ = "Fz"  # Frontal midline - Executive control
  75.     CZ = "Cz"  # Central midline - Motor control, attention
  76.     PZ = "Pz"  # Parietal midline - Attention, working memory
  77.  
  78. class FrequencyBand(Enum):
  79.     """EEG frequency bands"""
  80.     DELTA = "delta"      # 0.5-4 Hz
  81.     THETA = "theta"      # 4-8 Hz
  82.     ALPHA = "alpha"      # 8-13 Hz
  83.     BETA = "beta"        # 13-30 Hz
  84.     GAMMA = "gamma"      # 30-100 Hz
  85.  
  86. @dataclass
  87. class ClinicalInsight :
  88.     """Clinical Insight  data structure"""
  89.     symptom_domain: SymptomDomain
  90.     primary_sites: List[EEGSite]
  91.     qeeg_markers: List[str]
  92.     erp_components: List[str]
  93.     network_dynamics: List[str]
  94.     confidence_score: float
  95.     clinical_implications: str
  96.     assessment_recommendations: List[str]
  97.     references: List[str]
  98.  
  99. @dataclass
  100. class EEGMetrics:
  101.     """EEG metrics for specific sites and bands"""
  102.     site: EEGSite
  103.     band: FrequencyBand
  104.     power: float
  105.     relative_power: float
  106.     asymmetry: Optional[float] = None
  107.     coherence: Optional[float] = None
  108.     phase_lag: Optional[float] = None
  109.  
  110. @dataclass
  111. class ClinicalReport:
  112.     """Complete clinical Insight  report"""
  113.     patient_id: str
  114.     analysis_timestamp: datetime
  115.     symptom_ranking: List[Tuple[SymptomDomain, float]]
  116.     hypotheses: List[ClinicalInsight ]
  117.     eeg_metrics: List[EEGMetrics]
  118.     brain_map_data: Dict
  119.     confidence_summary: Dict
  120.     recommendations: List[str]
  121.  
  122. class ClinicalInsight Analyzer:
  123.     """
  124.    Advanced Clinical Insight  Report System
  125.    Processes EDF files to generate structured clinical hypotheses
  126.    """
  127.    
  128.     def __init__(self):
  129.         self.symptom_domain_weights = {
  130.             SymptomDomain.IMPULSIVITY_AROUSAL: 0.25,
  131.             SymptomDomain.ATTENTION_EXECUTIVE: 0.30,
  132.             SymptomDomain.SOCIAL_INTERPERSONAL: 0.15,
  133.             SymptomDomain.MOOD_EMOTIONAL: 0.20,
  134.             SymptomDomain.COGNITIVE_PROCESSING: 0.10
  135.         }
  136.        
  137.         # Executive Control Network site priorities
  138.         self.executive_sites = [EEGSite.FZ, EEGSite.CZ, EEGSite.PZ]
  139.        
  140.         # Frequency band definitions
  141.         self.frequency_bands = {
  142.             FrequencyBand.DELTA: (0.5, 4),
  143.             FrequencyBand.THETA: (4, 8),
  144.             FrequencyBand.ALPHA: (8, 13),
  145.             FrequencyBand.BETA: (13, 30),
  146.             FrequencyBand.GAMMA: (30, 100)
  147.         }
  148.        
  149.         # Clinical Insight  templates
  150.         self.Insight _templates = self._initialize_Insight _templates()
  151.        
  152.     def _initialize_Insight _templates(self) -> Dict:
  153.         """Initialize clinical Insight  templates based on Gunkelman/Gattis methodology"""
  154.         return {
  155.             SymptomDomain.IMPULSIVITY_AROUSAL: {
  156.                 'primary_sites': [EEGSite.FZ, EEGSite.CZ],
  157.                 'qeeg_markers': [
  158.                     'Frontal slowing (increased theta)',
  159.                     'Beta hyperarousal patterns',
  160.                     'Theta/Beta ratio elevation',
  161.                     'Frontal alpha asymmetry'
  162.                 ],
  163.                 'erp_components': ['N200', 'P300', 'Error-related negativity'],
  164.                 'network_dynamics': [
  165.                     'Salience network dysfunction',
  166.                     'Executive control network imbalance',
  167.                     'Frontal-parietal connectivity issues'
  168.                 ],
  169.                 'clinical_implications': 'Patterns suggest difficulties with impulse control and arousal regulation, potentially indicating ADHD, anxiety, or trauma-related symptoms.',
  170.                 'assessment_recommendations': [
  171.                     'Comprehensive qEEG with eyes closed/eyes open protocols',
  172.                     'ERP assessment for attention and inhibitory control',
  173.                     'Behavioral assessment for impulsivity measures',
  174.                     'Consider medication effects on arousal patterns'
  175.                 ],
  176.                 'references': [
  177.                     'Gunkelman, J. & Johnstone, J. (2005). Neurofeedback and the Brain.',
  178.                     'Arns, M. et al. (2013). EEG-based ADHD diagnosis: A review.'
  179.                 ]
  180.             },
  181.            
  182.             SymptomDomain.ATTENTION_EXECUTIVE: {
  183.                 'primary_sites': [EEGSite.FZ, EEGSite.CZ, EEGSite.PZ],
  184.                 'qeeg_markers': [
  185.                     'Central theta elevation',
  186.                     'Theta/Beta ratio (TBR) patterns',
  187.                     'Executive Control Network markers',
  188.                     'Frontal-parietal coherence'
  189.                 ],
  190.                 'erp_components': ['P300', 'N2pc', 'Contingent negative variation'],
  191.                 'network_dynamics': [
  192.                     'Executive Control Network dysfunction',
  193.                     'Default Mode Network interference',
  194.                     'Frontal-parietal attention network',
  195.                     'Cingulo-opercular network'
  196.                 ],
  197.                 'clinical_implications': 'Executive control network patterns suggest attention regulation difficulties, working memory issues, and potential ADHD or executive dysfunction.',
  198.                 'assessment_recommendations': [
  199.                     'TBR analysis across executive sites (Fz, Cz, Pz)',
  200.                     'Working memory task with EEG monitoring',
  201.                     'Continuous performance test with ERP',
  202.                     'Executive function behavioral assessment'
  203.                 ],
  204.                 'references': [
  205.                     'Gunkelman, J. (2019). The Clinical Use of QEEG.',
  206.                     'Posner, M.I. & Petersen, S.E. (1990). The attention system of the human brain.'
  207.                 ]
  208.             },
  209.            
  210.             SymptomDomain.SOCIAL_INTERPERSONAL: {
  211.                 'primary_sites': [EEGSite.FZ, EEGSite.CZ],
  212.                 'qeeg_markers': [
  213.                     'Right-hemisphere beta activity',
  214.                     'Frontal alpha asymmetry',
  215.                     'Social brain network markers',
  216.                     'Mirror neuron system indicators'
  217.                 ],
  218.                 'erp_components': ['N170', 'P300', 'Late positive potential'],
  219.                 'network_dynamics': [
  220.                     'Social brain network dysfunction',
  221.                     'Theory of mind network',
  222.                     'Empathy network connectivity',
  223.                     'Social attention network'
  224.                 ],
  225.                 'clinical_implications': 'Social processing patterns may indicate difficulties with social cognition, empathy, or interpersonal relationships.',
  226.                 'assessment_recommendations': [
  227.                     'Social cognition assessment',
  228.                     'Theory of mind testing',
  229.                     'Social anxiety evaluation',
  230.                     'Interpersonal relationship assessment'
  231.                 ],
  232.                 'references': [
  233.                     'Gattis, M. (2018). Social Brain Networks in Clinical Practice.',
  234.                     'Frith, C.D. & Frith, U. (2006). The neural basis of mentalizing.'
  235.                 ]
  236.             }
  237.         }
  238.    
  239.     def analyze_edf_file(self, edf_path: Union[str, Path]) -> ClinicalReport:
  240.         """
  241.        Main analysis function - processes EDF file and generates clinical hypotheses
  242.        
  243.        Args:
  244.            edf_path: Path to EDF file
  245.            
  246.        Returns:
  247.            ClinicalReport object with complete analysis
  248.        """
  249.         edf_path = Path(edf_path)
  250.         if not edf_path.exists():
  251.             raise FileNotFoundError(f"EDF file not found: {edf_path}")
  252.        
  253.         logger.info(f"Starting clinical Insight  analysis for: {edf_path.name}")
  254.        
  255.         # Load and preprocess EDF data
  256.         raw_data = self._load_edf_data(edf_path)
  257.        
  258.         # Extract key metrics for executive sites
  259.         eeg_metrics = self._extract_executive_metrics(raw_data)
  260.        
  261.         # Generate symptom domain rankings
  262.         symptom_ranking = self._rank_symptom_domains(eeg_metrics)
  263.        
  264.         # Generate clinical hypotheses
  265.         hypotheses = self._generate_clinical_hypotheses(symptom_ranking, eeg_metrics)
  266.        
  267.         # Create brain map data
  268.         brain_map_data = self._create_brain_map_data(eeg_metrics)
  269.        
  270.         # Generate confidence summary
  271.         confidence_summary = self._calculate_confidence_summary(hypotheses, eeg_metrics)
  272.        
  273.         # Generate recommendations
  274.         recommendations = self._generate_recommendations(hypotheses)
  275.        
  276.         # Create final report
  277.         report = ClinicalReport(
  278.             patient_id=edf_path.stem,
  279.             analysis_timestamp=datetime.now(),
  280.             symptom_ranking=symptom_ranking,
  281.             hypotheses=hypotheses,
  282.             eeg_metrics=eeg_metrics,
  283.             brain_map_data=brain_map_data,
  284.             confidence_summary=confidence_summary,
  285.             recommendations=recommendations
  286.         )
  287.        
  288.         logger.info("Clinical Insight  analysis completed successfully")
  289.         return report
  290.    
  291.     def _load_edf_data(self, edf_path: Path):
  292.         """Load and preprocess EDF data"""
  293.         if not MNE_AVAILABLE:
  294.             raise ImportError("MNE-Python is required for EDF processing")
  295.        
  296.         try:
  297.             # Load EDF file
  298.             raw = read_raw_edf(edf_path, preload=True, verbose=False)
  299.            
  300.             # Apply standard preprocessing
  301.             raw.filter(0.5, 100, fir_design='firwin', verbose=False)
  302.             raw.notch_filter(60, verbose=False)  # Remove line noise
  303.            
  304.             # Resample to standard frequency if needed
  305.             if raw.info['sfreq'] > 500:
  306.                 raw.resample(500, verbose=False)
  307.            
  308.             logger.info(f"Loaded EDF: {len(raw.ch_names)} channels, {raw.info['sfreq']:.1f} Hz")
  309.             return raw
  310.            
  311.         except Exception as e:
  312.             logger.error(f"Error loading EDF file: {e}")
  313.             raise
  314.    
  315.     def _extract_executive_metrics(self, raw_data) -> List[EEGMetrics]:
  316.         """Extract EEG metrics for executive control network sites"""
  317.         metrics = []
  318.        
  319.         # Get available channel names
  320.         available_channels = [ch.upper() for ch in raw_data.ch_names]
  321.        
  322.         for site in self.executive_sites:
  323.             site_name = site.value.upper()
  324.            
  325.             # Find matching channel (handle variations like FZ, Fz, Fpz-Fz, etc.)
  326.             matching_channel = None
  327.             for ch in raw_data.ch_names:
  328.                 if site_name in ch.upper() or ch.upper() in site_name:
  329.                     matching_channel = ch
  330.                     break
  331.            
  332.             if not matching_channel:
  333.                 logger.warning(f"Channel {site_name} not found in data")
  334.                 continue
  335.            
  336.             # Extract data for this channel
  337.             channel_data = raw_data.get_data(picks=[matching_channel])[0]
  338.            
  339.             # Calculate metrics for each frequency band
  340.             for band, (low_freq, high_freq) in self.frequency_bands.items():
  341.                 try:
  342.                     # Calculate power spectral density
  343.                     freqs, psd = signal.welch(
  344.                         channel_data,
  345.                         fs=raw_data.info['sfreq'],
  346.                         nperseg=int(2 * raw_data.info['sfreq']),
  347.                         noverlap=int(raw_data.info['sfreq'])
  348.                     )
  349.                    
  350.                     # Extract band power
  351.                     band_mask = (freqs >= low_freq) & (freqs <= high_freq)
  352.                     band_power = np.trapz(psd[band_mask], freqs[band_mask])
  353.                    
  354.                     # Calculate relative power
  355.                     total_power = np.trapz(psd, freqs)
  356.                     relative_power = band_power / total_power if total_power > 0 else 0
  357.                    
  358.                     # Create metrics object
  359.                     metric = EEGMetrics(
  360.                         site=site,
  361.                         band=band,
  362.                         power=band_power,
  363.                         relative_power=relative_power
  364.                     )
  365.                    
  366.                     metrics.append(metric)
  367.                    
  368.                 except Exception as e:
  369.                     logger.warning(f"Error calculating metrics for {site.value} {band.value}: {e}")
  370.                     continue
  371.        
  372.         return metrics
  373.    
  374.     def _rank_symptom_domains(self, eeg_metrics: List[EEGMetrics]) -> List[Tuple[SymptomDomain, float]]:
  375.         """Rank symptom domains based on EEG patterns"""
  376.         domain_scores = {}
  377.        
  378.         for domain in SymptomDomain:
  379.             score = 0.0
  380.            
  381.             # Get template for this domain
  382.             template = self.Insight _templates.get(domain, {})
  383.             primary_sites = template.get('primary_sites', [])
  384.            
  385.             # Calculate score based on relevant metrics
  386.             for metric in eeg_metrics:
  387.                 if metric.site in primary_sites:
  388.                     # Weight different frequency bands based on clinical relevance
  389.                     if domain == SymptomDomain.IMPULSIVITY_AROUSAL:
  390.                         if metric.band == FrequencyBand.THETA:
  391.                             score += metric.relative_power * 2.0
  392.                         elif metric.band == FrequencyBand.BETA:
  393.                             score += metric.relative_power * 1.5
  394.                         elif metric.band == FrequencyBand.ALPHA:
  395.                             score += metric.relative_power * 1.0
  396.                    
  397.                     elif domain == SymptomDomain.ATTENTION_EXECUTIVE:
  398.                         if metric.band == FrequencyBand.THETA:
  399.                             score += metric.relative_power * 2.5
  400.                         elif metric.band == FrequencyBand.BETA:
  401.                             score += metric.relative_power * 1.0
  402.                         elif metric.band == FrequencyBand.ALPHA:
  403.                             score += metric.relative_power * 0.5
  404.                    
  405.                     elif domain == SymptomDomain.SOCIAL_INTERPERSONAL:
  406.                         if metric.band == FrequencyBand.BETA:
  407.                             score += metric.relative_power * 2.0
  408.                         elif metric.band == FrequencyBand.ALPHA:
  409.                             score += metric.relative_power * 1.5
  410.            
  411.             # Apply domain weight
  412.             score *= self.symptom_domain_weights.get(domain, 0.1)
  413.             domain_scores[domain] = score
  414.        
  415.         # Sort by score (highest first)
  416.         ranked_domains = sorted(domain_scores.items(), key=lambda x: x[1], reverse=True)
  417.         return ranked_domains
  418.    
  419.     def _generate_clinical_hypotheses(self, symptom_ranking: List[Tuple[SymptomDomain, float]],
  420.                                     eeg_metrics: List[EEGMetrics]) -> List[ClinicalInsight ]:
  421.         """Generate clinical hypotheses based on symptom rankings and EEG metrics"""
  422.         hypotheses = []
  423.        
  424.         for domain, score in symptom_ranking[:3]:  # Top 3 domains
  425.             if score > 0.1:  # Only generate hypotheses for significant scores
  426.                 template = self.Insight _templates.get(domain, {})
  427.                
  428.                 # Calculate confidence based on score and metric consistency
  429.                 confidence = min(score * 2, 1.0)  # Scale to 0-1
  430.                
  431.                 Insight  = ClinicalInsight (
  432.                     symptom_domain=domain,
  433.                     primary_sites=template.get('primary_sites', []),
  434.                     qeeg_markers=template.get('qeeg_markers', []),
  435.                     erp_components=template.get('erp_components', []),
  436.                     network_dynamics=template.get('network_dynamics', []),
  437.                     confidence_score=confidence,
  438.                     clinical_implications=template.get('clinical_implications', ''),
  439.                     assessment_recommendations=template.get('assessment_recommendations', []),
  440.                     references=template.get('references', [])
  441.                 )
  442.                
  443.                 hypotheses.append(Insight )
  444.        
  445.         return hypotheses
  446.    
  447.     def _create_brain_map_data(self, eeg_metrics: List[EEGMetrics]) -> Dict:
  448.         """Create brain map visualization data"""
  449.         brain_map = {
  450.             'executive_sites': {},
  451.             'network_connectivity': {},
  452.             'frequency_analysis': {}
  453.         }
  454.        
  455.         # Organize metrics by site
  456.         for metric in eeg_metrics:
  457.             site_name = metric.site.value
  458.             if site_name not in brain_map['executive_sites']:
  459.                 brain_map['executive_sites'][site_name] = {}
  460.            
  461.             brain_map['executive_sites'][site_name][metric.band.value] = {
  462.                 'power': float(metric.power),
  463.                 'relative_power': float(metric.relative_power)
  464.             }
  465.        
  466.         # Calculate network connectivity (simplified)
  467.         fz_metrics = [m for m in eeg_metrics if m.site == EEGSite.FZ]
  468.         cz_metrics = [m for m in eeg_metrics if m.site == EEGSite.CZ]
  469.         pz_metrics = [m for m in eeg_metrics if m.site == EEGSite.PZ]
  470.        
  471.         # Calculate correlations between sites (simplified approach)
  472.         if fz_metrics and cz_metrics:
  473.             fz_powers = [m.power for m in fz_metrics]
  474.             cz_powers = [m.power for m in cz_metrics]
  475.             if len(fz_powers) == len(cz_powers):
  476.                 correlation = np.corrcoef(fz_powers, cz_powers)[0, 1]
  477.                 brain_map['network_connectivity']['Fz-Cz'] = float(correlation)
  478.        
  479.         return brain_map
  480.    
  481.     def _calculate_confidence_summary(self, hypotheses: List[ClinicalInsight ],
  482.                                     eeg_metrics: List[EEGMetrics]) -> Dict:
  483.         """Calculate overall confidence summary"""
  484.         if not hypotheses:
  485.             return {'overall_confidence': 0.0, 'data_quality': 'poor'}
  486.        
  487.         # Calculate average confidence
  488.         avg_confidence = np.mean([h.confidence_score for h in hypotheses])
  489.        
  490.         # Assess data quality based on metrics
  491.         data_quality = 'good'
  492.         if len(eeg_metrics) < 10:
  493.             data_quality = 'limited'
  494.         elif avg_confidence < 0.3:
  495.             data_quality = 'poor'
  496.        
  497.         return {
  498.             'overall_confidence': float(avg_confidence),
  499.             'data_quality': data_quality,
  500.             'Insight _count': len(hypotheses),
  501.             'metrics_count': len(eeg_metrics)
  502.         }
  503.    
  504.     def _generate_recommendations(self, hypotheses: List[ClinicalInsight ]) -> List[str]:
  505.         """Generate clinical recommendations based on hypotheses"""
  506.         recommendations = []
  507.        
  508.         if not hypotheses:
  509.             recommendations.append("Insufficient data for clinical Insight  generation")
  510.             return recommendations
  511.        
  512.         # General recommendations
  513.         recommendations.append("Comprehensive qEEG assessment recommended")
  514.         recommendations.append("Consider ERP assessment for attention and executive function")
  515.        
  516.         # Specific recommendations based on top Insight
  517.         top_Insight  = hypotheses[0]
  518.         recommendations.extend(top_Insight .assessment_recommendations[:3])
  519.        
  520.         # Additional recommendations
  521.         if any(h.symptom_domain == SymptomDomain.ATTENTION_EXECUTIVE for h in hypotheses):
  522.             recommendations.append("TBR analysis across executive sites (Fz, Cz, Pz)")
  523.        
  524.         if any(h.symptom_domain == SymptomDomain.IMPULSIVITY_AROUSAL for h in hypotheses):
  525.             recommendations.append("Behavioral assessment for impulsivity measures")
  526.        
  527.         return recommendations
  528.    
  529.     def save_report(self, report: ClinicalReport, output_path: Union[str, Path]):
  530.         """Save clinical report to JSON file"""
  531.         output_path = Path(output_path)
  532.        
  533.         # Convert report to dictionary
  534.         report_dict = asdict(report)
  535.        
  536.         # Convert datetime to string
  537.         report_dict['analysis_timestamp'] = report.analysis_timestamp.isoformat()
  538.        
  539.         # Convert enums to strings
  540.         def convert_enums(obj):
  541.             if isinstance(obj, dict):
  542.                 return {k: convert_enums(v) for k, v in obj.items()}
  543.             elif isinstance(obj, list):
  544.                 return [convert_enums(item) for item in obj]
  545.             elif isinstance(obj, Enum):
  546.                 return obj.value
  547.             elif hasattr(obj, '__dict__'):
  548.                 return {k: convert_enums(v) for k, v in obj.__dict__.items()}
  549.             else:
  550.                 return obj
  551.        
  552.         report_dict = convert_enums(report_dict)
  553.        
  554.         # Save to JSON
  555.         with open(output_path, 'w', encoding='utf-8') as f:
  556.             json.dump(report_dict, f, indent=2, ensure_ascii=False)
  557.        
  558.         logger.info(f"Clinical report saved to: {output_path}")
  559.    
  560.     def generate_visual_report(self, report: ClinicalReport, output_path: Union[str, Path]):
  561.         """Generate visual brain map and report"""
  562.         if not MATPLOTLIB_AVAILABLE:
  563.             logger.warning("Matplotlib not available - skipping visual report")
  564.             return
  565.        
  566.         output_path = Path(output_path)
  567.        
  568.         # Create figure with subplots
  569.         fig, axes = plt.subplots(2, 2, figsize=(15, 12))
  570.         fig.suptitle(f'Clinical Insight  Report - {report.patient_id}', fontsize=16, fontweight='bold')
  571.        
  572.         # Plot 1: Brain map visualization
  573.         self._plot_brain_map(axes[0, 0], report.brain_map_data)
  574.        
  575.         # Plot 2: Symptom domain rankings
  576.         self._plot_symptom_rankings(axes[0, 1], report.symptom_ranking)
  577.        
  578.         # Plot 3: EEG metrics by site
  579.         self._plot_eeg_metrics(axes[1, 0], report.eeg_metrics)
  580.        
  581.         # Plot 4: Confidence summary
  582.         self._plot_confidence_summary(axes[1, 1], report.confidence_summary)
  583.        
  584.         plt.tight_layout()
  585.         plt.savefig(output_path, dpi=300, bbox_inches='tight')
  586.         plt.close()
  587.        
  588.         logger.info(f"Visual report saved to: {output_path}")
  589.    
  590.     def _plot_brain_map(self, ax, brain_map_data):
  591.         """Plot brain map visualization"""
  592.         ax.set_title('Executive Control Network', fontweight='bold')
  593.        
  594.         # Create simplified brain map
  595.         sites = ['Fz', 'Cz', 'Pz']
  596.         colors = ['red', 'blue', 'green']
  597.        
  598.         for i, site in enumerate(sites):
  599.             if site in brain_map_data['executive_sites']:
  600.                 site_data = brain_map_data['executive_sites'][site]
  601.                 # Calculate average relative power across bands
  602.                 avg_power = np.mean([data['relative_power'] for data in site_data.values()])
  603.                
  604.                 # Plot site with size proportional to power
  605.                 ax.scatter(i, 0, s=avg_power*1000, c=colors[i], alpha=0.7, label=site)
  606.                 ax.text(i, 0.1, f'{avg_power:.3f}', ha='center', fontsize=8)
  607.        
  608.         ax.set_xlim(-0.5, 2.5)
  609.         ax.set_ylim(-0.5, 0.5)
  610.         ax.set_xticks(range(3))
  611.         ax.set_xticklabels(sites)
  612.         ax.legend()
  613.         ax.grid(True, alpha=0.3)
  614.    
  615.     def _plot_symptom_rankings(self, ax, symptom_ranking):
  616.         """Plot symptom domain rankings"""
  617.         ax.set_title('Symptom Domain Rankings', fontweight='bold')
  618.        
  619.         domains = [domain.value.replace('_', ' ').title() for domain, _ in symptom_ranking]
  620.         scores = [score for _, score in symptom_ranking]
  621.        
  622.         bars = ax.barh(range(len(domains)), scores, color='steelblue', alpha=0.7)
  623.         ax.set_yticks(range(len(domains)))
  624.         ax.set_yticklabels(domains)
  625.         ax.set_xlabel('Score')
  626.        
  627.         # Add value labels on bars
  628.         for i, (bar, score) in enumerate(zip(bars, scores)):
  629.             ax.text(bar.get_width() + 0.01, bar.get_y() + bar.get_height()/2,
  630.                    f'{score:.3f}', va='center', fontsize=8)
  631.        
  632.         ax.grid(True, alpha=0.3)
  633.    
  634.     def _plot_eeg_metrics(self, ax, eeg_metrics):
  635.         """Plot EEG metrics by site and band"""
  636.         ax.set_title('EEG Metrics by Site and Band', fontweight='bold')
  637.        
  638.         # Organize data for plotting
  639.         sites = ['Fz', 'Cz', 'Pz']
  640.         bands = ['delta', 'theta', 'alpha', 'beta', 'gamma']
  641.        
  642.         data_matrix = np.zeros((len(sites), len(bands)))
  643.        
  644.         for metric in eeg_metrics:
  645.             site_idx = sites.index(metric.site.value)
  646.             band_idx = bands.index(metric.band.value)
  647.             data_matrix[site_idx, band_idx] = metric.relative_power
  648.        
  649.         # Create heatmap
  650.         im = ax.imshow(data_matrix, cmap='viridis', aspect='auto')
  651.        
  652.         # Set labels
  653.         ax.set_xticks(range(len(bands)))
  654.         ax.set_xticklabels(bands)
  655.         ax.set_yticks(range(len(sites)))
  656.         ax.set_yticklabels(sites)
  657.        
  658.         # Add colorbar
  659.         plt.colorbar(im, ax=ax, label='Relative Power')
  660.        
  661.         # Add text annotations
  662.         for i in range(len(sites)):
  663.             for j in range(len(bands)):
  664.                 text = ax.text(j, i, f'{data_matrix[i, j]:.3f}',
  665.                              ha="center", va="center", color="white", fontsize=8)
  666.    
  667.     def _plot_confidence_summary(self, ax, confidence_summary):
  668.         """Plot confidence summary"""
  669.         ax.set_title('Analysis Confidence', fontweight='bold')
  670.        
  671.         # Create pie chart for confidence levels
  672.         confidence = confidence_summary['overall_confidence']
  673.         data_quality = confidence_summary['data_quality']
  674.        
  675.         # Define confidence levels
  676.         if confidence >= 0.7:
  677.             level = 'High'
  678.             color = 'green'
  679.         elif confidence >= 0.4:
  680.             level = 'Medium'
  681.             color = 'orange'
  682.         else:
  683.             level = 'Low'
  684.             color = 'red'
  685.        
  686.         # Create pie chart
  687.         sizes = [confidence, 1-confidence]
  688.         labels = [f'{level} ({confidence:.1%})', f'Uncertainty ({1-confidence:.1%})']
  689.         colors = [color, 'lightgray']
  690.        
  691.         wedges, texts, autotexts = ax.pie(sizes, labels=labels, colors=colors, autopct='%1.1f%%', startangle=90)
  692.        
  693.         # Add data quality info
  694.         ax.text(0, -1.3, f'Data Quality: {data_quality.title()}', ha='center', fontweight='bold')
  695.         ax.text(0, -1.5, f'Hypotheses: {confidence_summary["Insight _count"]}', ha='center')
  696.         ax.text(0, -1.7, f'Metrics: {confidence_summary["metrics_count"]}', ha='center')
  697.  
  698.  
  699. def main():
  700.     """Main function for command-line usage"""
  701.     import argparse
  702.    
  703.     parser = argparse.ArgumentParser(description='EEG Paradox Clinical Insight  Report System')
  704.     parser.add_argument('edf_file', help='Path to EDF file to analyze')
  705.     parser.add_argument('-o', '--output', help='Output directory for reports', default='.')
  706.     parser.add_argument('-v', '--visual', action='store_true', help='Generate visual report')
  707.     parser.add_argument('--verbose', action='store_true', help='Verbose logging')
  708.    
  709.     args = parser.parse_args()
  710.    
  711.     if args.verbose:
  712.         logging.getLogger().setLevel(logging.DEBUG)
  713.    
  714.     # Initialize analyzer
  715.     analyzer = ClinicalInsight Analyzer()
  716.    
  717.     try:
  718.         # Analyze EDF file
  719.         report = analyzer.analyze_edf_file(args.edf_file)
  720.        
  721.         # Save JSON report
  722.         output_dir = Path(args.output)
  723.         output_dir.mkdir(exist_ok=True)
  724.        
  725.         json_path = output_dir / f"{report.patient_id}_clinical_Insight .json"
  726.         analyzer.save_report(report, json_path)
  727.        
  728.         # Generate visual report if requested
  729.         if args.visual:
  730.             visual_path = output_dir / f"{report.patient_id}_clinical_Insight .png"
  731.             analyzer.generate_visual_report(report, visual_path)
  732.        
  733.         print(f"✅ Analysis completed successfully!")
  734.         print(f"📊 Report saved to: {json_path}")
  735.         if args.visual:
  736.             print(f"📈 Visual report saved to: {visual_path}")
  737.        
  738.         # Print summary
  739.         print(f"\n🔍 Top Symptom Domains:")
  740.         for i, (domain, score) in enumerate(report.symptom_ranking[:3], 1):
  741.             print(f"  {i}. {domain.value.replace('_', ' ').title()}: {score:.3f}")
  742.        
  743.         print(f"\n🧠 Clinical Hypotheses Generated: {len(report.hypotheses)}")
  744.         print(f"📈 Overall Confidence: {report.confidence_summary['overall_confidence']:.1%}")
  745.        
  746.     except Exception as e:
  747.         logger.error(f"Analysis failed: {e}")
  748.         sys.exit(1)
  749.  
  750.  
  751. if __name__ == "__main__":
  752.     main()
  753.  
Advertisement
Add Comment
Please, Sign In to add comment