unit testing and others
This commit is contained in:
@@ -1565,19 +1565,20 @@ def make_design_matrix(
|
||||
else:
|
||||
short_chans = None
|
||||
|
||||
# Set the new annotations
|
||||
raw_haemo.set_annotations(new_annot)
|
||||
raw_haemo_dm = raw_haemo.copy()
|
||||
|
||||
if resample:
|
||||
raw_haemo.resample(resample_freq, npad="auto")
|
||||
raw_haemo._data = raw_haemo._data * 1e6
|
||||
raw_haemo_dm.resample(resample_freq, npad="auto")
|
||||
try:
|
||||
short_chans.resample(resample_freq)
|
||||
except:
|
||||
pass
|
||||
|
||||
raw_haemo_dm._data = raw_haemo_dm._data * 1e6
|
||||
|
||||
design_matrix = make_first_level_design_matrix(
|
||||
raw=raw_haemo,
|
||||
raw=raw_haemo_dm,
|
||||
stim_dur=stim_dur,
|
||||
hrf_model=hrf_model,
|
||||
drift_model=drift_model,
|
||||
@@ -1635,7 +1636,7 @@ def make_design_matrix(
|
||||
fig, ax1 = plt.subplots(figsize=(10, 6), constrained_layout=True)
|
||||
_ = plot_design_matrix(design_matrix, axes=ax1)
|
||||
|
||||
return raw_haemo, design_matrix, fig
|
||||
return raw_haemo, raw_haemo_dm, design_matrix, fig
|
||||
|
||||
|
||||
|
||||
@@ -5417,7 +5418,7 @@ def process_participant(file_path, file_start, progress_callback=None):
|
||||
step_start = lap(step_start, timings, "Step 21")
|
||||
|
||||
# Step 22: Design Matrix
|
||||
raw_haemo, df_design_matrix, fig_design_matrix = make_design_matrix(
|
||||
raw_haemo, raw_haemo_dm, df_design_matrix, fig_design_matrix = make_design_matrix(
|
||||
raw_haemo=raw_haemo,
|
||||
resample=RESAMPLE,
|
||||
resample_freq=RESAMPLE_FREQ,
|
||||
@@ -5442,7 +5443,7 @@ def process_participant(file_path, file_start, progress_callback=None):
|
||||
step_start = lap(step_start, timings, "Step 22")
|
||||
|
||||
# Step 23: General Linear Model
|
||||
glm_est, fig_glm_topo = make_and_run_glm(raw_haemo, df_design_matrix, noise_model=NOISE_MODEL, bins=BINS, n_jobs=N_JOBS, verbosity=VERBOSITY)
|
||||
glm_est, fig_glm_topo = make_and_run_glm(raw_haemo_dm, df_design_matrix, noise_model=NOISE_MODEL, bins=BINS, n_jobs=N_JOBS, verbosity=VERBOSITY)
|
||||
_enqueue("GLM Topography", fig_glm_topo, png_queue)
|
||||
if progress_callback: progress_callback(23)
|
||||
logger.info("23")
|
||||
@@ -5508,119 +5509,53 @@ def sanitize_paths_for_pickle(raw_haemo, epochs):
|
||||
|
||||
|
||||
def functional_connectivity_spectral_epochs(
|
||||
epochs: DataFrame | None,
|
||||
epochs: Epochs,
|
||||
n_lines: int,
|
||||
vmin: float,
|
||||
) -> None:
|
||||
|
||||
# will crash without this load
|
||||
epochs.load_data()
|
||||
hbo_epochs = epochs.copy().pick(picks="hbo")
|
||||
data = hbo_epochs.get_data()
|
||||
names = hbo_epochs.ch_names
|
||||
sfreq = hbo_epochs.info["sfreq"]
|
||||
con = spectral_connectivity_epochs(
|
||||
data,
|
||||
method=["coh", "plv"],
|
||||
|
||||
con_coh = spectral_connectivity_epochs(
|
||||
hbo_epochs,
|
||||
method="coh",
|
||||
mode="multitaper",
|
||||
sfreq=sfreq,
|
||||
sfreq=hbo_epochs.info["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()
|
||||
|
||||
coh = np.squeeze(con_coh.get_data(output="dense"))
|
||||
np.fill_diagonal(coh, 0)
|
||||
np.fill_diagonal(plv, 0)
|
||||
|
||||
|
||||
plot_connectivity_circle(
|
||||
coh,
|
||||
names,
|
||||
hbo_epochs.ch_names,
|
||||
title="fNIRS Functional Connectivity (HbO - Coherence)",
|
||||
n_lines=n_lines,
|
||||
vmin=vmin
|
||||
)
|
||||
|
||||
|
||||
|
||||
def functional_connectivity_spectral_time(
|
||||
epochs: DataFrame | None,
|
||||
n_lines: int,
|
||||
vmin: float,
|
||||
) -> None:
|
||||
|
||||
# will crash without this load
|
||||
epochs.load_data()
|
||||
hbo_epochs = epochs.copy().pick(picks="hbo")
|
||||
data = hbo_epochs.get_data()
|
||||
names = hbo_epochs.ch_names
|
||||
sfreq = hbo_epochs.info["sfreq"]
|
||||
|
||||
freqs = np.linspace(0.04, 0.2, 10)
|
||||
n_cycles = freqs * 2
|
||||
|
||||
con = spectral_connectivity_time(
|
||||
data,
|
||||
freqs=freqs,
|
||||
method=["coh", "plv"],
|
||||
mode="multitaper",
|
||||
sfreq=sfreq,
|
||||
fmin=0.04,
|
||||
fmax=0.2,
|
||||
n_cycles=n_cycles,
|
||||
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=n_lines,
|
||||
vmin=vmin
|
||||
)
|
||||
|
||||
|
||||
|
||||
def functional_connectivity_envelope(
|
||||
epochs: DataFrame | None,
|
||||
epochs: Epochs,
|
||||
n_lines: int,
|
||||
vmin: float,
|
||||
) -> None:
|
||||
|
||||
# will crash without this load
|
||||
|
||||
epochs.load_data()
|
||||
hbo_epochs = epochs.copy().pick(picks="hbo")
|
||||
data = hbo_epochs.get_data()
|
||||
|
||||
|
||||
hbo_epochs.filter(l_freq=0.04, h_freq=0.2, verbose=True)
|
||||
|
||||
env = envelope_correlation(
|
||||
data,
|
||||
hbo_epochs.get_data(),
|
||||
orthogonalize=False,
|
||||
absolute=True
|
||||
)
|
||||
env_data = env.get_data(output="dense")
|
||||
|
||||
env_corr = env_data.mean(axis=0)
|
||||
|
||||
env_corr = np.mean(env.get_data(output="dense"), axis=0)
|
||||
env_corr = np.squeeze(env_corr)
|
||||
|
||||
np.fill_diagonal(env_corr, 0)
|
||||
|
||||
|
||||
plot_connectivity_circle(
|
||||
env_corr,
|
||||
hbo_epochs.ch_names,
|
||||
@@ -5630,107 +5565,149 @@ def functional_connectivity_envelope(
|
||||
)
|
||||
|
||||
|
||||
def functional_connectivity_spectral_time(
|
||||
epochs: Epochs,
|
||||
n_lines: int,
|
||||
vmin: float,
|
||||
) -> None:
|
||||
epochs.load_data()
|
||||
hbo_epochs = epochs.copy().pick(picks="hbo")
|
||||
|
||||
freqs = np.linspace(0.04, 0.2, 10)
|
||||
n_cycles = freqs * 2
|
||||
|
||||
con_coh = spectral_connectivity_time(
|
||||
hbo_epochs.get_data(),
|
||||
freqs=freqs,
|
||||
method="coh",
|
||||
mode="multitaper",
|
||||
sfreq=hbo_epochs.info["sfreq"],
|
||||
fmin=0.04,
|
||||
fmax=0.2,
|
||||
n_cycles=n_cycles,
|
||||
faverage=True,
|
||||
verbose=True
|
||||
)
|
||||
coh = np.squeeze(con_coh.get_data(output="dense"))
|
||||
if coh.ndim == 3:
|
||||
coh = coh.mean(axis=0)
|
||||
np.fill_diagonal(coh, 0)
|
||||
|
||||
plot_connectivity_circle(
|
||||
coh,
|
||||
hbo_epochs.ch_names,
|
||||
title="fNIRS Functional Connectivity (HbO - Coherence, Time-Resolved)",
|
||||
n_lines=n_lines,
|
||||
vmin=vmin
|
||||
)
|
||||
|
||||
|
||||
def functional_connectivity_betas(
|
||||
raw_hbo: BaseRaw,
|
||||
n_lines: int,
|
||||
vmin: float,
|
||||
event_name: str | None = None,
|
||||
*,
|
||||
drift_model: str = "cosine",
|
||||
drift_order: int = 1,
|
||||
apply_gsr: bool = True,
|
||||
min_effect_size: float = 0.7,
|
||||
alpha: float = 0.05,
|
||||
) -> None:
|
||||
|
||||
raw_hbo = raw_hbo.copy().pick(picks="hbo")
|
||||
onsets = raw_hbo.annotations.onset
|
||||
|
||||
# CRITICAL: Update the Raw object's annotations so the GLM sees unique events
|
||||
ann = raw_hbo.annotations
|
||||
new_desc = []
|
||||
|
||||
for i, desc in enumerate(ann.description):
|
||||
new_desc.append(f"{desc}__trial_{i:03d}")
|
||||
|
||||
ann.description = np.array(new_desc)
|
||||
|
||||
|
||||
# shoudl use user defiuned!!!!
|
||||
ann.description = np.array([
|
||||
f"{desc}__trial_{i:03d}" for i, desc in enumerate(ann.description)
|
||||
])
|
||||
|
||||
design_matrix = make_first_level_design_matrix(
|
||||
raw=raw_hbo,
|
||||
hrf_model='fir',
|
||||
hrf_model="fir",
|
||||
fir_delays=np.arange(0, 12, 1),
|
||||
drift_model='cosine',
|
||||
drift_order=1
|
||||
drift_model=drift_model,
|
||||
drift_order=drift_order,
|
||||
)
|
||||
|
||||
|
||||
# 3. Run GLM & Extract Betas
|
||||
glm_results = run_glm(raw_hbo, design_matrix)
|
||||
betas = np.array(glm_results.theta())
|
||||
reg_names = list(design_matrix.columns)
|
||||
|
||||
|
||||
glm_results = run_glm(raw_hbo, design_matrix)
|
||||
betas = np.array(glm_results.theta())
|
||||
if betas.ndim == 3 and betas.shape[-1] == 1:
|
||||
betas = betas.squeeze(axis=-1)
|
||||
|
||||
reg_names = list(design_matrix.columns)
|
||||
n_channels = betas.shape[0]
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 5. Find unique trial tags (optionally filtered by event)
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
trial_tags = sorted({
|
||||
col.split("_delay")[0]
|
||||
for col in reg_names
|
||||
if (
|
||||
("__trial_" in col)
|
||||
and (event_name is None or col.startswith(event_name + "__"))
|
||||
)
|
||||
if ("__trial_" in col) and (event_name is None or col.startswith(event_name + "__"))
|
||||
})
|
||||
|
||||
if len(trial_tags) < 4:
|
||||
raise ValueError(
|
||||
f"Only {len(trial_tags)} trials found for event_name={event_name}; "
|
||||
"need at least 4 to compute correlation degrees of freedom."
|
||||
)
|
||||
|
||||
if len(trial_tags) == 0:
|
||||
raise ValueError(f"No trials found for event_name={event_name}")
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 6. Build beta series (average across FIR delays per trial)
|
||||
# ------------------------------------------------------------------
|
||||
beta_series = np.zeros((n_channels, len(trial_tags)))
|
||||
for t_idx, tag in enumerate(trial_tags):
|
||||
col_idx = [j for j, col in enumerate(reg_names) if col.split("_delay")[0] == tag]
|
||||
beta_series[:, t_idx] = betas[:, col_idx].mean(axis=1)
|
||||
|
||||
# Vectorized Global Signal Regression (GSR)
|
||||
if apply_gsr:
|
||||
global_signal = np.mean(beta_series, axis=0)
|
||||
A = np.vstack([global_signal, np.ones(len(global_signal))]).T
|
||||
# Solve least squares for all channels simultaneously
|
||||
params, _, _, _ = np.linalg.lstsq(A, beta_series.T, rcond=None)
|
||||
beta_series_clean = (beta_series.T - A @ params).T
|
||||
else:
|
||||
beta_series_clean = beta_series
|
||||
|
||||
global_signal = np.mean(beta_series, axis=0)
|
||||
beta_series_clean = np.zeros_like(beta_series)
|
||||
for i in range(n_channels):
|
||||
slope, _ = np.polyfit(global_signal, beta_series[i, :], 1)
|
||||
beta_series_clean[i, :] = beta_series[i, :] - (slope * global_signal)
|
||||
|
||||
# --- Vectorized correlation + analytic p-values (replaces the nested
|
||||
# pearsonr loop below) ---
|
||||
n_trials = beta_series_clean.shape[1]
|
||||
corr_matrix = np.corrcoef(beta_series_clean)
|
||||
|
||||
with np.errstate(divide='ignore', invalid='ignore'):
|
||||
t_stats = corr_matrix * np.sqrt((n_trials - 2) / (1 - corr_matrix ** 2))
|
||||
|
||||
# Safe t-statistic calculation avoiding division by zero on diagonal
|
||||
corr_clipped = np.clip(corr_matrix, -0.999999, 0.999999)
|
||||
t_stats = corr_clipped * np.sqrt((n_trials - 2) / (1 - corr_clipped ** 2))
|
||||
p_matrix = 2 * t_dist.sf(np.abs(t_stats), df=n_trials - 2)
|
||||
np.fill_diagonal(p_matrix, 1.0) # diagonal r=1 -> nan/inf guarded explicitly
|
||||
|
||||
np.fill_diagonal(p_matrix, 1.0)
|
||||
|
||||
triu = np.triu_indices(n_channels, k=1)
|
||||
flat_p = p_matrix[triu]
|
||||
|
||||
reject, _ = multipletests(flat_p, method='fdr_bh', alpha=0.05)[:2]
|
||||
sig_corr_matrix = np.zeros_like(corr_matrix)
|
||||
reject, _, _, _ = multipletests(flat_p, method="fdr_bh", alpha=alpha)
|
||||
|
||||
sig_corr_matrix = np.zeros_like(corr_matrix)
|
||||
for idx, is_sig in enumerate(reject):
|
||||
r_val = corr_matrix[triu[0][idx], triu[1][idx]]
|
||||
# Only keep the absolute strongest connections
|
||||
if is_sig and abs(r_val) > 0.7:
|
||||
if is_sig and abs(r_val) > min_effect_size:
|
||||
sig_corr_matrix[triu[0][idx], triu[1][idx]] = r_val
|
||||
sig_corr_matrix[triu[1][idx], triu[0][idx]] = r_val
|
||||
|
||||
# 6. Plot
|
||||
gsr_tag = "GSR" if apply_gsr else "no GSR"
|
||||
plot_connectivity_circle(
|
||||
sig_corr_matrix,
|
||||
sig_corr_matrix,
|
||||
raw_hbo.ch_names,
|
||||
title="Strictly Filtered Connectivity (TDDR + GSR + Z-Score)",
|
||||
n_lines=None,
|
||||
vmin=0.7,
|
||||
title=f"Beta-Series Connectivity (FDR q<{alpha}, |r|>{min_effect_size}, {gsr_tag})",
|
||||
n_lines=n_lines,
|
||||
vmin=min_effect_size,
|
||||
vmax=1.0,
|
||||
colormap='hot' # Use 'hot' to make positive connections pop
|
||||
colormap="hot",
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def get_single_subject_beta_corr(raw_hbo, event_name=None, config=None):
|
||||
"""Processes one participant and returns their correlation matrix."""
|
||||
raw_hbo = raw_hbo.copy().pick(picks="hbo")
|
||||
|
||||
Reference in New Issue
Block a user