El_Chaderino

CPTSD vs PTSD vs Anxiety Py

Aug 31st, 2025 (edited)
244
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.77 KB | None | 0 0
  1. import mne
  2. import numpy as np
  3. import pandas as pd
  4. import matplotlib.pyplot as plt
  5. import json
  6. import csv
  7. from scipy.signal import welch
  8. from mne.preprocessing import ICA, create_eog_epochs, create_ecg_epochs
  9. from mne.viz import plot_topomap
  10. from mne.channels import make_standard_montage
  11. from mne import create_info, EvokedArray
  12.  
  13. # ---- CONFIG ----
  14. BANDS = {
  15.     "delta": (0.5, 4),
  16.     "theta": (4, 8),
  17.     "alpha": (8, 12),
  18.     "smr": (12, 15),
  19.     "beta": (15, 25),
  20.     "hi_beta": (25, 30),
  21.     "gamma": (30, 45)
  22. }
  23.  
  24. CHANNELS_10_20 = ['Fp1', 'Fp2', 'F3', 'F4', 'C3', 'C4', 'P3', 'P4',
  25.                   'O1', 'O2', 'F7', 'F8', 'T3', 'T4', 'T5', 'T6',
  26.                   'Fz', 'Cz', 'Pz']
  27.  
  28. # ---- FEATURE EXTRACTION ----
  29. def compute_bandpower(data, sfreq, band):
  30.     fmin, fmax = band
  31.     freqs, psd = welch(data, sfreq, nperseg=1024)
  32.     idx_band = np.logical_and(freqs >= fmin, freqs <= fmax)
  33.     return np.sum(psd[:, idx_band], axis=1)
  34.  
  35. def extract_features(raw):
  36.     sfreq = raw.info['sfreq']
  37.     data = raw.get_data(picks=CHANNELS_10_20)
  38.     features = {}
  39.     for i, ch_name in enumerate(CHANNELS_10_20):
  40.         ch_data = data[i, :]
  41.         features[ch_name] = {}
  42.         for band_name, band_range in BANDS.items():
  43.             power = compute_bandpower(ch_data[np.newaxis, :], sfreq, band_range)
  44.             features[ch_name][band_name] = float(power[0])
  45.     return features
  46.  
  47. # ---- ARTIFACT REJECTION ----
  48. def reject_artifacts(raw):
  49.     raw.set_eeg_reference('average', projection=True)
  50.     ica = ICA(n_components=15, random_state=97, max_iter='auto')
  51.     ica.fit(raw)
  52.  
  53.     # Eye blinks
  54.     eog_epochs = create_eog_epochs(raw, reject_by_annotation=True)
  55.     eog_inds, _ = ica.find_bads_eog(eog_epochs)
  56.     ica.exclude.extend(eog_inds)
  57.  
  58.     # ECG artifacts
  59.     ecg_epochs = create_ecg_epochs(raw, reject_by_annotation=True)
  60.     ecg_inds, _ = ica.find_bads_ecg(ecg_epochs)
  61.     ica.exclude.extend(ecg_inds)
  62.  
  63.     # EMG noise (high frequency)
  64.     freqs, psd = welch(raw.get_data(), raw.info['sfreq'])
  65.     emg_noise = psd.mean(axis=1).max() > 1e-6  # crude threshold
  66.     if emg_noise:
  67.         print("⚠️ High EMG noise detected")
  68.  
  69.     raw_clean = ica.apply(raw.copy())
  70.     return raw_clean
  71.  
  72. # ---- CLASSIFICATION ----
  73. def classify_conditions_advanced(features):
  74.     rationale = {}
  75.     results = {}
  76.  
  77.     beta_fz = features['Fz']['beta']
  78.     hi_beta_fz = features['Fz']['hi_beta']
  79.     alpha_occ = np.mean([features['O1']['alpha'], features['O2']['alpha']])
  80.     theta_cz = features['Cz']['theta']
  81.     theta_coherence_fz_pz = abs(features['Fz']['theta'] - features['Pz']['theta'])
  82.     gamma_post = np.mean([features['O1']['gamma'], features['O2']['gamma']])
  83.     alpha_asymmetry = features['F3']['alpha'] - features['F4']['alpha']
  84.  
  85.     results['PTSD'] = int(hi_beta_fz > 10 and beta_fz > 12 and alpha_occ < 6)
  86.     rationale['PTSD'] = {
  87.         "hi_beta_fz": hi_beta_fz,
  88.         "beta_fz": beta_fz,
  89.         "alpha_occipital_mean": alpha_occ,
  90.         "criteria_met": results['PTSD']
  91.     }
  92.  
  93.     results['cPTSD'] = int(
  94.         results['PTSD'] and
  95.         theta_cz > 8 and
  96.         theta_coherence_fz_pz < 2.0 and
  97.         gamma_post < 3.5
  98.     )
  99.     rationale['cPTSD'] = {
  100.         "inherits_PTSD": results['PTSD'],
  101.         "theta_cz": theta_cz,
  102.         "theta_coherence_fz_pz": theta_coherence_fz_pz,
  103.         "posterior_gamma_mean": gamma_post,
  104.         "criteria_met": results['cPTSD']
  105.     }
  106.  
  107.     beta_frontal = np.mean([features['F3']['beta'], features['F4']['beta']])
  108.     alpha_frontal = np.mean([features['F3']['alpha'], features['F4']['alpha']])
  109.  
  110.     results['Anxiety (Primary)'] = int(beta_frontal > 12 and alpha_frontal > 6 and not results['PTSD'])
  111.     results['Anxiety (Symptom)'] = int(beta_frontal > 10 and results['PTSD'])
  112.  
  113.     rationale['Anxiety (Primary)'] = {
  114.         "beta_frontal": beta_frontal,
  115.         "alpha_frontal": alpha_frontal,
  116.         "criteria_met": results['Anxiety (Primary)']
  117.     }
  118.  
  119.     rationale['Anxiety (Symptom)'] = {
  120.         "beta_frontal": beta_frontal,
  121.         "PTSD_flag": results['PTSD'],
  122.         "criteria_met": results['Anxiety (Symptom)']
  123.     }
  124.  
  125.     return results, rationale
  126.  
  127. # ---- ARCHETYPE MAPPING ----
  128. def map_archetypes(features):
  129.     alpha_frontal = np.mean([features['F3']['alpha'], features['F4']['alpha']])
  130.     alpha_occip = np.mean([features['O1']['alpha'], features['O2']['alpha']])
  131.     theta_frontal = np.mean([features['F3']['theta'], features['F4']['theta']])
  132.     gamma_cortex = np.mean([features[site]['gamma'] for site in ['Cz', 'Pz', 'Fz']])
  133.     delta_occip = np.mean([features['O1']['delta'], features['O2']['delta']])
  134.     beta_fz = features['Fz']['beta']
  135.  
  136.     archetypes = []
  137.  
  138.     if alpha_frontal > 10 and gamma_cortex > 5:
  139.         archetypes.append("The Sage")
  140.     if beta_fz > 12 and alpha_occip < 6:
  141.         archetypes.append("The Hero")
  142.     if alpha_occip > 10 and delta_occip < 5 and theta_frontal < 6:
  143.         archetypes.append("The Caregiver")
  144.     if alpha_frontal > 12 and beta_fz < 6 and gamma_cortex < 3:
  145.         archetypes.append("The Innocent")
  146.     if theta_frontal > 8 and gamma_cortex > 7:
  147.         archetypes.append("The Explorer")
  148.     if alpha_frontal < 6 and theta_frontal > 8:
  149.         archetypes.append("The Orphan")
  150.     if gamma_cortex > 8:
  151.         archetypes.append("The Magician")
  152.  
  153.     if not archetypes:
  154.         archetypes.append("Mixed or transitional")
  155.  
  156.     return archetypes
  157.  
  158. # ---- TOPOGRAPHIC MAPPING ----
  159. def generate_topomaps(features, output_prefix='topomap'):
  160.     montage = make_standard_montage("standard_1020")
  161.     ch_names = list(features.keys())
  162.     ch_types = ["eeg"] * len(ch_names)
  163.     info = create_info(ch_names=ch_names, sfreq=250, ch_types=ch_types)
  164.     info.set_montage(montage)
  165.  
  166.     bands = list(next(iter(features.values())).keys())
  167.     for band in bands:
  168.         band_values = [features[ch][band] for ch in ch_names]
  169.         evoked = EvokedArray(np.expand_dims(band_values, axis=1), info)
  170.         fig, ax = plt.subplots()
  171.         plot_topomap(evoked.data[:, 0], evoked.info, axes=ax, show=False)
  172.         ax.set_title(f'{band.capitalize()} Band Topomap')
  173.         plt.savefig(f'{output_prefix}_{band}.png', dpi=150)
  174.         plt.close()
  175.  
  176. # ---- NEUROFEEDBACK PROTOCOL ----
  177. def suggest_protocol(classifications):
  178.     protocol = {}
  179.     if classifications.get("PTSD", 0) > 0.8:
  180.         protocol = {"Site": "Fz, Pz", "Reward": "12–15 Hz (SMR)", "Inhibit": "Hi-Beta, Theta", "Type": "Stabilization"}
  181.     elif classifications.get("Anxiety (Primary)", 0) > 0.8:
  182.         protocol = {"Site": "F3, F4", "Reward": "Low Beta", "Inhibit": "Hi-Beta", "Type": "Calming"}
  183.     elif classifications.get("cPTSD", 0) > 0.8:
  184.         protocol = {"Site": "O1, Cz", "Reward": "Alpha", "Inhibit": "Delta, Theta", "Type": "Alpha-Theta"}
  185.     else:
  186.         protocol = {"Site": "Cz", "Reward": "12–15 Hz", "Inhibit": "Theta", "Type": "Default"}
  187.     return protocol
  188.  
  189. # ---- EXPORT ----
  190. def export_research_data(subject_id, features, classifications, rationale):
  191.     flat_features = {'subject_id': subject_id}
  192.     for ch, bands in features.items():
  193.         for band, val in bands.items():
  194.             flat_features[f"{ch}_{band}"] = val
  195.  
  196.     with open(f"{subject_id}_features.csv", "w", newline="") as csvfile:
  197.         writer = csv.DictWriter(csvfile, fieldnames=flat_features.keys())
  198.         writer.writeheader()
  199.         writer.writerow(flat_features)
  200.  
  201.     out_json = {
  202.         "subject_id": subject_id,
  203.         "features": features,
  204.         "classification": classifications,
  205.         "rationale_trace": rationale
  206.     }
  207.  
  208.     with open(f"{subject_id}_research_export.json", "w") as f:
  209.         json.dump(out_json, f, indent=4)
  210.  
  211. # ---- MAIN PIPELINE ----
  212. def run_qeeg_research_mode(edf_path, subject_id="subject001"):
  213.     raw = mne.io.read_raw_edf(edf_path, preload=True)
  214.     raw.pick_channels(CHANNELS_10_20)
  215.     raw.filter(1., 45., fir_design='firwin')
  216.  
  217.     raw_clean = reject_artifacts(raw)
  218.     features = extract_features(raw_clean)
  219.     classifications, rationale = classify_conditions_advanced(features)
  220.     protocol = suggest_protocol(classifications)
  221.     archetypes = map_archetypes(features)
  222.  
  223.     output = {
  224.         "subject_id": subject_id,
  225.         "features": features,
  226.         "diagnostic_confidence": classifications,
  227.         "protocol_suggestion": protocol,
  228.         "archetype_mapping": archetypes,
  229.         "diagnostic_rationale": rationale
  230.     }
  231.  
  232.     with open(f"{subject_id}_full_output.json", "w") as f:
  233.         json.dump(output, f, indent=4)
  234.  
  235.     export_research_data(subject_id, features, classifications, rationale)
  236.     generate_topomaps(features, output_prefix=f"{subject_id}_topomap")
  237.     print(f"🧠 QEEG Analysis complete for {subject_id}")
  238.     return output
  239.  
  240. # Example usage:
  241. # run_qeeg_research_mode("/mnt/data/subject1.edf", subject_id="anon_001")
  242.  
Advertisement
Add Comment
Please, Sign In to add comment