Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEAT Make seg and desc mandatory grouping #10

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[flake8]
doctests = False
exclude =
**/__init__.py
**/tests/*
*build/
wonkyconn/_version.py
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ dependencies = [
"numba",
"seaborn",
"matplotlib",
"statsmodels"
]
dynamic = ["version"]

Expand All @@ -41,6 +42,7 @@ dev = [
"types-all",
"pandas-stubs",
"types-tqdm",
"pyrsistent",
]
test = ["nibabel", "nilearn", "pytest", "pytest-cov", "templateflow < 23.0.0"]
docs = ["sphinx", "sphinx_rtd_theme", "myst-parser", "sphinx-argparse"]
Expand Down Expand Up @@ -94,6 +96,8 @@ module = [
"seaborn.*",
"statsmodels.*",
"templateflow.*",
"nibabel.*",
"pyrsistent.*",
]

[[tool.mypy.overrides]]
Expand Down
7 changes: 5 additions & 2 deletions wonkyconn/features/calculate_degrees_of_freedom.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ def calculate_degrees_of_freedom_loss(
Calculate the percent of degrees of freedom lost during denoising.

Parameters:
- bids_file (BIDSFile): The BIDS file for which to calculate the degrees of freedom.
- bids_file (BIDSFile): The BIDS file for which to calculate the
degrees of freedom.

Returns:
- float: The percentage of degrees of freedom lost.
Expand All @@ -50,7 +51,9 @@ def calculate_degrees_of_freedom_loss(


def _calculate_for_key(
connectivity_matrices: list[ConnectivityMatrix], count: list[int], key: str
connectivity_matrices: list[ConnectivityMatrix],
count: npt.NDArray[np.int64],
key: str,
) -> float:
values: Sequence[int | list[str] | None] = [
connectivity_matrix.metadata.get(key, None)
Expand Down
27 changes: 18 additions & 9 deletions wonkyconn/features/quality_control_connectivity.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,17 @@ def calculate_qcfc(
accounted for participant age and sex

Parameters:
data_frame (pd.DataFrame): The data frame containing the covariates "age" and "gender". It needs to have one row for each connectivity matrix.
connectivity_matrices (Iterable[ConnectivityMatrix]): The connectivity matrices to calculate QCFC for.
metric_key (str, optional): The key of the metric to use for QCFC calculation. Defaults to "MeanFramewiseDisplacement".
data_frame (pd.DataFrame): The data frame containing the covariates
"age" and "gender". It needs to have one row for each
connectivity matrix.
connectivity_matrices (Iterable[ConnectivityMatrix]): The
connectivity matrices to calculate QCFC for.
metric_key (str, optional): The key of the metric to use for QCFC
calculation. Defaults to "MeanFramewiseDisplacement".

Returns:
pd.DataFrame: The QCFC values between connectivity matrices and the metric.
pd.DataFrame: The QCFC values between connectivity matrices and
the metric.

"""
metrics = np.asarray(
Expand All @@ -45,7 +50,9 @@ def calculate_qcfc(
connectivity_arrays = [
connectivity_matrix.load()
for connectivity_matrix in tqdm(
connectivity_matrices, desc="Loading connectivity matrices", leave=False
connectivity_matrices,
desc="Loading connectivity matrices",
leave=False,
)
]

Expand All @@ -64,19 +71,21 @@ def calculate_qcfc(

p_value = correlation_p_value(correlation, m)

qcfc = pd.DataFrame(dict(i=i, j=j, correlation=correlation, p_value=p_value))
qcfc = pd.DataFrame(
dict(i=i, j=j, correlation=correlation, p_value=p_value)
)
qcfc = qcfc.set_index(["i", "j"])

return qcfc


def calculate_median_absolute(x: pd.Series) -> float:
def calculate_median_absolute(x: pd.Series[float]) -> float:
"""Calculate Absolute median value"""
return x.abs().median()


def significant_level(
x: pd.Series, alpha: float = 0.05, correction: str | None = None
x: pd.Series[float], alpha: float = 0.05, correction: str | None = None
) -> npt.NDArray[np.bool_]:
"""
Apply FDR correction to a pandas.Series p-value object.
Expand All @@ -92,7 +101,7 @@ def significant_level(

method : None or str
Default as None for no multiple comparison
Mutiple comparison methods.
Multiple comparison methods.
See statsmodels.stats.multitest.multipletests

Returns
Expand Down
2 changes: 1 addition & 1 deletion wonkyconn/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def gc_logger(log_level: str = "INFO") -> logging.Logger:
handlers=[RichHandler()],
)

return logging.getLogger("giga_connectome")
return logging.getLogger("wonkyconn")


gc_log = gc_logger()
Expand Down
24 changes: 18 additions & 6 deletions wonkyconn/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ def global_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
formatter_class=argparse.RawTextHelpFormatter,
description=(
"Evaluating the residual motion in fMRI connectome and visualize reports"
"Evaluating the residual motion in fMRI connectome and visualize "
"reports per segmentation per denoising strategy."
),
)

Expand Down Expand Up @@ -41,13 +42,19 @@ def global_parser() -> argparse.ArgumentParser:
"--group-by",
type=str,
nargs="+",
default=["seg"],
help="Select which tags to group the connectivity matrices by. Default is `seg`.",
help=(
"Select which entities to group the connectivity matrices by "
"(e.g. `sub`, `ses`, `task`)."
),
required=False,
)
parser.add_argument(
"--phenotypes",
type=str,
help="Path to the phenotype file that has the columns `participant_id`, `gender` coded as `M` and `F` and `age` in years.",
help=(
"Path to the phenotype file that has the columns `participant_id`,"
" `gender` coded as `M` and `F` and `age` in years."
),
required=True,
)
parser.add_argument(
Expand All @@ -57,10 +64,15 @@ def global_parser() -> argparse.ArgumentParser:
action="append",
metavar=("SEG", "ATLAS"),
default=list(),
help="Specify the atlas file to use for a segmentation label in the data",
help=(
"Specify the atlas file to use for a segmentation label in the "
"data"
),
)

parser.add_argument("-v", "--version", action="version", version=__version__)
parser.add_argument(
"-v", "--version", action="version", version=__version__
)
parser.add_argument("--debug", action="store_true", default=False)
parser.add_argument(
"--verbosity",
Expand Down
12 changes: 4 additions & 8 deletions wonkyconn/tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,7 @@ def test_help(capsys):
except SystemExit:
pass
captured = capsys.readouterr()
assert (
"Evaluating the residual motion in fMRI connectome and visualize reports"
in captured.out
)
assert "fMRI connectome and visualize reports" in captured.out


def _copy_file(path: Path, new_path: Path, sub: str) -> None:
Expand All @@ -49,7 +46,9 @@ def _copy_file(path: Path, new_path: Path, sub: str) -> None:
relmat = pd.read_csv(path, sep="\t")
(n,) = set(relmat.shape)

array = scipy.spatial.distance.squareform(relmat.to_numpy() - np.eye(n))
array = scipy.spatial.distance.squareform(
relmat.to_numpy() - np.eye(n)
)
np.random.shuffle(array)

new_array = scipy.spatial.distance.squareform(array) + np.eye(n)
Expand Down Expand Up @@ -115,9 +114,6 @@ def test_smoke(tmp_path: Path):
argv = [
"--phenotypes",
str(phenotypes_path),
"--group-by",
"seg",
"desc",
*seg_to_atlas_args,
str(bids_dir),
str(output_dir),
Expand Down
43 changes: 30 additions & 13 deletions wonkyconn/visualization/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
matplotlib.rcParams["font.family"] = "DejaVu Sans"


def _make_group_label(group_by: list[str], values: pd.Series) -> str:
def _make_group_label(group_by: list[str], values: pd.Series[str]) -> str:
label: str = ""
for a, b in zip(group_by, values, strict=True):
if label:
Expand All @@ -22,17 +22,23 @@ def _make_group_label(group_by: list[str], values: pd.Series) -> str:
return label


def plot(result_frame: pd.DataFrame, group_by: list[str], output_dir: Path) -> None:
def plot(
result_frame: pd.DataFrame, group_by: list[str], output_dir: Path
) -> None:
"""
Plot all three metrics based on the given result data frame.

Args:
result_frame (pd.DataFrame): The DataFrame containing the the columns "median_absolute_qcfc",
"percentage_significant_qcfc", "distance_dependence", "confound_regression_percentage",
"motion_scrubbing_percentage", and "nonsteady_states_detector_percentage", and the
columns in the `group_by` variable.
group_by (list[str]): The list of columns that the results are grouped by.
output_dir (Path): The directory to save the plot image into as "metrics.png".
result_frame (pd.DataFrame): The DataFrame containing the the columns
"median_absolute_qcfc", "percentage_significant_qcfc",
"distance_dependence", "confound_regression_percentage",
"motion_scrubbing_percentage", and
"nonsteady_states_detector_percentage", and the columns in the
`group_by` variable.
group_by (list[str]): The list of columns that the results are
grouped by.
output_dir (Path): The directory to save the plot image into as
"metrics.png".

Returns:
None
Expand Down Expand Up @@ -60,7 +66,9 @@ def plot(result_frame: pd.DataFrame, group_by: list[str], output_dir: Path) -> N
color=palette[0],
ax=median_absolute_qcfc_axes,
)
median_absolute_qcfc_axes.set_title("Median absolute value of QC-FC correlations")
median_absolute_qcfc_axes.set_title(
"Median absolute value of QC-FC correlations"
)
median_absolute_qcfc_axes.set_xlabel("Median absolute value")
median_absolute_qcfc_axes.set_ylabel("Group")

Expand Down Expand Up @@ -93,7 +101,7 @@ def plot(result_frame: pd.DataFrame, group_by: list[str], output_dir: Path) -> N

def plot_degrees_of_freedom_loss(
result_frame: pd.DataFrame,
group_labels: pd.Series,
group_labels: pd.Series[str],
degrees_of_freedom_loss_axes: Axes,
legend_axes: Axes,
) -> None:
Expand All @@ -116,9 +124,18 @@ def plot_degrees_of_freedom_loss(
color=colors[2],
ax=degrees_of_freedom_loss_axes,
)
degrees_of_freedom_loss_axes.set_title("Percentage of degrees of freedom lost")
degrees_of_freedom_loss_axes.set_title(
"Percentage of degrees of freedom lost"
)
degrees_of_freedom_loss_axes.set_xlabel("Percentage %")
labels = ["Confounds regression", "Motion scrubbing", "Non-steady states detector"]
handles = [mpatches.Patch(color=c, label=label) for c, label in zip(colors, labels)]
labels = [
"Confounds regression",
"Motion scrubbing",
"Non-steady states detector",
]
handles = [
mpatches.Patch(color=c, label=label)
for c, label in zip(colors, labels)
]
legend_axes.legend(handles=handles)
legend_axes.axis("off")
Loading
Loading