Files
flares/fc.py
2026-01-30 20:16:55 -08:00

207 lines
5.9 KiB
Python

import mne
import numpy as np
from mne.preprocessing.nirs import optical_density, beer_lambert_law
from mne_connectivity import spectral_connectivity_epochs
from mne_connectivity.viz import plot_connectivity_circle
raw = mne.io.read_raw_snirf("E:/CVI_V_Adults_Cor/P21_CVI_V_updated.snirf", preload=True)
raw.info["bads"] = [] # mark bad channels here if needed
raw_od = optical_density(raw)
raw_hb = beer_lambert_law(raw_od)
raw_hbo = raw_hb.copy().pick(picks="hbo")
raw_hbo.filter(
l_freq=0.01,
h_freq=0.2,
picks="hbo",
verbose=False
)
events = mne.make_fixed_length_events(
raw_hbo,
duration=30.0
)
epochs = mne.Epochs(
raw_hbo,
events,
tmin=0,
tmax=30.0,
baseline=None,
preload=True,
verbose=False
)
data = epochs.get_data() # (n_epochs, n_channels, n_times)
names = epochs.ch_names
sfreq = epochs.info["sfreq"]
con = spectral_connectivity_epochs(
data,
method=["coh", "plv"],
mode="multitaper",
sfreq=sfreq,
fmin=0.04,
fmax=0.2,
faverage=True,
verbose=True
)
con_coh, con_plv = con
coh = con_coh.get_data(output="dense").squeeze()
plv = con_plv.get_data(output="dense").squeeze()
np.fill_diagonal(coh, 0)
np.fill_diagonal(plv, 0)
plot_connectivity_circle(
coh,
names,
title="fNIRS Functional Connectivity (HbO - Coherence)",
n_lines=40
)
from mne_connectivity import envelope_correlation
env = envelope_correlation(
data,
orthogonalize=False,
absolute=True
)
env_data = env.get_data(output="dense")
env_corr = env_data.mean(axis=0)
env_corr = np.squeeze(env_corr)
np.fill_diagonal(env_corr, 0)
plot_connectivity_circle(
env_corr,
epochs.ch_names,
title="fNIRS HbO Envelope Correlation (Task Connectivity)",
n_lines=40
)
from mne_nirs.statistics import run_glm
from mne_nirs.experimental_design import make_first_level_design_matrix
raw_hb.annotations.description = [
f"Reach_{i}" if d == "Reach" else d
for i, d in enumerate(raw_hb.annotations.description)
]
design_matrix = make_first_level_design_matrix(
raw_hb,
stim_dur=1.0, # We assume a short burst since duration is unknown
hrf_model='fir', # Finite Impulse Response
fir_delays=np.arange(0, 12) # Look at 0-20 seconds after onset
)
# 2. Run the GLM
# This calculates the brain's response for every channel
glm_est = run_glm(raw_hb, design_matrix)
import pandas as pd
# 3. Extract Beta Weights
beta_df = glm_est.to_dataframe()
print("\n--- DEBUG: Dataframe Info ---")
print(f"Total rows in beta_df: {len(beta_df)}")
print(f"Columns available: {list(beta_df.columns)}")
print(f"Unique Chroma values: {beta_df['Chroma'].unique()}")
print(f"First 5 unique Conditions: {beta_df['Condition'].unique()[:5]}")
# FIX: Use .str.contains() because FIR conditions are named like 'Reach[5.0]'
# We filter for HbO AND any condition that starts with 'Reach'
hbo_betas = beta_df[(beta_df['Chroma'] == 'hbo') &
(beta_df['Condition'].str.contains('Reach'))]
hbo_betas = hbo_betas.copy()
hbo_betas[['Trial', 'Delay']] = hbo_betas['Condition'].str.extract(r'(Reach_\d+)_delay_(\d+)')
# 2. Find which DELAY (time point) is best across ALL trials
# We convert 'Delay' to numeric so we can sort them properly later if needed
hbo_betas['Delay'] = pd.to_numeric(hbo_betas['Delay'])
# IMPORTANT: We ignore delay 0 and 1 because they are usually 0.0000 (stimulus onset)
# Brain responses in fNIRS usually peak between delays 4 and 8 (4-8 seconds)
mask = (hbo_betas['Delay'] >= 4) & (hbo_betas['Delay'] <= 8)
hbo_window = hbo_betas[mask]
if hbo_window.empty:
print("Warning: No data found in the 4-8s window. Check your 'fir_delays' range.")
# Fallback to whatever is available if 4-8 is missing
mean_by_delay = hbo_betas.groupby('Delay')['theta'].mean()
else:
mean_by_delay = hbo_window.groupby('Delay')['theta'].mean()
peak_delay_num = mean_by_delay.idxmax()
print(f"\n--- DEBUG: FIR Timing ---")
print(f"Delays analyzed: {list(mean_by_delay.index)}")
print(f"Peak brain response found at delay: {peak_delay_num}")
# 3. Filter the data to ONLY include that peak delay across ALL trials
peak_df = hbo_betas[hbo_betas['Delay'] == peak_delay_num]
mne_order = raw_hbo.ch_names
# 4. Pivot: Rows = Trials (Reach_1, Reach_2...), Columns = Channels
beta_pivot = peak_df.pivot(index='Trial', columns='ch_name', values='theta')
beta_pivot = beta_pivot.reindex(columns=mne_order)
print(f"Pivot table shape: {beta_pivot.shape} (Should be something like 30 trials x 26 channels)")
# 5. Correlation (Now it has a series of data to correlate!)
beta_corr_matrix = beta_pivot.corr().values
np.fill_diagonal(beta_corr_matrix, 0)
# Replace any NaNs with 0 (occurs if a channel has 0 variance)
beta_corr_matrix = np.nan_to_num(beta_corr_matrix)
import matplotlib.pyplot as plt
channel_names = beta_pivot.columns.tolist()
# Create the plot
plot_connectivity_circle(
beta_corr_matrix,
channel_names,
n_lines=40, # Show only the top 40 strongest connections
title=f"FIR Beta Series Connectivity)",
)
# 1. Aggregate the mean response for each delay across all trials and channels
# We want to see the general 'shape' of the Reach response
time_points = np.arange(0, 12) # Matches your fir_delays
average_response = hbo_betas.groupby('Delay')['theta'].mean()
# 2. Plotting
plt.figure(figsize=(10, 6))
for ch in hbo_betas['ch_name'].unique():
ch_data = hbo_betas[hbo_betas['ch_name'] == ch].groupby('Delay')['theta'].mean()
plt.plot(time_points, ch_data, color='gray', alpha=0.3) # Individual channels
# Plot the 'Grand Average' in bold red
plt.plot(time_points, average_response, color='red', linewidth=3, label='Grand Average')
plt.axvline(x=4, color='green', linestyle='--', label='Window Start (4s)')
plt.axvline(x=8, color='green', linestyle='--', label='Window End (8s)')
plt.title("FIR Hemodynamic Response to 'Reach' (HbO)")
plt.xlabel("Seconds after Stimulus")
plt.ylabel("HbO Concentration (Beta Weight)")
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()