From f4b0ecafd07306ea407c83f39fe6ca5fabc2ddc6 Mon Sep 17 00:00:00 2001 From: timonmerk Date: Mon, 11 Mar 2024 18:31:37 +0100 Subject: [PATCH] Revert "Add grid-write up and connectome interface" This reverts commit 5e34446a8b8c1bb044dbc2c90b9e15184d71623b. --- .gitignore | 2 +- example_stream_mne_lsl.py | 67 ----- logging_example.py | 41 --- logging_example_2.py | 43 --- ...ted Anatomical Labeling 3 (Rolls 2020).nii | Bin 902981 -> 0 bytes .../_zenodo_downloader.py | 9 - .../ConnectivityDecoding/get_grid_hull.m | 34 --- .../get_grid_whole_brain.py | 106 ------- .../helper_write_connectome.py | 180 ++++++----- py_neuromodulation/nm_RMAP.py | 279 ++---------------- pyproject.toml | 53 ++-- write_example_RMAP.py | 32 +- 12 files changed, 179 insertions(+), 667 deletions(-) delete mode 100644 example_stream_mne_lsl.py delete mode 100644 logging_example.py delete mode 100644 logging_example_2.py delete mode 100644 py_neuromodulation/ConnectivityDecoding/Automated Anatomical Labeling 3 (Rolls 2020).nii delete mode 100644 py_neuromodulation/ConnectivityDecoding/_zenodo_downloader.py delete mode 100644 py_neuromodulation/ConnectivityDecoding/get_grid_hull.m delete mode 100644 py_neuromodulation/ConnectivityDecoding/get_grid_whole_brain.py diff --git a/.gitignore b/.gitignore index 28416b1e..ae588e98 100644 --- a/.gitignore +++ b/.gitignore @@ -167,6 +167,6 @@ write_example_RMAP.np .python-version -py_neuromodulation/ConnectivityDecoding/connectome_folder/* +py_neuromodulation/ConnectivityDecoding/connectome_struct.mat plot_0_first_demo.py diff --git a/example_stream_mne_lsl.py b/example_stream_mne_lsl.py deleted file mode 100644 index cd014ccd..00000000 --- a/example_stream_mne_lsl.py +++ /dev/null @@ -1,67 +0,0 @@ -from mne_lsl.player import PlayerLSL -from mne_lsl.stream import StreamLSL -from mne_lsl import stream_viewer -from matplotlib import pyplot as plt -import numpy as np -import threading -import time - -if __name__ == "__main__": - - f_name = r"C:\code\py_neuromodulation\py_neuromodulation\data\sub-testsub\ses-EphysMedOff\ieeg\sub-testsub_ses-EphysMedOff_task-gripforce_run-0_ieeg.vhdr" - # raw = mne.io.read_raw_brainvision(f_name) - # plt.figure() - # plt.plot([1, 4, 5]) - # plt.show(block=True) - - player = PlayerLSL(f_name, name="example_stream", chunk_size=100) - player = player.start() - - player.info - - sfreq = player.info["sfreq"] - - chunk_size = player.chunk_size - interval = chunk_size / sfreq # in seconds - print(f"Interval between 2 push operations: {interval} seconds.") - - stream = StreamLSL(name="example_stream", bufsize=2).connect() - ch_types = stream.get_channel_types(unique=True) - print(f"Channel types included: {', '.join(ch_types)}") - - viewer = stream_viewer.StreamViewer(stream_name="example_stream") - # viewer.start() - - data_l = [] - timestamps_l = [] - idx_ = 0 - - # start = time.time() - last_time = time.time() - - time_diff_pull = 0.001 - time_diff_pull = 0 - while True: - if time.time() - last_time >= time_diff_pull: - time_diff = time.time() - last_time - last_time = time.time() - print(last_time) - # the shape of the returned data will be defined by ceil(winsize * self._info["sfreq"]) - # using this method I can obtain repetitively previous data - - # - data, timestamps = stream.get_data( - winsize=0.1 - ) # get here only the new data - print(data.shape) - data_l.append(data) - timestamps_l.append(time_diff) - - # plt.figure() - # plt.plot(timestamps) - # plt.show(block=True) - # check here 1. the timing - print(np.concatenate(data_l).shape) - - stream.disconnect() - player.stop() diff --git a/logging_example.py b/logging_example.py deleted file mode 100644 index 4a095404..00000000 --- a/logging_example.py +++ /dev/null @@ -1,41 +0,0 @@ -import logging -import logging_example_2 - - -# from logging_example_2 import logger -logger = logging.getLogger("MyLogger") - - -class SecondLogger: - def __init__(self) -> None: - pass - - def call_logging_warnining( - self, - ): - logger.warning("Logger 2: Warning") - - def call_logging_error( - self, - ): - try: - raise ValueError("Logger 2: Error") - except ValueError as e: - # logging.exception("message") - logger.exception(e, exc_info=True) - - -if __name__ == "__main__": - - log_1 = logging_example_2.FirstLogger() - log_1.call_logging_warnining() - - # log_1 has a logger that writes to a file, how can the logger in this file be used to write to the same file? - - log_2 = SecondLogger() - log_2.call_logging_warnining() - log_2.call_logging_error() - - logger.info("This is a test") - - # write now the logging output to a file diff --git a/logging_example_2.py b/logging_example_2.py deleted file mode 100644 index 7fc92f80..00000000 --- a/logging_example_2.py +++ /dev/null @@ -1,43 +0,0 @@ -import logging - -# include the filename in the log output -# Configure the logger -logger = logging.getLogger("MyLogger") -logger.setLevel(logging.INFO) - -# Create a file handler and set its level to DEBUG -file_handler = logging.FileHandler("logfile.log") -file_handler.setLevel(logging.INFO) - -console_handler = logging.StreamHandler() -console_handler.setLevel(logging.DEBUG) - -# Create a formatter and add it to the handler -formatter = logging.Formatter( - "%(asctime)s:%(levelname)s:%(name)s:%(filename)s:%(message)s" -) -file_handler.setFormatter(formatter) -console_handler.setFormatter(formatter) - -# Add the file handler to the logger -logger.addHandler(file_handler) -logger.addHandler(console_handler) - - -class FirstLogger: - def __init__(self) -> None: - pass - - def call_logging_warnining( - self, - ): - logger.warning("Logger 1: Warning") - - def call_logging_error( - self, - ): - try: - raise ValueError("Logger 2: Error") - except ValueError as e: - # logging.exception("message") - logger.exception(e, exc_info=True) diff --git a/py_neuromodulation/ConnectivityDecoding/Automated Anatomical Labeling 3 (Rolls 2020).nii b/py_neuromodulation/ConnectivityDecoding/Automated Anatomical Labeling 3 (Rolls 2020).nii deleted file mode 100644 index edfda51bc5901f9a0325fe62de6ab0b8ff264895..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 902981 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[Y0*ohodhbP@#WE+~kr4NO`gWara{GQ%|FLi-~ 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 O1`HT5V8DO@0~-jS39K;y diff --git a/py_neuromodulation/ConnectivityDecoding/_zenodo_downloader.py b/py_neuromodulation/ConnectivityDecoding/_zenodo_downloader.py deleted file mode 100644 index 45e0ce95..00000000 --- a/py_neuromodulation/ConnectivityDecoding/_zenodo_downloader.py +++ /dev/null @@ -1,9 +0,0 @@ -import wget - -record_id = "10804702" -file_name = "AGPotData.csv" - -wget.download( - f"https://zenodo.org/api/records/{record_id}/files/{file_name}/content", - out=f"{file_name}.mat", -) diff --git a/py_neuromodulation/ConnectivityDecoding/get_grid_hull.m b/py_neuromodulation/ConnectivityDecoding/get_grid_hull.m deleted file mode 100644 index 8dcc16b6..00000000 --- a/py_neuromodulation/ConnectivityDecoding/get_grid_hull.m +++ /dev/null @@ -1,34 +0,0 @@ -addpath('C:\code\wjn_toolbox'); -addpath(genpath('C:\code\leaddbs')); -addpath(genpath('C:\code\spm12')); - - -%% -ctx = wjn_mni_cortex(); -downsample_ctx=ctx.vertices(1:20:end,:); %downsample by 10 - -save("downsampled_cortex.mat", "downsample_ctx") - -figure; -scatter3(downsample_ctx(:,1), downsample_ctx(:,2), downsample_ctx(:,3), 'filled'); -title('3D Scatter Plot Example'); -xlabel('X-axis'); -ylabel('Y-axis'); -zlabel('Z-axis'); -grid on; - - - -PATH_OUT = "D:\Connectome_RMAP_OUT\ROIs"; - -for a =1:size(downsample_ctx,1) - disp(a) - roiname = fullfile(PATH_OUT, strcat('ROI-', string(a), '.nii')); - mni = [downsample_ctx(a, 1) downsample_ctx(a, 2) downsample_ctx(a, 3)]; - wjn_spherical_roi(roiname,mni,4); -end - - - - - diff --git a/py_neuromodulation/ConnectivityDecoding/get_grid_whole_brain.py b/py_neuromodulation/ConnectivityDecoding/get_grid_whole_brain.py deleted file mode 100644 index eef535ba..00000000 --- a/py_neuromodulation/ConnectivityDecoding/get_grid_whole_brain.py +++ /dev/null @@ -1,106 +0,0 @@ -import nibabel as nib -import numpy as np -import scipy.io as sio -import os -from matplotlib import pyplot as plt - - -class NiiToMNI: - - def __init__( - self, - PATH_nii_file: str = r"C:\code\RMap_ROI_Estimation\Automated Anatomical Labeling 3 (Rolls 2020).nii", - ) -> None: - - self.img = nib.load(PATH_nii_file) - self.data = self.img.get_fdata() - - def downsample_nii( - self, - resampling_factor: int = 150, - ): - - # PATH_MNI_TO_ATLAS = r"C:\code\mni_to_atlas\src\mni_to_atlas\atlases\AAL.nii" - # img_mni_to_atlas = nib.load(PATH_MNI_TO_ATLAS) - - x_dim, y_dim, z_dim = self.data.shape - - # Create arrays of voxel coordinates - x_coords, y_coords, z_coords = np.meshgrid( - range(x_dim), range(y_dim), range(z_dim), indexing="ij" - ) - - # Downsample here the voxels --> check lateron if the voxels have non-zero values - x_c_flatten = x_coords.flatten()[::resampling_factor] - y_c_flatten = y_coords.flatten()[::resampling_factor] - z_c_flatten = z_coords.flatten()[::resampling_factor] - - # Combine coordinates into a single array - voxel_coordinates = np.column_stack( - ( - x_c_flatten, - y_c_flatten, - z_c_flatten, - np.ones(x_c_flatten.shape[0]), - ) - ) - - aff_m = self.img.affine - aff_m[0, 0] = 2 - aff_m[0, 3] = -90 - - mni_coordinates = np.dot(aff_m, voxel_coordinates.T).T[:, :3] - - return mni_coordinates - - def select_non_zero_voxels( - self, - mni_coordinates: np.array, - ): - - coords = np.hstack( - (mni_coordinates, np.ones((mni_coordinates.shape[0], 1))) - ) - - # and transform back to get the voxel values - voxels_downsampled = np.array( - np.linalg.solve(self.img.affine, coords.T).T - ).astype(int)[:, :3] - - ival = [] - coord_ = [] - for i in range(voxels_downsampled.shape[0]): - ival.append(self.data[tuple(voxels_downsampled[i, :])]) - coord_.append(mni_coordinates[i, :]) - - # get only voxel values non-zero - ival = np.array(ival) - coord_ = np.array(coord_) - ival_non_zero = ival[ival != 0] - coord_non_zero = coord_[ival != 0] - print(coord_non_zero.shape) - - return coord_non_zero, ival_non_zero - - def plot_3d_coordinates(self, coord_non_zero: np.array): - fig = plt.figure() - ax = fig.add_subplot(111, projection="3d") - ax.scatter( - coord_non_zero[:, 0], - coord_non_zero[:, 1], - coord_non_zero[:, 2], - s=50, - alpha=0.2, - ) - plt.show() - - -if __name__ == "__main__": - - nii_to_mni = NiiToMNI( - PATH_nii_file=r"C:\code\py_neuromodulation\ConnectivityDecoding\Automated Anatomical Labeling 3 (Rolls 2020).nii" - ) - mni_coordinates = nii_to_mni.downsample_nii(resampling_factor=150) - coord_non_zero, ival_non_zero = nii_to_mni.select_non_zero_voxels( - mni_coordinates - ) diff --git a/py_neuromodulation/ConnectivityDecoding/helper_write_connectome.py b/py_neuromodulation/ConnectivityDecoding/helper_write_connectome.py index 5ec2b257..47ddea71 100644 --- a/py_neuromodulation/ConnectivityDecoding/helper_write_connectome.py +++ b/py_neuromodulation/ConnectivityDecoding/helper_write_connectome.py @@ -5,6 +5,84 @@ from matplotlib import pyplot as plt +class NiiToMNI: + + def __init__( + self, + PATH_nii_file: str = r"C:\code\RMap_ROI_Estimation\Automated Anatomical Labeling 3 (Rolls 2020).nii", + ) -> None: + + self.img = nib.load(PATH_nii_file) + self.data = self.img.get_fdata() + + def downsample_nii( + self, + resampling_factor: int = 150, + ): + + # PATH_MNI_TO_ATLAS = r"C:\code\mni_to_atlas\src\mni_to_atlas\atlases\AAL.nii" + # img_mni_to_atlas = nib.load(PATH_MNI_TO_ATLAS) + + x_dim, y_dim, z_dim = self.data.shape + + # Create arrays of voxel coordinates + x_coords, y_coords, z_coords = np.meshgrid( + range(x_dim), range(y_dim), range(z_dim), indexing="ij" + ) + + # Downsample here the voxels --> check lateron if the voxels have non-zero values + x_c_flatten = x_coords.flatten()[::resampling_factor] + y_c_flatten = y_coords.flatten()[::resampling_factor] + z_c_flatten = z_coords.flatten()[::resampling_factor] + + # Combine coordinates into a single array + voxel_coordinates = np.column_stack( + ( + x_c_flatten, + y_c_flatten, + z_c_flatten, + np.ones(x_c_flatten.shape[0]), + ) + ) + + aff_m = self.img.affine + aff_m[0, 0] = 2 + aff_m[0, 3] = -90 + + mni_coordinates = np.dot(aff_m, voxel_coordinates.T).T[:, :3] + + return mni_coordinates + + def select_non_zero_voxels( + self, + mni_coordinates: np.array, + ): + + coords = np.hstack( + (mni_coordinates, np.ones((mni_coordinates.shape[0], 1))) + ) + + # and transform back to get the voxel values + voxels_downsampled = np.array( + np.linalg.solve(self.img.affine, coords.T).T + ).astype(int)[:, :3] + + ival = [] + coord_ = [] + for i in range(voxels_downsampled.shape[0]): + ival.append(self.data[tuple(voxels_downsampled[i, :])]) + coord_.append(mni_coordinates[i, :]) + + # get only voxel values non-zero + ival = np.array(ival) + coord_ = np.array(coord_) + ival_non_zero = ival[ival != 0] + coord_non_zero = coord_[ival != 0] + print(coord_non_zero.shape) + + return coord_non_zero, ival_non_zero + + def write_connectome_mat( PATH_Fingerprints: str = r"D:\Connectome_RMAP_OUT\ROIs\HCP1000 6K", PATH_CONNECTOME: str = os.path.join( @@ -12,65 +90,22 @@ def write_connectome_mat( "ConnectivityDecoding", "connectome_struct.mat", ), - func_: bool = False, ): # connectome = sio.loadmat(PATH_CONNECTOME) # check if read was successful # load all fingerprints and put them in .npy dict_connectome = {} - if func_ is False: - files_fps = [f for f in os.listdir(PATH_Fingerprints) if ".nii" in f] - else: - files_fps = [ - f - for f in os.listdir(PATH_Fingerprints) - if "func_seed_AvgR_Fz.nii" in f - ] - - # I except 1025 files, check which ones are missing - missing_files = [] - - for i in range(1, 1026): - - MISSING = False - - if func_ is False: - if f"ROI-{i}_struc_seed.nii" not in files_fps: - missing_files.append(f"ROI-{i}_struc_seed.nii") - MISSING = True - else: - if f"ROI-{i}_func_seed_AvgR_Fz.nii" not in files_fps: - missing_files.append(f"ROI-{i}_func_seed_AvgR_Fz.nii") - MISSING = True - - if MISSING: - ROI_file = os.path.join( - f"D:\Connectome_RMAP_OUT\whole_brain\ROIs", f"ROI-{i}.nii" - ) - # copy the ROI file to the following folder: - PATH_ROI_OUT = ( - r"D:\Connectome_RMAP_OUT\whole_brain\ROI_missing_struc" - ) - import shutil - - shutil.copy(ROI_file, os.path.join(PATH_ROI_OUT, f"ROI-{i}.nii")) - - for idx, f in enumerate(files_fps): + files_fps = [f for f in os.listdir(PATH_Fingerprints) if ".nii" in f] + for f in files_fps: # load the .nii file and put it all in in a dictionary with the name of the file fp = ( nib.load(os.path.join(PATH_Fingerprints, f)) .get_fdata() .astype(np.float16) ) - if "struc" in f: - dict_connectome[f[f.find("ROI-") + 4 : f.find("_struc")]] = fp - else: - dict_connectome[ - f[f.find("ROI-") + 4 : f.find("_func_seed_AvgR_Fz.nii")] - ] = fp - - print(idx) + dict_connectome[f[f.find("ROI-") + 4 : f.find("_struc")]] = fp + # save the dictionary sio.savemat( PATH_CONNECTOME, @@ -80,40 +115,21 @@ def write_connectome_mat( if __name__ == "__main__": - write_connectome_mat( - PATH_Fingerprints=r"D:\Connectome_RMAP_OUT\whole_brain\struc\HCP1000 6K", - PATH_CONNECTOME=os.path.join( - "py_neuromodulation", - "ConnectivityDecoding", - "connectome_whole_brain_struc.mat", - ), - ) # 58 files are missing - - write_connectome_mat( - PATH_Fingerprints=r"D:\Connectome_RMAP_OUT\whole_brain\func", - PATH_CONNECTOME=os.path.join( - "py_neuromodulation", - "ConnectivityDecoding", - "connectome_whole_brain_func.mat", - ), - func_=True, + nii_to_mni = NiiToMNI( + PATH_nii_file=r"C:\code\RMap_ROI_Estimation\Automated Anatomical Labeling 3 (Rolls 2020).nii" + ) + mni_coordinates = nii_to_mni.downsample_nii(resampling_factor=150) + coord_non_zero, ival_non_zero = nii_to_mni.select_non_zero_voxels( + mni_coordinates ) - write_connectome_mat( - PATH_Fingerprints=r"D:\Connectome_RMAP_OUT\hull\func\GSP 1000 (Yeo 2011)_Full Set (Yeo 2011)", - PATH_CONNECTOME=os.path.join( - "py_neuromodulation", - "ConnectivityDecoding", - "connectome_hull_func.mat", - ), - func_=True, - ) # all there - - write_connectome_mat( - PATH_Fingerprints=r"D:\Connectome_RMAP_OUT\hull\struc\HCP1000 6K", - PATH_CONNECTOME=os.path.join( - "py_neuromodulation", - "ConnectivityDecoding", - "connectome_hull_struc.mat", - ), - ) # 5 missing + fig = plt.figure() + ax = fig.add_subplot(111, projection="3d") + ax.scatter( + coord_non_zero[:, 0], + coord_non_zero[:, 1], + coord_non_zero[:, 2], + s=50, + alpha=0.2, + ) + plt.show() diff --git a/py_neuromodulation/nm_RMAP.py b/py_neuromodulation/nm_RMAP.py index 88adc391..f7ccc8f6 100644 --- a/py_neuromodulation/nm_RMAP.py +++ b/py_neuromodulation/nm_RMAP.py @@ -1,205 +1,43 @@ +import enum +import nibabel as nib import numpy as np import os -import wget # from numba import jit from scipy import stats import scipy.io as sio import pandas as pd -from typing import Union, Tuple, List -import nibabel as nib -from matplotlib import pyplot as plt +from typing import Union import py_neuromodulation from py_neuromodulation import nm_plots -LIST_STRUC_UNCONNECTED_GRIDPOINTS_HULL = [256, 385, 417, 447, 819, 914] -LIST_STRUC_UNCONNECTED_GRIDPOINTS_WHOLEBRAIN = [ - 1, - 8, - 16, - 33, - 34, - 35, - 36, - 37, - 51, - 75, - 77, - 78, - 99, - 109, - 115, - 136, - 155, - 170, - 210, - 215, - 243, - 352, - 359, - 361, - 415, - 416, - 422, - 529, - 567, - 569, - 622, - 623, - 625, - 627, - 632, - 633, - 634, - 635, - 639, - 640, - 641, - 643, - 644, - 650, - 661, - 663, - 667, - 683, - 684, - 685, - 704, - 708, - 722, - 839, - 840, - 905, - 993, - 1011, -] - class ConnectivityChannelSelector: - def __init__( - self, - whole_brain_connectome: bool = True, - func_connectivity: bool = True, - ) -> None: - """ConnectivityChannelSelector - - Parameters - ---------- - whole_brain_connectome : bool, optional - if True a 1236 whole-brain point grid is chosen, - if False, a 1025 point grid of the cortical hull is loaded, - by default True - func_connectivity : bool, optional - if true, functional connectivity fMRI is loaded, - if false structural dMRIby, default True - """ - - self.connectome_name = self._get_connectome_name( - whole_brain_connectome, func_connectivity - ) - - self.whole_brain_connectome = whole_brain_connectome - self.func_connectivity = func_connectivity + def __init__(self) -> None: self.PATH_CONN_DECODING = os.path.join( py_neuromodulation.__path__[0], "ConnectivityDecoding", ) - - if whole_brain_connectome: - self.PATH_GRID = os.path.join( - self.PATH_CONN_DECODING, - "mni_coords_whole_brain.mat", - ) - self.grid = sio.loadmat(self.PATH_GRID)["downsample_ctx"] - if func_connectivity is False: - # reduce the grid to only valid points that are not in LIST_STRUC_UNCONNECTED_GRIDPOINTS_WHOLEBRAIN - self.grid = np.delete( - self.grid, - LIST_STRUC_UNCONNECTED_GRIDPOINTS_WHOLEBRAIN, - axis=0, - ) - else: - self.PATH_GRID = os.path.join( - self.PATH_CONN_DECODING, - "mni_coords_cortical_surface.mat", - ) - self.grid = sio.loadmat(self.PATH_GRID)["downsample_ctx"] - if func_connectivity is False: - # reduce the grid to only valid points that are not in LIST_STRUC_UNCONNECTED_GRIDPOINTS_HULL - self.grid = np.delete( - self.grid, LIST_STRUC_UNCONNECTED_GRIDPOINTS_HULL, axis=0 - ) - - if func_connectivity: - self.RMAP_arr = nib.load( - os.path.join(self.PATH_CONN_DECODING, "RMAP_func_all.nii") - ).get_fdata() - else: - self.RMAP_arr = nib.load( - os.path.join(self.PATH_CONN_DECODING, "RMAP_struc.nii") - ).get_fdata() - - def _get_connectome_name( - self, whole_brain_connectome: str, func_connectivity: str - ): - - connectome_name = "connectome_" - if whole_brain_connectome: - connectome_name += "whole_brain_" - else: - connectome_name += "hull_" - if func_connectivity: - connectome_name += "func" - else: - connectome_name += "struc" - return connectome_name - - def get_available_connectomes(self) -> list: - """Return list of saved connectomes in the - package folder/ConnectivityDecoding/connectome_folder/ folder. - - Returns - ------- - list_connectomes: list - """ - return os.listdir( - os.path.join( - self.PATH_CONN_DECODING, - "connectome_folder", - ) - ) - - def plot_grid(self) -> None: - """Plot the loaded template grid that passed coordinates are matched to.""" - - fig = plt.figure() - ax = fig.add_subplot(111, projection="3d") - ax.scatter( - self.grid[:, 0], self.grid[:, 1], self.grid[:, 2], s=50, alpha=0.2 + self.PATH_GRID = os.path.join( + self.PATH_CONN_DECODING, + "downsampled_cortex.mat", ) - plt.show() - - def get_closest_node( - self, coord: Union[List, np.array] - ) -> Tuple[List, List]: - """Given a list or np.array of coordinates, return the closest nodes in the - grid and their indices. - - Parameters - ---------- - coord : np.array - MNI coordinates with shape (num_channels, 3) - - Returns - ------- - Tuple[List, List] - Grid coordinates, grid indices - """ + self.grid = sio.loadmat(self.PATH_GRID)["downsample_ctx"] + self.RMAP_func = nib.load( + os.path.join(self.PATH_CONN_DECODING, "RMAP_func_all.nii") + ).get_fdata() + self.RMAP_struct = nib.load( + os.path.join(self.PATH_CONN_DECODING, "RMAP_struc.nii") + ).get_fdata() + + def get_closest_node(self, coord: np.array): + # shape of coord: (num_channels, 3) + # returns the index of the closest node in the grid idx_ = [] for c in coord: dist = np.linalg.norm(self.grid - c, axis=1) @@ -208,62 +46,25 @@ def get_closest_node( return [self.grid[idx] for idx in idx_], idx_ def get_rmap_correlations( - self, fps: Union[list, np.array], RMAP_use: np.array = None - ) -> List: - """Calculate correlations of passed fingerprints with the RMAP - - Parameters - ---------- - fps : Union[list, np.array] - List of fingerprints - RMAP_use : np.array, optional - Passed RMAP, by default None - - Returns - ------- - List - correlation values - """ - - RMAP_ = self.RMAP_arr if RMAP_use is None else RMAP_use - RMAP_ = RMAP_.flatten() + self, fps: Union[list, np.array], struct_corr: bool = True + ): + if struct_corr: + RMAP_use = self.RMAP_struct + else: + RMAP_use = self.RMAP_func corrs = [] for fp in fps: - corrs.append(np.corrcoef(RMAP_, fp.flatten())[0][1]) + corrs.append(np.corrcoef(RMAP_use, fp)[0][1]) return corrs - def load_connectome( + def _check_connectome( self, - whole_brain_connectome: bool = None, - func_connectivity: bool = None, - ) -> None: - """Load connectome, if not available download connectome from - Zenodo. - - Parameters - ---------- - whole_brain_connectome : bool, optional - if true whole brain connectome - if false cortical hull grid connectome, by default None - func_connectivity : bool, optional - if true fMRI if false dMRI, by default None - """ - - if whole_brain_connectome is not None: - self.whole_brain_connectome = whole_brain_connectome - if func_connectivity is not None: - self.func_connectivity = func_connectivity - - self.connectome_name = self._get_connectome_name( - self.whole_brain_connectome, self.func_connectivity - ) - + ): PATH_CONNECTOME = os.path.join( - self.PATH_CONN_DECODING, - "connectome_folder", - self.connectome_name + ".mat", + py_neuromodulation.__path__[0], + "ConnectivityDecoding", + "connectome_struct.mat", ) - if os.path.exists(PATH_CONNECTOME) is False: user_input = input( "Do you want to download the connectome? (yes/no): " @@ -273,31 +74,15 @@ def load_connectome( elif user_input == "no": print("Connectome missing, has to be downloaded") - self.connectome = sio.loadmat(PATH_CONNECTOME) - - def get_grid_fingerprints(self, grid_idx: Union[list, np.array]) -> list: - return [self.connectome[str(grid_idx)] for grid_idx in grid_idx] - def download_connectome( self, ): # download the connectome from the Zenodo API print("Downloading the connectome...") - - record_id = "10804702" - file_name = self.connectome_name - - wget.download( - f"https://zenodo.org/api/records/{record_id}/files/{file_name}/content", - out=os.path.join( - self.PATH_CONN_DECODING, - "connectome_folder", - f"{self.connectome_name}.mat", - ), - ) + ... -class RMAPCross_Val_ChannelSelector: +class RMAPChannelSelector: def __init__(self) -> None: pass diff --git a/pyproject.toml b/pyproject.toml index e911b9de..22f1f8e3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,32 +29,33 @@ readme = "README.rst" requires-python = ">=3.10" dependencies = [ - "mne", - "filterpy >= 1.4.5", - "fooof", - "imbalanced-learn", - "matplotlib >= 3.3.4", - "mne-bids >= 0.8", - "mne-connectivity", - "mrmr-selection", - "nolds", - "numpy >= 1.21.2", - "pandas >= 1.2.2", - "pip", - "pynput", - "pybids", - "scikit-image", - "scikit-learn >= 0.24.2", - "scikit-optimize", - "scipy >= 1.7.1", - "seaborn >= 0.11", - "notebook", - "ipython", - "pybispectra>=1.0.0", - "pyparrm", - "pyarrow>=14.0.2", - "joblib>=1.3.2", - "black>=24.2.0", + "mne", + "filterpy >= 1.4.5", + "fooof", + "imbalanced-learn", + "matplotlib >= 3.3.4", + "mne-bids >= 0.8", + "mne-connectivity", + "mrmr-selection", + "nolds", + "numpy >= 1.21.2", + "pandas >= 1.2.2", + "pip", + "pynput", + "pybids", + "scikit-image", + "scikit-learn >= 0.24.2", + "scikit-optimize", + "scipy >= 1.7.1", + "seaborn >= 0.11", + "notebook", + "ipython", + "pybispectra>=1.0.0", + # + "pyparrm", + "pyarrow>=14.0.2", + "joblib>=1.3.2", + "black>=24.2.0", ] [project.optional-dependencies] diff --git a/write_example_RMAP.py b/write_example_RMAP.py index e3f3c50c..c5d14d9c 100644 --- a/write_example_RMAP.py +++ b/write_example_RMAP.py @@ -5,18 +5,28 @@ if __name__ == "__main__": - ch_sel = nm_RMAP.ConnectivityChannelSelector( - whole_brain_connectome=True, func_connectivity=False - ) - - ch_sel.get_available_connectomes() + ch_sel = nm_RMAP.ConnectivityChannelSelector() - ch_sel.load_connectome() - # ch_sel.plot_grid() + # make a 3D plot of ch_sel.grid + fig = plt.figure() + ax = fig.add_subplot(111, projection="3d") + ax.scatter( + ch_sel.grid[:, 0], ch_sel.grid[:, 1], ch_sel.grid[:, 2], s=50, alpha=0.2 + ) + plt.show() mni_coords = [[10, 40, 20], [50, 14, 12]] - grid_coords, grid_idxs = ch_sel.get_closest_node(mni_coords) - - grid_fps = ch_sel.get_grid_fingerprints(grid_idxs) + fps, grid_idxs = ch_sel.get_closest_node(mni_coords) + + fig = plt.figure() + ax = fig.add_subplot(111, projection="3d") + ax.scatter( + np.array(mni_coords)[0, :], + np.array(mni_coords)[1, :], + np.array(mni_coords)[2, :], + s=50, + alpha=0.2, + ) + plt.show() - corrs = ch_sel.get_rmap_correlations(grid_fps, ch_sel.RMAP_arr) + corrs = ch_sel.get_rmap_correlations(fps, struct_corr=True)