"""Connectivity export helpers."""
from __future__ import annotations
from pathlib import Path
import numpy as np
import pandas as pd
from mrsiprep.connectivity.connectivity import compute_metabolic_profiles, correlate_metabolic_profiles
from mrsiprep.connectivity.edges import build_edges
from mrsiprep.connectivity.nodes import build_nodes
from mrsiprep.io.naming import subject_session_dir
def _processing_label(config, gm_weighted: bool) -> str:
processing = []
if config.filter_biharmonic:
processing.append("filt-biharmonic")
if not config.no_pvc:
processing.append("pvcorr_GM" if gm_weighted else "pvcorr")
return ("_" + "_".join(processing)) if processing else ""
def _scale_entity(scale: str | None) -> str:
scale_value = str(scale)[len("scale"):] if scale and str(scale).lower().startswith("scale") else scale
return f"_scale{scale_value}" if scale_value else ""
def _metabolic_profile_path(config, subject: str, session: str | None, atlas_name: str, scale: str | None, gm_weighted: bool, n_perturbations: int) -> Path:
out_dir = subject_session_dir(config.derivative_dir, subject, session, "connectivity")
out_dir.mkdir(parents=True, exist_ok=True)
prefix = f"sub-{subject}" + (f"_ses-{session}" if session else "")
processing_label = _processing_label(config, gm_weighted)
return out_dir / f"{prefix}_atlas-{atlas_name}{_scale_entity(scale)}_npert-{n_perturbations}{processing_label}_desc-metabolicprofiles_mrsi.npz"
def _connectivity_matrix_path(config, subject: str, session: str | None, atlas_name: str, scale: str | None, gm_weighted: bool, n_perturbations: int) -> Path:
out_dir = subject_session_dir(config.derivative_dir, subject, session, "connectivity")
out_dir.mkdir(parents=True, exist_ok=True)
prefix = f"sub-{subject}" + (f"_ses-{session}" if session else "")
processing_label = _processing_label(config, gm_weighted)
return out_dir / f"{prefix}_atlas-{atlas_name}{_scale_entity(scale)}_npert-{n_perturbations}{processing_label}_desc-connectivity_mrsi.npz"
def _filter_excluded_parcels(table: pd.DataFrame, exclude_patterns: str | None, max_parcel_id: int | None) -> pd.DataFrame:
if exclude_patterns:
patterns = [pattern.strip() for pattern in exclude_patterns.split(",") if pattern.strip()]
if patterns:
names = table["parcel_name"].astype(str)
mask = pd.Series(False, index=table.index)
for pattern in patterns:
mask |= names.str.contains(pattern, regex=False)
table = table[~mask]
if max_parcel_id is not None:
table = table[table["parcel_id"] < max_parcel_id]
return table
[docs]
def export_connectivity(
config,
subject: str,
session: str | None,
profiles,
table: pd.DataFrame,
atlas_name: str,
scale: str | None = None,
) -> dict[str, Path]:
"""Metabolic similarity matrix built from an already-computed
:class:`~mrsiprep.connectivity.connectivity.MetabolicProfileResult`
(see :func:`export_metabolic_profiles`). Optional add-on, gated on
``--write-connectivity``."""
result = correlate_metabolic_profiles(profiles, method=config.connectivity_method)
sim = result.similarity
name_by_id = table.drop_duplicates("parcel_id").set_index("parcel_id")["parcel_name"]
parcel_names = np.array([str(name_by_id.get(parcel_id, parcel_id)) for parcel_id in result.parcel_ids])
matrix_npz = _connectivity_matrix_path(config, subject, session, atlas_name, scale, result.gm_weighted, result.n_perturbations)
nodes_tsv = matrix_npz.with_name(matrix_npz.stem.replace("desc-connectivity", "desc-nodes") + ".tsv")
edges_tsv = matrix_npz.with_name(matrix_npz.stem.replace("desc-connectivity", "desc-edges") + ".tsv")
np.savez(
matrix_npz,
matrix=sim.to_numpy(),
parcel_concentrations=result.parcel_concentrations,
labels_indices=result.parcel_ids,
parcel_names=parcel_names,
metabolites=np.array(result.metabolites),
method=result.method,
npert=result.n_perturbations,
sigma_scale=result.sigma_scale,
gm_weighted=result.gm_weighted,
)
build_nodes(table).to_csv(nodes_tsv, sep="\t", index=False)
build_edges(sim, config.connectivity_method).to_csv(edges_tsv, sep="\t", index=False)
return {"matrix_npz": matrix_npz, "nodes": nodes_tsv, "edges": edges_tsv}