├── .github └── workflows │ └── python-package.yml ├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.md ├── cepy ├── __init__.py ├── ce.py ├── data │ ├── ce_group.json.gz │ ├── ce_subject1.json │ ├── ce_subject1.json.gz │ ├── ce_subject2.json.gz │ ├── generate_example_ce.py │ ├── sc_group_matrix.npz │ ├── sc_subject1_matrix.npz │ └── sc_subject2_matrix.npz ├── embed_align.py ├── parallel.py ├── setup.cfg └── utils.py ├── docs ├── Makefile ├── conf.py ├── docs_header.png ├── index.rst └── source │ ├── cepy.rst │ └── modules.rst ├── examples ├── README.md ├── ce_prediction.ipynb ├── ce_subjects_pipeline.ipynb ├── data │ ├── NKI_200_schaefer_sc_matrices.npz │ ├── NKI_200_schaefer_subjects_ce.npz │ ├── NKI_demographics.csv │ ├── group_sc_matrix.npz │ ├── schaefer200_yeo17_networks.csv │ ├── schaefer200_yeo17_nodeName.csv │ ├── sub1_fc_matrix.npz │ ├── sub1_sc_matrix.npz │ ├── sub2_fc_matrix.npz │ └── sub2_sc_matrix.npz ├── images │ ├── ce_alignment.png │ ├── ce_individuals.png │ ├── ce_workflow.png │ ├── ce_workflow_full.png │ ├── ce_workflow_width.png │ ├── p_q_parames.png │ ├── random_walks.png │ └── structure_to_function.png ├── inter_embedding_alignment.ipynb ├── intra_embedding_alignment.ipynb ├── learn_embedding.ipynb └── random_walks_generation.ipynb ├── requirements.txt └── setup.py /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: Python package 5 | 6 | on: 7 | push: 8 | branches: 9 | - '**' 10 | pull_request: 11 | branches: 12 | - '**' 13 | 14 | jobs: 15 | build: 16 | 17 | runs-on: ubuntu-latest 18 | strategy: 19 | matrix: 20 | python-version: ['3.6', '3.7', '3.8'] 21 | 22 | steps: 23 | - uses: actions/checkout@v2 24 | - name: Set up Python ${{ matrix.python-version }} 25 | uses: actions/setup-python@v2 26 | with: 27 | python-version: ${{ matrix.python-version }} 28 | - name: Install dependencies 29 | run: | 30 | python -m pip install --upgrade pip 31 | python -m pip install flake8 pytest 32 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 33 | - name: Lint with flake8 34 | run: | 35 | # stop the build if there are Python syntax errors or undefined names 36 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 37 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 38 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 39 | - name: Test with pytest 40 | run: | 41 | pytest --doctest-modules cepy 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled python modules. 2 | *.pyc 3 | 4 | # Setuptools distribution folder. 5 | /dist/ 6 | 7 | # Python egg metadata, regenerated from source files by setuptools. 8 | /*.egg-info 9 | 10 | .idea 11 | 12 | MANIFEST 13 | 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Gidon Levakov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft cepy/data* 2 | exclude cepy/data/generate_example_ce.py 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cepy 2 | 3 | Implementation of the connectome embedding (CE) framework. 4 | 5 | Embedding of brain graph or connectome embedding (CE) involves finding a compact vectorized 6 | representation of nodes that captures their higher-order topological attributes. CE are 7 | obtained using the node2vec algorithm fitted on random walk on a brain graph. The current 8 | framework includes a novel approach to align separately learned embeddings to the same 9 | latent space. Cepy is tested on Python 3.6, 3.7 and 3.8. 10 | 11 | - **Documentation:** https://cepy.readthedocs.io/en/latest/ 12 | 13 | ## Installation 14 | 15 | `pip install cepy` 16 | 17 | ## Usage 18 | ```python 19 | import cepy as ce 20 | import numpy as np 21 | 22 | # Load an adjacency matrix (structural connectivity matrix) 23 | sc_group = ce.get_example('sc_group_matrix') 24 | 25 | # Initiate and fit the connectome embedding model 26 | ce_group = ce.CE(permutations = 1, seed=1) 27 | ce_group.fit(sc_group) 28 | 29 | # Extract the cosine similarity matrix among all pairwise nodes 30 | cosine_sim = ce_group.similarity() 31 | 32 | # Save and load the model 33 | ce_group.save_model('group_ce.json') 34 | ce_loaded = ce.load_model('group_ce.json') # load it 35 | 36 | # Load two existing CE models 37 | ce_subject1 = ce.get_example('ce_subject1') 38 | ce_subject2 = ce.get_example('ce_subject2') 39 | 40 | # Align the two to the space of the [ce_group]: 41 | ce_subject1_aligned = ce.align(ce_group, ce_subject1) 42 | ce_subject2_aligned = ce.align(ce_group, ce_subject2) 43 | 44 | # Extract the node vectorized representations (normalized) for subsequent use - prediction, for example 45 | w_sbject1 = ce_subject1_aligned.weights.get_w_mean(norm = True) 46 | w_sbject2 = ce_subject2_aligned.weights.get_w_mean(norm = True) 47 | 48 | 49 | ``` 50 | 51 | A set of example interactive Jupyter notebooks are also available [here](https://github.com/GidLev/cepy/tree/master/examples). 52 | 53 | ### Citing 54 | If you find *cepy* useful for your research, please consider citing the following paper: 55 | 56 | Levakov, G., Faskowitz, J., Avidan, G., & Sporns, O. (2021). Mapping individual differences across brain network structure 57 | to function and behavior with connectome embedding. Neuroimage, 242, 118469. 58 | 59 | ### Contributing 60 | Cepy is an open-source software project, and we welcome contributions from anyone. 61 | We suggest [raising](https://github.com/GidLev/cepy/issues) an issue prior to 62 | working on a new feature. 63 | 64 | ### Reference 65 | * The node2vec implementation is modeified from the [node2vec](https://github.com/eliorc/node2vec) package by Elior Cohen and the [connectome_embedding](https://github.com/gidonro/Connectome-embeddings) code by Gideon Rosenthal. 66 | * Rosenthal, G., Váša, F., Griffa, A., Hagmann, P., Amico, E., Goñi, J., ... & Sporns, O. (2018). Mapping higher-order relations between brain structure and function with embedded vector representations of connectomes. Nature communications, 9(1), 1-12. 67 | ; 68 | -------------------------------------------------------------------------------- /cepy/__init__.py: -------------------------------------------------------------------------------- 1 | from .ce import CE, load_model, similarity, get_example, get_examples_path 2 | from .embed_align import align 3 | 4 | __version__ = '1.0.0' 5 | -------------------------------------------------------------------------------- /cepy/ce.py: -------------------------------------------------------------------------------- 1 | import random 2 | from collections import defaultdict 3 | import numpy as np 4 | import networkx as nx 5 | from joblib import Parallel, delayed 6 | from tqdm.auto import tqdm 7 | from .parallel import parallel_generate_walks, parallel_learn_embeddings 8 | import tempfile 9 | import shutil 10 | import pickle 11 | import os 12 | import gzip 13 | from cepy.utils import normalize, check_adjacency_matrix 14 | import warnings 15 | import json 16 | import pkg_resources 17 | 18 | # TODO: Option to add nodes names to the CE class 19 | # TODO: implement nearest/n-nearest nodes query 20 | 21 | class CE: 22 | 23 | """ 24 | The main Cepy class for buildings and fitting the connectome embedding model 25 | 26 | Parameters 27 | ---------- 28 | dimensions : int, optional 29 | Number of embedding dimensions. 30 | walk_length : int, optional 31 | Number of nodes in each walk. 32 | num_walks : int, optional 33 | Number of walks initiated from each node. 34 | permutations : int, optional 35 | Number of independent fitting iteration. 36 | p : float, optional 37 | Return hyper parameter (see [1]_). 38 | q : float, optional 39 | In-out parameter (see [1]_). 40 | weight_key : str, optional 41 | On weighted graphs, this is the key for the weight attribute. 42 | workers : int, optional 43 | Number of workers for parallel execution. 44 | sampling_strategy : dict, optional 45 | Node specific sampling strategies, supports setting node specific 'q', 'p', 46 | 'num_walks' and 'walk_length'. Set to None for homogeneous sampling. 47 | verbosity : int, optional 48 | Verbosity level from 2 (high) to 0 (low). 49 | seed : int, optional 50 | Seed for the random number generator. Deterministic results can be obtained if seed is set and workers=1. 51 | window : int, optional 52 | The maximum number of steps between the current and predicted node within a sequence. 53 | min_count : int, optional 54 | Ignores all nodes with total frequency lower than this. 55 | iter : int, optional 56 | Number of iterations (epochs) over all random walk samples. 57 | save_walks: bool, optional 58 | Whether to save the sampled random walks, if True will result in larger memory consumption. 59 | word2vec_kws : dict, optional 60 | Additional parameteres for gensim.models.Word2Vec. Notice that window, min_count, 61 | iter should be entered as separate parameters (would be ignored). 62 | temp_folder : str, optional 63 | Path to folder with enough space to hold the memory map of self.d_graph 64 | (for big graphs); to be passed joblib.Parallel.temp_folder. 65 | pregenerated_walks: list, optional 66 | List of lists of node names, the walks to train the word2vec model 67 | References 68 | ---------- 69 | .. [1] Grover, A., & Leskovec, J. (2016, August). node2vec: Scalable feature learning for networks. 70 | In Proceedings of the 22nd ACM SIGKDD international conference on Knowledge discovery and 71 | data mining (pp. 855-864). 72 | 73 | Examples 74 | -------- 75 | >>> #Learn embeddings for a given connectome: 76 | >>> import numpy as np 77 | >>> import cepy as ce 78 | >>> sc_group = ce.get_example('sc_group_matrix') 79 | >>> ce_group = ce.CE(permutations=1, seed=1) # initiate the connectome embedding model 80 | >>> ce_group.fit(sc_group) # fit the model 81 | Start training 1 word2vec models on 1 threads. 82 | >>> '%.2f' % ce_group.similarity()[0, 1] # Extract the cosine similarity between node 0 and 1 83 | '0.62' 84 | >>> ce_group.save_model('group_ce_copy.json') # save a model: 85 | >>> ce_loaded_copy = ce.load_model('group_ce_copy.json') # load it 86 | >>> # Extract the same cosine similarity again, this should be identical apart from minor numerical difference 87 | >>> '%.2f' % ce_loaded_copy.similarity()[0, 1] 88 | '0.62' 89 | """ 90 | 91 | def __init__(self, dimensions: int = 30, walk_length: int = 20, num_walks: int = 800, 92 | permutations: int = 100, p: float = 1, q: float = 1, 93 | weight_key: str = 'weight', workers: int = 1, sampling_strategy: dict = None, 94 | verbosity: int = 1, temp_folder: str = None, seed: int = None, window: int = 3, 95 | min_count: int = 0, iter: int = 1, save_walks:bool = False, 96 | word2vec_kws: dict = {}, pregenerated_walks: list = None): 97 | 98 | """ 99 | Initiates the connectome embedding object. 100 | """ 101 | self.input_args = dict(locals()) 102 | del self.input_args['self'] 103 | self.FIRST_TRAVEL_KEY = 'first_travel_key' 104 | self.PROBABILITIES_KEY = 'probabilities' 105 | self.NEIGHBORS_KEY = 'neighbors' 106 | self.WEIGHT_KEY = 'weight' 107 | self.NUM_WALKS_KEY = 'num_walks' 108 | self.WALK_LENGTH_KEY = 'walk_length' 109 | self.P_KEY = 'p' 110 | self.Q_KEY = 'q' 111 | 112 | 113 | self.dimensions = dimensions 114 | self.walk_length = walk_length 115 | if num_walks < 800: 116 | warnings.warn('num_walks is recommended to be at least 800, but is ' + str(num_walks) + '.') 117 | self.num_walks = num_walks 118 | self.p = p 119 | self.q = q 120 | self.weight_key = weight_key 121 | self.workers = workers 122 | self.verbosity = verbosity 123 | self.d_graph = defaultdict(dict) 124 | self.word2vec_kws = word2vec_kws 125 | self.permutations = permutations 126 | self.save_walks = save_walks 127 | self.pregenerated_walks = pregenerated_walks 128 | if sampling_strategy is None: 129 | self.sampling_strategy = {} 130 | else: 131 | self.sampling_strategy = sampling_strategy 132 | 133 | self.temp_folder, self.require = None, None 134 | if temp_folder: 135 | if not os.path.isdir(temp_folder): 136 | raise NotADirectoryError("temp_folder does not exist or is not a directory. ({})".format(temp_folder)) 137 | 138 | self.temp_folder = temp_folder 139 | self.require = "sharedmem" 140 | 141 | self.seed = seed 142 | if seed is not None: 143 | random.seed(seed) 144 | np.random.seed(seed) 145 | self.word2vec_kws['seed'] = seed 146 | 147 | if 'workers' not in self.word2vec_kws: 148 | self.word2vec_kws['workers'] = self.workers 149 | 150 | # Check gensim version, after v4.0.0: size->vector_size and iter-> epochs 151 | gensim_version = pkg_resources.get_distribution("gensim").version 152 | if gensim_version >= '4.0.0': 153 | self.word2vec_kws['vector_size'] = self.dimensions 154 | self.word2vec_kws['epochs'] = iter 155 | else: 156 | self.word2vec_kws['size'] = self.dimensions 157 | self.word2vec_kws['iter'] = iter 158 | 159 | if 'compute_loss' not in self.word2vec_kws: 160 | self.word2vec_kws['compute_loss'] = True 161 | 162 | # window, min_count, iter should be entered as separate parameters and not to [word2vec_kws] (would be ignored) 163 | self.word2vec_kws['window'] = window 164 | self.word2vec_kws['min_count'] = min_count 165 | 166 | 167 | def _precompute_probabilities(self): 168 | """ 169 | Precomputes transition probabilities for each node. 170 | """ 171 | 172 | d_graph = self.d_graph 173 | 174 | nodes_generator = self.graph.nodes() if self.verbosity <= 1 \ 175 | else tqdm(self.graph.nodes(), desc='Computing transition probabilities') 176 | 177 | for source in nodes_generator: 178 | 179 | # Init probabilities dict for first travel 180 | if self.PROBABILITIES_KEY not in d_graph[source]: 181 | d_graph[source][self.PROBABILITIES_KEY] = dict() 182 | 183 | for current_node in self.graph.neighbors(source): 184 | 185 | # Init probabilities dict 186 | if self.PROBABILITIES_KEY not in d_graph[current_node]: 187 | d_graph[current_node][self.PROBABILITIES_KEY] = dict() 188 | 189 | unnormalized_weights = list() 190 | d_neighbors = list() 191 | 192 | # Calculate unnormalized weights 193 | for destination in self.graph.neighbors(current_node): 194 | 195 | p = self.sampling_strategy[current_node].get(self.P_KEY, 196 | self.p) if current_node in self.sampling_strategy else self.p 197 | q = self.sampling_strategy[current_node].get(self.Q_KEY, 198 | self.q) if current_node in self.sampling_strategy else self.q 199 | 200 | if destination == source: # Backwards probability 201 | ss_weight = self.graph[current_node][destination].get(self.weight_key, 1) * 1 / p 202 | elif destination in self.graph[source]: # If the neighbor is connected to the source 203 | ss_weight = self.graph[current_node][destination].get(self.weight_key, 1) 204 | else: 205 | ss_weight = self.graph[current_node][destination].get(self.weight_key, 1) * 1 / q 206 | 207 | # Assign the unnormalized sampling strategy weight, normalize during random walk 208 | unnormalized_weights.append(ss_weight) 209 | d_neighbors.append(destination) 210 | 211 | # Normalize 212 | unnormalized_weights = np.array(unnormalized_weights) 213 | d_graph[current_node][self.PROBABILITIES_KEY][ 214 | source] = unnormalized_weights / unnormalized_weights.sum() 215 | 216 | # Calculate first_travel weights for source 217 | first_travel_weights = [] 218 | 219 | for destination in self.graph.neighbors(source): 220 | first_travel_weights.append(self.graph[source][destination].get(self.weight_key, 1)) 221 | 222 | first_travel_weights = np.array(first_travel_weights) 223 | d_graph[source][self.FIRST_TRAVEL_KEY] = first_travel_weights / first_travel_weights.sum() 224 | 225 | # Save neighbors 226 | d_graph[source][self.NEIGHBORS_KEY] = list(self.graph.neighbors(source)) 227 | 228 | def _generate_walks(self) -> list: 229 | """ 230 | Generates the random walks which will be used later for model fitting as List of walks. 231 | Each walk is a list of nodes. 232 | """ 233 | 234 | flatten = lambda l: [item for sublist in l for item in sublist] 235 | 236 | # Split num_walks for each worker 237 | num_walks_lists = np.array_split(range(self.num_walks), self.workers) 238 | 239 | walk_results = Parallel(n_jobs=self.workers, temp_folder=self.temp_folder, require=self.require)( 240 | delayed(parallel_generate_walks)(self.d_graph, 241 | self.walk_length, 242 | len(num_walks), 243 | idx, 244 | self.sampling_strategy, 245 | self.NUM_WALKS_KEY, 246 | self.WALK_LENGTH_KEY, 247 | self.NEIGHBORS_KEY, 248 | self.PROBABILITIES_KEY, 249 | self.FIRST_TRAVEL_KEY, 250 | self.seed, 251 | self.verbosity) for 252 | idx, num_walks 253 | in enumerate(num_walks_lists, 1)) 254 | 255 | self.walks = flatten(walk_results) 256 | 257 | def _learn_embeddings(self): 258 | ''' 259 | fit the node2vec models and retrieve the learned weights 260 | ''' 261 | if self.verbosity > 0: 262 | print('Start training ', self.permutations, ' word2vec models on ', self.word2vec_kws['workers'], 263 | 'threads.') 264 | 265 | if (self.workers < self.permutations) and (self.workers > 1): 266 | # parallel model fittings, each on a single thread 267 | word2vec_kws_parallel = self.word2vec_kws.copy() 268 | word2vec_kws_parallel['workers'] = 1 269 | learned_parameters = Parallel(n_jobs=self.workers, temp_folder=self.temp_folder, require=self.require)( 270 | delayed(parallel_learn_embeddings)(self.temp_walks_path, 271 | word2vec_kws_parallel, 272 | self.nonzero_indices, 273 | self.X.shape[0], 274 | idx, 275 | self.verbosity) for 276 | idx 277 | in np.arange(self.permutations)) 278 | else: 279 | # sequential model fittings, each on a multiple thread 280 | learned_parameters = [] 281 | for idx in np.arange(self.permutations): 282 | learned_parameters.append(parallel_learn_embeddings(self.temp_walks_path, 283 | self.word2vec_kws, 284 | self.nonzero_indices, 285 | self.X.shape[0], 286 | idx, 287 | self.verbosity)) 288 | 289 | # unpack the learned embeddings 290 | self.weights = self.Weights() 291 | self.weights.w = [learned_parameters[x]['w'] for x in np.arange(len(learned_parameters))] 292 | self.weights.w_apos = [learned_parameters[x]['w_apos'] for x in np.arange(len(learned_parameters))] 293 | self.training_losses = [learned_parameters[x]['training_loss'] for x in np.arange(len(learned_parameters))] 294 | 295 | def fit(self, X: np.array): 296 | ''' 297 | Sample random walks and fit a word2vec model. 298 | 299 | Parameters 300 | ---------- 301 | X : ndarray 302 | Input adjacency matrix, shape: (n_nodes, n_nodes) 303 | 304 | Returns 305 | ---------- 306 | 307 | walks: list, optional 308 | List of lists of nodes 309 | ''' 310 | 311 | check_adjacency_matrix(X) 312 | self.X = X 313 | 314 | # deal with zero-connected nodes 315 | self.nonzero_indices = np.where(self.X.sum(axis=0) > 0)[0] 316 | nonzero_adjacency_mat = self.X[self.nonzero_indices, :][:, self.nonzero_indices] 317 | if self.pregenerated_walks: 318 | if len(self.nonzero_indices) < len(self.X): 319 | warnings.warn('Zero connected nodes are found, make sure [pregenerated_walks] are created after removing them') 320 | self.walks = self.pregenerated_walks 321 | else: 322 | self.graph = nx.convert_matrix.from_numpy_matrix(nonzero_adjacency_mat) 323 | self._precompute_probabilities() 324 | self._generate_walks() 325 | 326 | self.temp_walks_path = tempfile.mkdtemp() + '/walks.txt' 327 | with open(self.temp_walks_path, 'w') as walks_file: 328 | walks_file.write('\n'.join(' '.join(map(str, sl)) for sl in self.walks)) 329 | self._learn_embeddings() 330 | 331 | shutil.rmtree(os.path.dirname(self.temp_walks_path)) 332 | 333 | if not self.save_walks: 334 | del self.walks 335 | 336 | 337 | class Weights: 338 | ''' 339 | Stores the trained weight (W and W' matrices) of all fitting permutations. 340 | 341 | Extract the weights with ``get_w_permut(index, norm_flag)`` and ``get_w_mean(norm_flag)`` 342 | or ``get_w_apos_permut(index, norm_flag)`` and ``get_w_apos_mean(norm_flag)``. If norm_flag 343 | is set to True l2 normalization would apply on each vector before extraction. 344 | 345 | ''' 346 | 347 | def __init__(self): 348 | self.w = [] 349 | self.w_apos = [] 350 | 351 | def get_w_permut(self, index=0, norm=True): 352 | if norm: # L2 norm 353 | return self.w[index] / np.linalg.norm(self.w[index], axis=1)[:, np.newaxis] 354 | else: 355 | return self.w[index] 356 | 357 | def get_w_mean(self, norm=True): 358 | all_w = [self.get_w_permut(i, norm=norm) for i in np.arange(len(self.w))] 359 | return np.mean(all_w, axis=0) 360 | 361 | def get_w_apos_permut(self, index=0, norm=True): 362 | if norm: # L2 norm 363 | return self.w_apos[index] / np.linalg.norm(self.w_apos[index], axis=0)[np.newaxis, :] 364 | else: 365 | return self.w_apos[index] 366 | 367 | def get_w_apos_mean(self, norm=True): 368 | all_w_apos = [self.get_w_apos_permut(i, norm=norm) for i in np.arange(len(self.w_apos))] 369 | return np.mean(all_w_apos, axis=0) 370 | 371 | def similarity(self, *args, **kwargs): 372 | return similarity(self, *args, **kwargs) 373 | 374 | def pickle_model(self, path, compress=False): 375 | ''' 376 | Save a model to a pikle object 377 | 378 | Parameters 379 | ---------- 380 | path : str 381 | Path to the file. 382 | compress: bool 383 | Whether to compress the file with gzip 384 | 385 | Examples 386 | -------- 387 | >>> #Load a model and save to file: 388 | >>> import cepy as ce 389 | >>> data_path = ce.get_examples_path() 390 | >>> ce_subject1 = ce.load_model(data_path + '/ce_subject1.json.gz') 391 | >>> ce_subject1.pickle_model('saved_model.pkl') 392 | ''' 393 | if path[-7:] == '.pkl.gz': 394 | compress = True 395 | if compress: 396 | if path[-7:] != '.pkl.gz': 397 | path = path + '.pkl.gz' 398 | with gzip.open(path, 'wb') as f: 399 | pickle.dump(self, f, 3) 400 | else: 401 | if path[-4:] != '.pkl': 402 | path = path + '.pkl' 403 | with open(path, 'wb') as output: 404 | pickle.dump(self, output, 3) 405 | 406 | def save_model(self, path, compress=False): 407 | ''' 408 | Save a model to a pikle object 409 | 410 | Parameters 411 | ---------- 412 | path : str 413 | Path to the file. 414 | compress: bool 415 | Whether to compress the file with gzip 416 | 417 | Examples 418 | -------- 419 | >>> #Load a model and save to file: 420 | >>> import cepy as ce 421 | >>> data_path = ce.get_examples_path() 422 | >>> ce_subject1 = ce.load_model(data_path + '/ce_subject1.json') 423 | >>> ce_subject1.save_model('saved_model.json') 424 | ''' 425 | if not hasattr(self, 'input_args'): 426 | warnings.warn('It seems the model was create with an older version ' 427 | 'of Cepy, please use [pickle_model] instead.') 428 | return None 429 | model_data = {'input_args': self.input_args} 430 | if hasattr(self, 'weights'): 431 | model_data['X'] = self.X.tolist() 432 | model_data['model_weights'] = {'w': [x.tolist() for x in self.weights.w], 433 | 'w_apos': [x.tolist() for x in self.weights.w_apos]} 434 | model_data['training_losses'] = self.training_losses 435 | 436 | if hasattr(self, 'walks'): 437 | model_data['walks'] = self.walks 438 | 439 | if path[-8:] == '.json.gz': 440 | compress = True 441 | if compress: 442 | if path[-8:] != '.json.gz': 443 | path = path + '.json.gz' 444 | with gzip.open(path, 'wt', encoding="UTF-8") as output: 445 | json.dump(model_data, output) 446 | else: 447 | if path[-5:] != '.json': 448 | path = path + '.json' 449 | with open(path, 'w') as output: 450 | json.dump(model_data, output) 451 | 452 | def load_model(path): 453 | ''' 454 | Returns a saved model from a pikle object 455 | 456 | Parameters 457 | ---------- 458 | path : str 459 | Path to the file. 460 | 461 | Returns 462 | ------- 463 | x : CE 464 | 465 | Examples 466 | -------- 467 | >>> # Save then load a model 468 | >>> import cepy as ce 469 | >>> ce_subject1 = ce.get_example('ce_subject1') 470 | >>> sim = ce_subject1.similarity() 471 | >>> '%.2f' % sim[2,5] 472 | '0.16' 473 | >>> ce_subject1.save_model('ce_subject1_copy.json') 474 | >>> ce_subject1_copy = ce.load_model('ce_subject1_copy.json') 475 | >>> sim = ce_subject1_copy.similarity() 476 | >>> '%.2f' % sim[2,5] 477 | '0.16' 478 | ''' 479 | if path[-8:] == '.json.gz': 480 | with gzip.open(path, 'rt', encoding='UTF-8') as f: 481 | loaded_model_dict = json.load(f) 482 | loaded_model = model_from_dict(loaded_model_dict) 483 | elif path[-5:] == '.json': 484 | with open(path, 'r') as f: 485 | loaded_model_dict = json.load(f) 486 | loaded_model = model_from_dict(loaded_model_dict) 487 | elif path[-7:] == '.pkl.gz': 488 | with gzip.open(path, 'rb') as f: 489 | loaded_model = pickle.load(f) 490 | elif path[-4:] == '.pkl': 491 | with open(path, 'rb') as f: 492 | loaded_model = pickle.load(f) 493 | return loaded_model 494 | 495 | def model_from_dict(m_dict): 496 | model = CE(**m_dict['input_args']) 497 | if 'model_weights' in m_dict: 498 | model.weights = model.Weights() 499 | model.weights.w = [np.array(x) for x in m_dict['model_weights']['w']] 500 | model.weights.w_apos = [np.array(x) for x in m_dict['model_weights']['w_apos']] 501 | model.training_losses = m_dict['training_losses'] 502 | model.X = np.array(m_dict['X']) 503 | if 'walks' in m_dict: 504 | model.walks = m_dict['walks'] 505 | return model 506 | 507 | 508 | def similarity(X, Y=None, permut_indices=None, method='cosine_similarity', norm=None): 509 | ''' 510 | Derive several similarity measures among nodes within the same connectome embeding or among differnet embeddings 511 | 512 | Parameters 513 | ---------- 514 | X : CE 515 | The first connectome embedding class on which we perform the similarity measurement 516 | Y : CE, optional 517 | The second connectome embedding class on which we perform the similarity measurement. If None, then Y = X. 518 | permut_indices : tuple or list of tuple, optional 519 | Indices pairs of permutation (idependent fitting iterations) of the first and secocond CEs. Similarity would be taken for X[index1] and Y[index2]. For a list of tuples similarity would be taken for all pairs. If None all possible pairs are tested. 520 | method : str, optional 521 | The similarity measure, one of 'cosine_similarity' | 'hadamard' | 'l1' | 'l2'. 522 | norm : str, optional 523 | Which norm sholud be taken before the smilarity measure, on of 'l1' | 'l2' | 'max'. If None no normalization is applied. This has no effect on cosine similarity. 524 | 525 | Returns 526 | ------- 527 | x : {(num_nodes, num_nodes), (num_nodes, num_nodes, num_embedding_dim)} ndarray or list of ndarray 528 | 529 | Examples 530 | -------- 531 | >>> #Load, align and measure the similarity among two connectome embedding: 532 | >>> import numpy as np 533 | >>> import cepy as ce 534 | >>> ce_subject1 = ce.get_example('ce_subject1') 535 | >>> sim = ce.similarity(ce_subject1, ce_subject1, method='cosine_similarity') 536 | >>> '%.2f' % sim[3,2] 537 | '0.83' 538 | >>> sim = ce_subject1.similarity(ce_subject1, method='cosine_similarity') # equivalent 539 | >>> '%.2f' % sim[3,2] 540 | '0.83' 541 | >>> ce_subject2 = ce.get_example('ce_subject2') 542 | >>> ce_group = ce.get_example('ce_group') 543 | >>> # aligned both subject to the group consensus space 544 | >>> ce_subject1_aligned = ce.align(ce_group, ce_subject1) 545 | >>> ce_subject2_aligned = ce.align(ce_group, ce_subject2) 546 | >>> # and measure the similarity among all corresponding nodes across subjects 547 | >>> sim = ce.similarity(ce_subject1, ce_subject2, method='cosine_similarity') 548 | >>> diagonal_indices = np.diag_indices(sim.shape[0]) 549 | >>> '%.2f' % sim[diagonal_indices].mean() 550 | '0.57' 551 | ''' 552 | if Y == None: 553 | Y = X 554 | 555 | if permut_indices == None: 556 | permut_indices = [(i, i) for i in np.arange(min(len(X.weights.w), len(Y.weights.w)))] 557 | if type(permut_indices) == tuple: 558 | permut_indices = [permut_indices] 559 | 560 | similarity_measures = [] 561 | for permut_index in permut_indices: 562 | x = X.weights.w[permut_index[0]] 563 | y = Y.weights.w[permut_index[1]] 564 | if not np.all(np.isclose(X.weights.w_apos[permut_index[0]], X.weights.w_apos[permut_index[1]])): 565 | warnings.warn('x and y are not aligned.') 566 | node_dim = x.shape[0] 567 | 568 | if method == 'cosine_similarity': 569 | norm = 'l2' 570 | if norm != None: 571 | if x is y: 572 | x = normalize(x, norm) 573 | y = x 574 | else: 575 | x = normalize(x, norm) 576 | y = normalize(y, norm) 577 | 578 | if method == 'cosine_similarity': 579 | similarity_measure = np.dot(x, y.T) 580 | elif method in ['hadamard', 'l1', 'l2']: 581 | array_x = np.transpose(np.tile(x[:, :, np.newaxis], (1, 1, node_dim)), (0, 2, 1)) 582 | array_y = np.transpose(np.tile(y[:, :, np.newaxis], (1, 1, node_dim)), (2, 0, 1)) 583 | if method == 'hadamard': 584 | similarity_measure = array_x * array_y 585 | elif method == 'l1': 586 | similarity_measure = np.abs(array_x - array_y) 587 | elif method == 'l2': 588 | similarity_measure = (array_x - array_y) ** 2 589 | else: 590 | raise Exception('Methods ', method, 'is not supported.') 591 | 592 | similarity_measures.append(similarity_measure) 593 | 594 | if len(similarity_measures) == 1: 595 | return similarity_measures[0] 596 | else: 597 | return np.mean(similarity_measures, axis=0) 598 | 599 | 600 | def get_example(name): 601 | ''' 602 | Returns an existing file example. Can be used for testing/ experimenting. 603 | 604 | Parameters 605 | ---------- 606 | file : str 607 | File name (without the extention). 608 | 609 | Returns 610 | ------- 611 | path : str 612 | path to the file 613 | 614 | Examples 615 | -------- 616 | >>> #Load an existing connectome embedding model: 617 | >>> import cepy as ce 618 | >>> ce_subject1= ce.get_example('ce_subject1') 619 | >>> w = ce_subject1.weights.get_w_mean() 620 | >>> w.shape 621 | (200, 30) 622 | ''' 623 | import pathlib 624 | import cepy as ce 625 | files = ['ce_subject1.json.gz', 'ce_subject2.json.gz', 'ce_group.json.gz', 'sc_subject1_matrix.npz', 626 | 'sc_subject2_matrix.npz', 'sc_group_matrix.npz'] 627 | names = [file[:file.find('.')] for file in files] 628 | file_types = [''.join(pathlib.Path(file).suffixes) for file in files] 629 | names_to_files = dict(zip(names, files)) 630 | names_to_file_types = dict(zip(names, file_types)) 631 | 632 | if not name in names: 633 | raise ValueError(name, 'is not recognized.') 634 | 635 | data_path = os.path.dirname(ce.__file__) + '/data' 636 | path = data_path + '/' + names_to_files[name] 637 | if not os.path.isfile(path): 638 | raise Exception('The file', path, ' is missing.') 639 | 640 | if names_to_file_types[name] == '.npz': 641 | res = np.load(path)['x'] 642 | elif names_to_file_types[name] in ['.json', '.json.gz']: 643 | res = load_model(path) 644 | return res 645 | 646 | 647 | def get_examples_path(): 648 | ''' 649 | Returns the file examples path. 650 | ''' 651 | import cepy as ce 652 | return os.path.dirname(ce.__file__) + '/data' 653 | 654 | 655 | if __name__ == "__main__": 656 | import doctest 657 | 658 | doctest.testmod() 659 | -------------------------------------------------------------------------------- /cepy/data/ce_group.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GidLev/cepy/b97053c89a6f3017ea79b16f74667d747c55db0e/cepy/data/ce_group.json.gz -------------------------------------------------------------------------------- /cepy/data/ce_subject1.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GidLev/cepy/b97053c89a6f3017ea79b16f74667d747c55db0e/cepy/data/ce_subject1.json.gz -------------------------------------------------------------------------------- /cepy/data/ce_subject2.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GidLev/cepy/b97053c89a6f3017ea79b16f74667d747c55db0e/cepy/data/ce_subject2.json.gz -------------------------------------------------------------------------------- /cepy/data/generate_example_ce.py: -------------------------------------------------------------------------------- 1 | ''' 2 | import sys 3 | # # insert at 1, 0 is the script path (or '' in REPL) 4 | # sys.path.insert(1, '/path/to/cepy') 5 | import cepy as ce 6 | parms = {'dimensions': 30, 'walk_length': 20, 'num_walks': 800, 'workers': 1, 7 | 'p': 0.1, 'q': 1.6, 'verbosity': 2, 'window': 3, 'min_count': 0, 8 | 'iter': 1, 'permutations': 5, 'seed': 2} 9 | 10 | sc_subject1 = ce.get_example('sc_subject1_matrix') 11 | ce_subject1 = ce.CE(**parms) 12 | ce_subject1.fit(sc_subject1) 13 | ce_subject1.save_model('ce_subject1.json') 14 | ce_subject1.save_model('ce_subject1.json.gz') 15 | 16 | sc_subject2 = ce.get_example('sc_subject2_matrix') 17 | ce_subject2 = ce.CE(**parms) 18 | ce_subject2.fit(sc_subject2) 19 | ce_subject2.save_model('ce_subject2.json.gz') 20 | 21 | sc_group = ce.get_example('sc_group_matrix') 22 | ce_group = ce.CE(**parms) 23 | ce_group.fit(sc_group) 24 | ce_group.save_model('ce_group.json.gz') 25 | 26 | print('Done generating examples.') 27 | ''' -------------------------------------------------------------------------------- /cepy/data/sc_group_matrix.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GidLev/cepy/b97053c89a6f3017ea79b16f74667d747c55db0e/cepy/data/sc_group_matrix.npz -------------------------------------------------------------------------------- /cepy/data/sc_subject1_matrix.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GidLev/cepy/b97053c89a6f3017ea79b16f74667d747c55db0e/cepy/data/sc_subject1_matrix.npz -------------------------------------------------------------------------------- /cepy/data/sc_subject2_matrix.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GidLev/cepy/b97053c89a6f3017ea79b16f74667d747c55db0e/cepy/data/sc_subject2_matrix.npz -------------------------------------------------------------------------------- /cepy/embed_align.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from collections.abc import Iterable 3 | import copy 4 | 5 | def align(base_ce, target_ce, base_index = 0, target_indices ='all'): 6 | ''' 7 | Aligned connectome embeddings originated from independent fitting iteration 8 | 9 | Parameters 10 | ---------- 11 | base_ce : CE 12 | Containes the latent space for which all target connectome embeddings would be aligned to 13 | target_ce : CE 14 | The connectome embeddings to be aligned 15 | base_index : int, optional 16 | The index of the connectome embedding iteration within base_ce 17 | target_indices : str or list 18 | Index of the connectome embedding within target_ce to be aligned. if set to 'all' then all available fitting iteration are aligned. 19 | 20 | Examples 21 | -------- 22 | >>> #Load, align and measure the similarity among two connectome embedding: 23 | >>> import numpy as np 24 | >>> import cepy as ce 25 | >>> ce_subject1 = ce.get_example('ce_subject1') 26 | >>> ce_subject2 = ce.get_example('ce_subject2') 27 | >>> sim = ce.similarity(ce_subject1, ce_subject2, method='cosine_similarity') 28 | >>> diagonal_indices = np.diag_indices(sim.shape[0]) 29 | >>> '%.2f' % sim[diagonal_indices].mean() # measure the similarity among all corresponding nodes across subjects 30 | '0.57' 31 | >>> # now we repeat the process but first align the two: 32 | >>> ce_group = ce.get_example('ce_group') 33 | >>> ce_subject1_aligned = ce.align(ce_group, ce_subject1) 34 | >>> ce_subject2_aligned = ce.align(ce_group, ce_subject2) 35 | >>> sim = ce.similarity(ce_subject1_aligned,ce_subject2_aligned,method='cosine_similarity') 36 | >>> '%.2f' % sim[diagonal_indices].mean() 37 | '0.79' 38 | ''' 39 | 40 | aligned_target_ce = copy.deepcopy(target_ce) 41 | # compute the inverse of the base's W' 42 | transformation_to_base = np.linalg.pinv(base_ce.weights.w_apos[base_index]).T 43 | 44 | if target_indices == 'all': 45 | target_indices = np.arange(len(target_ce.weights.w)) 46 | elif type(target_indices) == int: 47 | target_indices = [target_indices] 48 | if not isinstance(target_indices, Iterable): 49 | raise TypeError('The "targets_index" argument must be set to "all" or int or list of integers') 50 | 51 | for target_index in target_indices: 52 | aligned_target_ce.weights.w[target_index] = np.dot(transformation_to_base, 53 | np.dot(target_ce.weights.w_apos[target_index].T, 54 | target_ce.weights.w[target_index].T)).T 55 | aligned_target_ce.weights.w_apos[target_index] = base_ce.weights.w_apos[base_index] 56 | return aligned_target_ce 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /cepy/parallel.py: -------------------------------------------------------------------------------- 1 | import random 2 | import numpy as np 3 | from tqdm import tqdm 4 | import gensim 5 | import time 6 | import pkg_resources 7 | 8 | def parallel_generate_walks(d_graph: dict, global_walk_length: int, num_walks: int, cpu_num: int, 9 | sampling_strategy: dict = None, num_walks_key: str = None, walk_length_key: str = None, 10 | neighbors_key: str = None, probabilities_key: str = None, first_travel_key: str = None, 11 | seed: int = None, verbosity: int = 1) -> list: 12 | """ 13 | Generates the random walks which will be used as the skip-gram input. 14 | 15 | :return: List of walks. Each walk is a list of nodes. 16 | """ 17 | np.random.seed(seed) 18 | walks = list() 19 | 20 | if verbosity > 1: 21 | pbar = tqdm(total=num_walks, desc='Generating walks (CPU: {})'.format(cpu_num)) 22 | for n_walk in range(num_walks): 23 | 24 | # Update progress bar 25 | if verbosity > 1: 26 | pbar.update(1) 27 | 28 | # Shuffle the nodes 29 | shuffled_nodes = list(d_graph.keys()) 30 | random.shuffle(shuffled_nodes) 31 | 32 | # Start a random walk from every node 33 | for source in shuffled_nodes: 34 | 35 | # Skip nodes with specific num_walks 36 | if source in sampling_strategy and \ 37 | num_walks_key in sampling_strategy[source] and \ 38 | sampling_strategy[source][num_walks_key] <= n_walk: 39 | continue 40 | 41 | # Start walk 42 | walk = [source] 43 | 44 | # Calculate walk length 45 | if source in sampling_strategy: 46 | walk_length = sampling_strategy[source].get(walk_length_key, global_walk_length) 47 | else: 48 | walk_length = global_walk_length 49 | 50 | # Perform walk 51 | while len(walk) < walk_length: 52 | 53 | walk_options = d_graph[walk[-1]].get(neighbors_key, None) 54 | 55 | # Skip dead end nodes 56 | if not walk_options: 57 | break 58 | 59 | if len(walk) == 1: # For the first step 60 | probabilities = d_graph[walk[-1]][first_travel_key] 61 | walk_to = np.random.choice(walk_options, size=1, p=probabilities)[0] 62 | else: 63 | probabilities = d_graph[walk[-1]][probabilities_key][walk[-2]] 64 | walk_to = np.random.choice(walk_options, size=1, p=probabilities)[0] 65 | 66 | walk.append(walk_to) 67 | 68 | walk = list(map(str, walk)) # Convert all to strings 69 | 70 | walks.append(walk) 71 | 72 | if verbosity > 1: 73 | pbar.close() 74 | 75 | return walks 76 | 77 | def get_hash(astring): 78 | ''' 79 | Returns consistent values to the word2vec model to ensure reproducibility. 80 | 81 | Replace python's inconsistent hashing function (notice this is not a real hashing function but it will work for the current use). 82 | ''' 83 | return int(astring) 84 | 85 | 86 | def parallel_learn_embeddings(walks_file, word2vec_kws, nonzero_indices, num_nodes, cpu_num, verbosity): 87 | """ 88 | Fit the node2vec model on the sampled walks and returns the learned parameters. 89 | 90 | :return: A dictionary with the w and w' parameters and the final training loss. 91 | """ 92 | if verbosity > 1: 93 | s_time = time.time() 94 | 95 | model = gensim.models.Word2Vec(corpus_file=walks_file, hashfxn = get_hash, **word2vec_kws) 96 | 97 | # The word2vec algorithm does not preserve the nodes order, so we should sort it 98 | gensim_version = pkg_resources.get_distribution("gensim").version 99 | if gensim_version > '4.0.0': 100 | nodes_unordered = np.array([int(node) for node in model.wv.index_to_key]) 101 | else: 102 | nodes_unordered = np.array([int(node) for node in model.wv.index2word]) 103 | sorting_indices = np.argsort(nodes_unordered) 104 | 105 | # initiate W and W' 106 | embed_dims = word2vec_kws['vector_size'] if 'vector_size' in word2vec_kws else word2vec_kws['size'] 107 | w = np.empty((num_nodes, embed_dims)) 108 | w_apos = np.empty((embed_dims, num_nodes)) 109 | 110 | # get the trained matrices for the non-zero connected nodes 111 | w[nonzero_indices, :] = model.wv.vectors[sorting_indices, :] 112 | if pkg_resources.get_distribution("gensim").version >= '4.0.0': 113 | w_apos[:, nonzero_indices] = model.syn1neg.T[:, sorting_indices] 114 | else: 115 | w_apos[:, nonzero_indices] = model.trainables.syn1neg.T[:, sorting_indices] 116 | # set random values for the zero connected nodes 117 | if len(nonzero_indices) < num_nodes: 118 | zero_indices = np.ones((num_nodes), dtype = bool) 119 | zero_indices[nonzero_indices] = 0 120 | w[zero_indices, :] = np.random.uniform(low=-0.5, high=0.5, \ 121 | size=(int(zero_indices.sum()), embed_dims)) / embed_dims 122 | w_apos[:, zero_indices] = np.random.uniform(low=-0.5, high=0.5, \ 123 | size=(embed_dims, int(zero_indices.sum()))) / embed_dims 124 | 125 | training_loss = model.get_latest_training_loss() 126 | 127 | if verbosity > 1: 128 | print('Done training the word2vec model ', cpu_num, 'in {:.4f} seconds.'.format(time.time() - s_time)) 129 | 130 | return {'w': w, 'w_apos': w_apos, 'training_loss': training_loss} 131 | -------------------------------------------------------------------------------- /cepy/setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md -------------------------------------------------------------------------------- /cepy/utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import warnings 3 | 4 | def normalize(X, norm='l2', axis=1): 5 | """Scale input vectors individually to unit norm (vector length). 6 | 7 | Parameters 8 | ---------- 9 | X : {array-like, sparse matrix} of shape (n_samples, n_features) 10 | The data to normalize, element by element. 11 | scipy.sparse matrices should be in CSR format to avoid an 12 | un-necessary copy. 13 | norm : {'l1', 'l2', 'max'}, default='l2' 14 | The norm to use to normalize each non zero sample (or each non-zero 15 | feature if axis is 0). 16 | axis : {0, 1}, default=1 17 | axis used to normalize the data along. If 1, independently normalize 18 | each sample, otherwise (if 0) normalize each feature. 19 | 20 | Returns 21 | ------- 22 | X : {ndarray, sparse matrix} of shape (n_samples, n_features) 23 | Normalized input X. 24 | 25 | 26 | """ 27 | if norm not in ('l1', 'l2', 'max'): 28 | raise ValueError("'%s' is not a supported norm" % norm) 29 | 30 | if axis == 0: 31 | X = X.T 32 | 33 | 34 | if norm == 'l1': 35 | norms = np.abs(X).sum(axis=1) 36 | elif norm == 'l2': 37 | norms = row_norms(X) 38 | elif norm == 'max': 39 | norms = np.max(abs(X), axis=1) 40 | norms = _handle_zeros_in_scale(norms, copy=False) 41 | X /= norms[:, np.newaxis] 42 | 43 | if axis == 0: 44 | X = X.T 45 | 46 | return X 47 | 48 | def row_norms(X, squared=False): 49 | """Row-wise (squared) Euclidean norm of X. 50 | Equivalent to np.sqrt((X * X).sum(axis=1)), but also supports sparse 51 | matrices and does not create an X.shape-sized temporary. 52 | Performs no input validation. 53 | Parameters 54 | ---------- 55 | X : array-like 56 | The input array. 57 | squared : bool, default=False 58 | If True, return squared norms. 59 | Returns 60 | ------- 61 | array-like 62 | The row-wise (squared) Euclidean norm of X. 63 | """ 64 | norms = np.einsum('ij,ij->i', X, X) 65 | if not squared: 66 | np.sqrt(norms, norms) 67 | return norms 68 | 69 | def _handle_zeros_in_scale(scale, copy=True): 70 | """Makes sure that whenever scale is zero, we handle it correctly. 71 | This happens in most scalers when we have constant features. 72 | """ 73 | 74 | # if we are fitting on 1D arrays, scale might be a scalar 75 | if np.isscalar(scale): 76 | if scale == .0: 77 | scale = 1. 78 | return scale 79 | elif isinstance(scale, np.ndarray): 80 | if copy: 81 | # New array to avoid side-effects 82 | scale = scale.copy() 83 | scale[scale == 0.0] = 1.0 84 | return scale 85 | 86 | 87 | def check_adjacency_matrix(X): 88 | assert type(X) == np.ndarray, ('Input is expected as a numpy array') 89 | assert np.all(X >= 0), ('No negative edges allowed in the adjacency matrix') 90 | assert np.all(X == X.T), ('The adjacency matrix is expected to be symmetric') 91 | if np.any(X.sum(axis=0) == 0): 92 | warnings.warn('The input adjacency matrix contains zero connected nodes.') -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | sys.path.insert(0, os.path.abspath('../')) 4 | 5 | 6 | # -- Project information ----------------------------------------------------- 7 | 8 | project = 'Cepy' 9 | copyright = '2020, Gidon Levakov' 10 | author = 'Gidon Levakov' 11 | 12 | # The full version, including alpha/beta/rc tags 13 | release = '1.0.0' 14 | 15 | 16 | # -- General configuration --------------------------------------------------- 17 | 18 | # Add any Sphinx extension module names here, as strings. They can be 19 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 20 | # ones. 21 | extensions = ['sphinx.ext.napoleon', 'sphinx.ext.autodoc', 'sphinx.ext.coverage', 'sphinx.ext.doctest'] 22 | 23 | # Add any paths that contain templates here, relative to this directory. 24 | templates_path = ['_templates'] 25 | 26 | # List of patterns, relative to source directory, that match files and 27 | # directories to ignore when looking for source files. 28 | # This pattern also affects html_static_path and html_extra_path. 29 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 30 | 31 | 32 | # -- Options for HTML output ------------------------------------------------- 33 | 34 | # The theme to use for HTML and HTML Help pages. See the documentation for 35 | # a list of builtin themes. 36 | # 37 | html_theme = 'nature' 38 | 39 | # Add any paths that contain custom static files (such as style sheets) here, 40 | # relative to this directory. They are copied after the builtin static files, 41 | # so a file named "default.css" will overwrite the builtin "default.css". 42 | html_static_path = ['_static'] 43 | 44 | 45 | # see https://samnicholls.net/2016/06/15/how-to-sphinx-readthedocs/ 46 | -------------------------------------------------------------------------------- /docs/docs_header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GidLev/cepy/b97053c89a6f3017ea79b16f74667d747c55db0e/docs/docs_header.png -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. image:: docs_header.png 2 | 3 | 4 | Welcome to Cepy's documentation! 5 | ================================ 6 | 7 | The Cepy pacakge is a python implementation of the connectome embedding (CE) framework. 8 | 9 | Embedding of brain graph or connectome embedding (CE) involves finding a compact vectorized 10 | representation of nodes that captures their higher-order topological attributes. CE are 11 | obtained using the node2vec algorithm fitted on random walk on a brain graph. The current 12 | framework includes a novel approach to align separately learned embeddings to the same 13 | latent space. 14 | 15 | Installation 16 | ============ 17 | 18 | Cepy can be installed using pip: 19 | 20 | .. code-block:: shell 21 | 22 | pip install cepy 23 | 24 | Example Notebooks 25 | ================= 26 | If you're looking for hands-on examples on connectome embeddings (CE) and Cepy, check out the links below. These are notebooks that go over the general methodology of the CE framework and how it is used in Cepy. They are available both as static GitHub pages and interactive Binder notebooks. 27 | 28 | We recommend going over them in the following order: 29 | 30 | 31 | * Random walk sampling (see a.i.) - `static `__, |Binder 1| 32 | 33 | .. |Binder 1| image:: https://mybinder.org/badge_logo.svg 34 | :target: https://mybinder.org/v2/gh/GidLev/cepy/master?filepath=examples%2Frandom_walks_generation.ipynb/ 35 | 36 | * Learning (fitting) connectome embedding (CE; see a.ii, a.iii) and mapping structural to functional connectivity (c.i) - `static `__, |Binder 2| 37 | 38 | .. |Binder 2| image:: https://mybinder.org/badge_logo.svg 39 | :target: https://mybinder.org/v2/gh/GidLev/cepy/master?filepath=examples%2Flearn_embedding.ipynb/ 40 | 41 | * Aligning CEs within the same individual (independent fitting of the same subject) - `static `__, |Binder 3| 42 | 43 | .. |Binder 3| image:: https://mybinder.org/badge_logo.svg 44 | :target: https://mybinder.org/v2/gh/GidLev/cepy/master?filepath=examples%2Fintra_embedding_alignment.ipynb/ 45 | 46 | * Aligning CEs between individuals (across subjects; see b) - `static `__, |Binder 4| 47 | 48 | .. |Binder 4| image:: https://mybinder.org/badge_logo.svg 49 | :target: https://mybinder.org/v2/gh/GidLev/cepy/master?filepath=examples%2Finter_embedding_alignment.ipynb/ 50 | 51 | * Learning and aligning CEs of a large cohort (see a,b) - `static `__ 52 | 53 | * Predicting age from aligned CEs (see c.ii)- `static `__, |Binder 5| 54 | 55 | .. |Binder 5| image:: https://mybinder.org/badge_logo.svg 56 | :target: https://mybinder.org/v2/gh/GidLev/cepy/master?filepath=examples%2Fce_prediction.ipynb/ 57 | 58 | 59 | Quick start 60 | ================================ 61 | 62 | .. code-block:: python 63 | :linenos: 64 | 65 | import cepy as ce 66 | import numpy as np 67 | 68 | # Load an adjacency matrix (structural connectivity matrix) 69 | sc_group = ce.get_example('sc_group_matrix') 70 | 71 | # Initiate and fit the connectome embedding model 72 | ce_group = ce.CE(permutations = 1, seed=1) 73 | ce_group.fit(sc_group) 74 | 75 | # Extract the cosine similarity matrix among pairwise nodes 76 | cosine_sim = ce_group.similarity() 77 | 78 | # Save and load the model 79 | ce_group.save_model('group_ce.json') 80 | ce_loaded = ce.load_model('group_ce.json') # load it 81 | 82 | # Load two existing CE models 83 | ce_subject1 = ce.get_example('ce_subject1') 84 | ce_subject2 = ce.get_example('ce_subject2') 85 | 86 | # Align the two to the space of the [ce]: 87 | ce_subject1_aligned = ce.align(ce_group, ce_subject1) 88 | ce_subject2_aligned = ce.align(ce_group, ce_subject2) 89 | 90 | # Extract the node vectorized representations (normalized) for subsequent use (prediction, for example) 91 | w_sbject1 = ce_subject1_aligned.weights.get_w_mean(norm = True) 92 | w_sbject2 = ce_subject2_aligned.weights.get_w_mean(norm = True) 93 | 94 | - see the full `API `_ 95 | 96 | Cite 97 | ================ 98 | 99 | If you find *cepy* useful for your research, please consider citing the following paper: 100 | 101 | Levakov, G., Faskowitz, J., Avidan, G., & Sporns, O. (2021). Mapping individual differences across brain network structure to function and behavior with connectome embedding. Neuroimage, 242, 118469. 102 | 103 | Acknowledgements 104 | ================ 105 | 106 | * The node2vec implementation is modeified from the `node2vec `_ package by Elior Cohen and the `connectome_embedding `_ code by Gideon Rosenthal. 107 | * Rosenthal, G., Váša, F., Griffa, A., Hagmann, P., Amico, E., Goñi, J., ... & Sporns, O. (2018). Mapping higher-order relations between brain structure and function with embedded vector representations of connectomes. Nature communications, 9(1), 1-12. 108 | 109 | 110 | Indices and tables 111 | ================== 112 | 113 | * :ref:`genindex` 114 | * :ref:`modindex` 115 | * :ref:`search` 116 | 117 | -------------------------------------------------------------------------------- /docs/source/cepy.rst: -------------------------------------------------------------------------------- 1 | cepy package 2 | ============ 3 | 4 | Submodules 5 | ---------- 6 | 7 | cepy.ce module 8 | -------------- 9 | 10 | .. automodule:: cepy.ce 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | cepy.embed\_align module 16 | ------------------------ 17 | 18 | .. automodule:: cepy.embed_align 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | cepy.parallel module 24 | -------------------- 25 | 26 | .. automodule:: cepy.parallel 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | cepy.utils module 32 | ----------------- 33 | 34 | .. automodule:: cepy.utils 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | Module contents 40 | --------------- 41 | 42 | .. automodule:: cepy 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | -------------------------------------------------------------------------------- /docs/source/modules.rst: -------------------------------------------------------------------------------- 1 | cepy 2 | ==== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | cepy 8 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Connectome embedding workflow step-by-step 4 | 5 | 6 | As originally proposed by Rosenthal et al. (2018), Cepy utilize the word2vec 7 | algorithm (Mikolov et al., 2013) to create a vectorized representation of 8 | brain nodes based on their high-level topological relations. The word2vec 9 | algorithm is use to create word embeddings that preserve 10 | their context as it typically appears in a sentence. In our work nodes 11 | instead of words are embedded preserving their "context" defined by their 12 | neighbors in a random walks instead of sentences. 13 | 14 | **In the following notebooks we will cover the basics of the CE implementation 15 | and demonstrate its ability for mapping structural connectivity to functional 16 | connectivity and to individual differences.** 17 | 18 | Python Jupyter notebooks are available as static [**GitHub**](https://github.com/) pages or as interactive Binder ( ![Binder](https://mybinder.org/badge_logo.svg) ) notebooks. 19 | 20 | 21 | 22 | The connectome embedding framework 23 | 24 | 25 | 26 | * Random walk sampling (**a.i.**) - [static](https://github.com/GidLev/cepy/blob/master/examples/random_walks_generation.ipynb), 27 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/GidLev/cepy/master?filepath=examples%2Frandom_walks_generation.ipynb) 28 | 29 | * Learning connectome embedding (CE; **a.ii**, **a.iii**) and mapping structural to functional connectivity (**c.i**) - 30 | [static](https://github.com/GidLev/cepy/blob/master/examples/learn_embedding.ipynb), 31 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/GidLev/cepy/master?filepath=examples%2Flearn_embedding.ipynb) 32 | 33 | * Aligning CEs within the same individual (independent fitting of the same subject) - [static](https://github.com/GidLev/cepy/blob/master/examples/intra_embedding_alignment.ipynb), 34 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/GidLev/cepy/master?filepath=examples%2Fintra_embedding_alignment.ipynb) 35 | 36 | * Aligning CEs between individuals (across subjects; **b**) - [static](https://github.com/GidLev/cepy/blob/master/examples/inter_embedding_alignment.ipynb), 37 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/GidLev/cepy/master?filepath=examples%2Finter_embedding_alignment.ipynb) 38 | 39 | * Learning and aligning CEs of a large cohort (**a**,**b**) - [static](https://github.com/GidLev/cepy/blob/master/examples/ce_subjects_pipeline.ipynb) 40 | 41 | * Predicting age from aligned CEs (**c.ii**)- [static](https://github.com/GidLev/cepy/blob/master/examples/ce_prediction.ipynb), 42 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/GidLev/cepy/master?filepath=examples%2Fce_prediction.ipynb) 43 | 44 | 45 | ## Reference 46 | 47 | * Mikolov, T., Sutskever, I., Chen, K., Corrado, G. S., & Dean, J. (2013). Distributed representations of words and phrases and their compositionality. In Advances in neural information processing systems (pp. 3111-3119). 48 | 49 | 50 | * Rosenthal, G., Váša, F., Griffa, A., Hagmann, P., Amico, E., Goñi, J., ... & Sporns, O. (2018). Mapping higher-order relations between brain structure and function with embedded vector representations of connectomes. Nature communications, 9(1), 1-12. -------------------------------------------------------------------------------- /examples/ce_subjects_pipeline.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import sys\n", 10 | "sys.path.insert(1, '/media/galia-lab/Data1/users/gidonl/connectome_embed/cepy')\n", 11 | "import numpy as np\n", 12 | "import cepy as ce\n", 13 | "import os\n", 14 | "import time" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "## Learn embeddings of The Enhanced Nathan Kline Institute Rockland Sample:\n", 22 | "\n", 23 | "\n", 24 | "The purpose of this notebook is to create CEs of a large group of subjects from The\n", 25 | "Enhanced Nathan Kline Institute Rockland Sample (eNKI-RS; Nooner et al., 2012). This is a large open\n", 26 | "lifespan dataset (n=542) that includes diffusion-weighted imaging, resting-state\n", 27 | "fMRI, and demographics. We recommend running this notebook in a multi-thread machine\n", 28 | "to save time.\n", 29 | "\n", 30 | "Let's start with loading the relevant data:" 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": 2, 36 | "metadata": { 37 | "pycharm": { 38 | "name": "#%%\n" 39 | } 40 | }, 41 | "outputs": [ 42 | { 43 | "name": "stdout", 44 | "output_type": "stream", 45 | "text": [ 46 | "(542, 200, 200)\n" 47 | ] 48 | } 49 | ], 50 | "source": [ 51 | "!wget -O NKI_200_schaefer_sc_matrices.npz 'https://github.com/GidLev/cepy/blob/master/examples/NKI_200_schaefer_sc_matrices.npz?raw=true';\n", 52 | "sc_matrices = np.load('NKI_200_schaefer_sc_matrices.npz')['matrices']\n", 53 | "print(sc_matrices.shape)" 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "metadata": {}, 59 | "source": [ 60 | "Notice the shape of the structural connectivity matrices is (*n_subjects*, *n_nodes*, *n_nodes*)\n", 61 | "Now we set the embedding algorithm hyper-parameters:" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": 3, 67 | "metadata": { 68 | "pycharm": { 69 | "name": "#%%\n" 70 | } 71 | }, 72 | "outputs": [], 73 | "source": [ 74 | "# node2vec initialization parameters\n", 75 | "word2vec_params = {'sg': 0, 'min_count': 0}\n", 76 | "parms = {'dimensions': 30, 'walk_length': 20, 'num_walks': 800,\n", 77 | " 'workers': 8, 'p': 0.1, 'q': 1.6, 'seed': 1, 'window': 3,\n", 78 | " 'iter': 1, 'verbosity': 0, 'permutations': 10, \n", 79 | " 'word2vec_kws': word2vec_params}" 80 | ] 81 | }, 82 | { 83 | "cell_type": "markdown", 84 | "metadata": {}, 85 | "source": [ 86 | "Loop over all subjects - initiate, fit and save the nodes embeddings (in a CE object):" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": 4, 92 | "metadata": { 93 | "pycharm": { 94 | "name": "#%%\n" 95 | } 96 | }, 97 | "outputs": [ 98 | { 99 | "name": "stdout", 100 | "output_type": "stream", 101 | "text": [ 102 | "Done with subject 0 .\n", 103 | "Done with subject 1 .\n", 104 | "Done with subject 2 .\n", 105 | "Done with subject 3 .\n", 106 | "Done with subject 4 .\n", 107 | "Done with subject 5 .\n", 108 | "Done with subject 6 .\n", 109 | "Done with subject 7 .\n", 110 | "Done with subject 8 .\n", 111 | "Done with subject 9 .\n", 112 | "Done with subject 10 .\n", 113 | "Done with subject 11 .\n", 114 | "Done with subject 12 .\n", 115 | "Done with subject 13 .\n", 116 | "Done with subject 14 .\n", 117 | "Done with subject 15 .\n", 118 | "Done with subject 16 .\n", 119 | "Done with subject 17 .\n", 120 | "Done with subject 18 .\n", 121 | "Done with subject 19 .\n", 122 | "Done with subject 20 .\n", 123 | "Done with subject 21 .\n", 124 | "Done with subject 22 .\n", 125 | "Done with subject 23 .\n", 126 | "Done with subject 24 .\n", 127 | "Done with subject 25 .\n", 128 | "Done with subject 26 .\n", 129 | "Done with subject 27 .\n", 130 | "Done with subject 28 .\n", 131 | "Done with subject 29 .\n", 132 | "Done with subject 30 .\n", 133 | "Done with subject 31 .\n", 134 | "Done with subject 32 .\n", 135 | "Done with subject 33 .\n", 136 | "Done with subject 34 .\n", 137 | "Done with subject 35 .\n", 138 | "Done with subject 36 .\n", 139 | "Done with subject 37 .\n", 140 | "Done with subject 38 .\n", 141 | "Done with subject 39 .\n", 142 | "Done with subject 40 .\n", 143 | "Done with subject 41 .\n", 144 | "Done with subject 42 .\n", 145 | "Done with subject 43 .\n", 146 | "Done with subject 44 .\n", 147 | "Done with subject 45 .\n", 148 | "Done with subject 46 .\n", 149 | "Done with subject 47 .\n", 150 | "Done with subject 48 .\n", 151 | "Done with subject 49 .\n", 152 | "Done with subject 50 .\n", 153 | "Done with subject 51 .\n", 154 | "Done with subject 52 .\n", 155 | "Done with subject 53 .\n", 156 | "Done with subject 54 .\n", 157 | "Done with subject 55 .\n", 158 | "Done with subject 56 .\n", 159 | "Done with subject 57 .\n", 160 | "Done with subject 58 .\n", 161 | "Done with subject 59 .\n", 162 | "Done with subject 60 .\n", 163 | "Done with subject 61 .\n", 164 | "Done with subject 62 .\n", 165 | "Done with subject 63 .\n", 166 | "Done with subject 64 .\n", 167 | "Done with subject 65 .\n", 168 | "Done with subject 66 .\n", 169 | "Done with subject 67 .\n", 170 | "Done with subject 68 .\n", 171 | "Done with subject 69 .\n", 172 | "Done with subject 70 .\n", 173 | "Done with subject 71 .\n", 174 | "Done with subject 72 .\n", 175 | "Done with subject 73 .\n", 176 | "Done with subject 74 .\n", 177 | "Done with subject 75 .\n", 178 | "Done with subject 76 .\n", 179 | "Done with subject 77 .\n", 180 | "Done with subject 78 .\n", 181 | "Done with subject 79 .\n", 182 | "Done with subject 80 .\n", 183 | "Done with subject 81 .\n", 184 | "Done with subject 82 .\n", 185 | "Done with subject 83 .\n", 186 | "Done with subject 84 .\n", 187 | "Done with subject 85 .\n", 188 | "Done with subject 86 .\n", 189 | "Done with subject 87 .\n", 190 | "Done with subject 88 .\n", 191 | "Done with subject 89 .\n", 192 | "Done with subject 90 .\n", 193 | "Done with subject 91 .\n", 194 | "Done with subject 92 .\n", 195 | "Done with subject 93 .\n", 196 | "Done with subject 94 .\n", 197 | "Done with subject 95 .\n", 198 | "Done with subject 96 .\n", 199 | "Done with subject 97 .\n", 200 | "Done with subject 98 .\n", 201 | "Done with subject 99 .\n", 202 | "Done with subject 100 .\n", 203 | "Done with subject 101 .\n", 204 | "Done with subject 102 .\n", 205 | "Done with subject 103 .\n", 206 | "Done with subject 104 .\n", 207 | "Done with subject 105 .\n", 208 | "Done with subject 106 .\n", 209 | "Done with subject 107 .\n", 210 | "Done with subject 108 .\n", 211 | "Done with subject 109 .\n", 212 | "Done with subject 110 .\n", 213 | "Done with subject 111 .\n", 214 | "Done with subject 112 .\n", 215 | "Done with subject 113 .\n", 216 | "Done with subject 114 .\n", 217 | "Done with subject 115 .\n", 218 | "Done with subject 116 .\n", 219 | "Done with subject 117 .\n", 220 | "Done with subject 118 .\n", 221 | "Done with subject 119 .\n", 222 | "Done with subject 120 .\n", 223 | "Done with subject 121 .\n", 224 | "Done with subject 122 .\n", 225 | "Done with subject 123 .\n", 226 | "Done with subject 124 .\n", 227 | "Done with subject 125 .\n", 228 | "Done with subject 126 .\n", 229 | "Done with subject 127 .\n", 230 | "Done with subject 128 .\n", 231 | "Done with subject 129 .\n", 232 | "Done with subject 130 .\n", 233 | "Done with subject 131 .\n", 234 | "Done with subject 132 .\n", 235 | "Done with subject 133 .\n", 236 | "Done with subject 134 .\n", 237 | "Done with subject 135 .\n", 238 | "Done with subject 136 .\n", 239 | "Done with subject 137 .\n", 240 | "Done with subject 138 .\n", 241 | "Done with subject 139 .\n", 242 | "Done with subject 140 .\n", 243 | "Done with subject 141 .\n", 244 | "Done with subject 142 .\n", 245 | "Done with subject 143 .\n", 246 | "Done with subject 144 .\n", 247 | "Done with subject 145 .\n", 248 | "Done with subject 146 .\n", 249 | "Done with subject 147 .\n", 250 | "Done with subject 148 .\n", 251 | "Done with subject 149 .\n", 252 | "Done with subject 150 .\n", 253 | "Done with subject 151 .\n", 254 | "Done with subject 152 .\n", 255 | "Done with subject 153 .\n", 256 | "Done with subject 154 .\n", 257 | "Done with subject 155 .\n", 258 | "Done with subject 156 .\n", 259 | "Done with subject 157 .\n", 260 | "Done with subject 158 .\n", 261 | "Done with subject 159 .\n", 262 | "Done with subject 160 .\n", 263 | "Done with subject 161 .\n", 264 | "Done with subject 162 .\n", 265 | "Done with subject 163 .\n", 266 | "Done with subject 164 .\n", 267 | "Done with subject 165 .\n", 268 | "Done with subject 166 .\n", 269 | "Done with subject 167 .\n", 270 | "Done with subject 168 .\n", 271 | "Done with subject 169 .\n", 272 | "Done with subject 170 .\n", 273 | "Done with subject 171 .\n", 274 | "Done with subject 172 .\n", 275 | "Done with subject 173 .\n", 276 | "Done with subject 174 .\n", 277 | "Done with subject 175 .\n", 278 | "Done with subject 176 .\n", 279 | "Done with subject 177 .\n", 280 | "Done with subject 178 .\n", 281 | "Done with subject 179 .\n", 282 | "Done with subject 180 .\n", 283 | "Done with subject 181 .\n", 284 | "Done with subject 182 .\n", 285 | "Done with subject 183 .\n", 286 | "Done with subject 184 .\n", 287 | "Done with subject 185 .\n", 288 | "Done with subject 186 .\n", 289 | "Done with subject 187 .\n", 290 | "Done with subject 188 .\n", 291 | "Done with subject 189 .\n", 292 | "Done with subject 190 .\n", 293 | "Done with subject 191 .\n", 294 | "Done with subject 192 .\n", 295 | "Done with subject 193 .\n", 296 | "Done with subject 194 .\n", 297 | "Done with subject 195 .\n", 298 | "Done with subject 196 .\n", 299 | "Done with subject 197 .\n", 300 | "Done with subject 198 .\n", 301 | "Done with subject 199 .\n", 302 | "Done with subject 200 .\n", 303 | "Done with subject 201 .\n", 304 | "Done with subject 202 .\n", 305 | "Done with subject 203 .\n", 306 | "Done with subject 204 .\n", 307 | "Done with subject 205 .\n", 308 | "Done with subject 206 .\n", 309 | "Done with subject 207 .\n", 310 | "Done with subject 208 .\n", 311 | "Done with subject 209 .\n", 312 | "Done with subject 210 .\n", 313 | "Done with subject 211 .\n", 314 | "Done with subject 212 .\n", 315 | "Done with subject 213 .\n", 316 | "Done with subject 214 .\n", 317 | "Done with subject 215 .\n", 318 | "Done with subject 216 .\n", 319 | "Done with subject 217 .\n", 320 | "Done with subject 218 .\n", 321 | "Done with subject 219 .\n", 322 | "Done with subject 220 .\n", 323 | "Done with subject 221 .\n", 324 | "Done with subject 222 .\n", 325 | "Done with subject 223 .\n", 326 | "Done with subject 224 .\n", 327 | "Done with subject 225 .\n", 328 | "Done with subject 226 .\n", 329 | "Done with subject 227 .\n", 330 | "Done with subject 228 .\n", 331 | "Done with subject 229 .\n", 332 | "Done with subject 230 .\n", 333 | "Done with subject 231 .\n", 334 | "Done with subject 232 .\n", 335 | "Done with subject 233 .\n", 336 | "Done with subject 234 .\n", 337 | "Done with subject 235 .\n", 338 | "Done with subject 236 .\n", 339 | "Done with subject 237 .\n", 340 | "Done with subject 238 .\n", 341 | "Done with subject 239 .\n", 342 | "Done with subject 240 .\n", 343 | "Done with subject 241 .\n", 344 | "Done with subject 242 .\n", 345 | "Done with subject 243 .\n", 346 | "Done with subject 244 .\n", 347 | "Done with subject 245 .\n", 348 | "Done with subject 246 .\n", 349 | "Done with subject 247 .\n", 350 | "Done with subject 248 .\n", 351 | "Done with subject 249 .\n", 352 | "Done with subject 250 .\n", 353 | "Done with subject 251 .\n", 354 | "Done with subject 252 .\n", 355 | "Done with subject 253 .\n", 356 | "Done with subject 254 .\n", 357 | "Done with subject 255 .\n", 358 | "Done with subject 256 .\n", 359 | "Done with subject 257 .\n", 360 | "Done with subject 258 .\n", 361 | "Done with subject 259 .\n", 362 | "Done with subject 260 .\n", 363 | "Done with subject 261 .\n", 364 | "Done with subject 262 .\n", 365 | "Done with subject 263 .\n", 366 | "Done with subject 264 .\n", 367 | "Done with subject 265 .\n", 368 | "Done with subject 266 .\n", 369 | "Done with subject 267 .\n", 370 | "Done with subject 268 .\n", 371 | "Done with subject 269 .\n", 372 | "Done with subject 270 .\n", 373 | "Done with subject 271 .\n", 374 | "Done with subject 272 .\n", 375 | "Done with subject 273 .\n", 376 | "Done with subject 274 .\n", 377 | "Done with subject 275 .\n", 378 | "Done with subject 276 .\n", 379 | "Done with subject 277 .\n", 380 | "Done with subject 278 .\n", 381 | "Done with subject 279 .\n", 382 | "Done with subject 280 .\n", 383 | "Done with subject 281 .\n", 384 | "Done with subject 282 .\n", 385 | "Done with subject 283 .\n", 386 | "Done with subject 284 .\n", 387 | "Done with subject 285 .\n", 388 | "Done with subject 286 .\n", 389 | "Done with subject 287 .\n", 390 | "Done with subject 288 .\n", 391 | "Done with subject 289 .\n", 392 | "Done with subject 290 .\n", 393 | "Done with subject 291 .\n", 394 | "Done with subject 292 .\n", 395 | "Done with subject 293 .\n", 396 | "Done with subject 294 .\n", 397 | "Done with subject 295 .\n", 398 | "Done with subject 296 .\n", 399 | "Done with subject 297 .\n", 400 | "Done with subject 298 .\n", 401 | "Done with subject 299 .\n", 402 | "Done with subject 300 .\n", 403 | "Done with subject 301 .\n", 404 | "Done with subject 302 .\n", 405 | "Done with subject 303 .\n", 406 | "Done with subject 304 .\n", 407 | "Done with subject 305 .\n", 408 | "Done with subject 306 .\n", 409 | "Done with subject 307 .\n", 410 | "Done with subject 308 .\n", 411 | "Done with subject 309 .\n", 412 | "Done with subject 310 .\n", 413 | "Done with subject 311 .\n", 414 | "Done with subject 312 .\n", 415 | "Done with subject 313 .\n", 416 | "Done with subject 314 .\n", 417 | "Done with subject 315 .\n", 418 | "Done with subject 316 .\n", 419 | "Done with subject 317 .\n", 420 | "Done with subject 318 .\n", 421 | "Done with subject 319 .\n", 422 | "Done with subject 320 .\n", 423 | "Done with subject 321 .\n", 424 | "Done with subject 322 .\n", 425 | "Done with subject 323 .\n", 426 | "Done with subject 324 .\n", 427 | "Done with subject 325 .\n", 428 | "Done with subject 326 .\n", 429 | "Done with subject 327 .\n", 430 | "Done with subject 328 .\n", 431 | "Done with subject 329 .\n", 432 | "Done with subject 330 .\n", 433 | "Done with subject 331 .\n", 434 | "Done with subject 332 .\n", 435 | "Done with subject 333 .\n", 436 | "Done with subject 334 .\n", 437 | "Done with subject 335 .\n", 438 | "Done with subject 336 .\n", 439 | "Done with subject 337 .\n", 440 | "Done with subject 338 .\n", 441 | "Done with subject 339 .\n", 442 | "Done with subject 340 .\n", 443 | "Done with subject 341 .\n", 444 | "Done with subject 342 .\n", 445 | "Done with subject 343 .\n", 446 | "Done with subject 344 .\n", 447 | "Done with subject 345 .\n" 448 | ] 449 | }, 450 | { 451 | "name": "stdout", 452 | "output_type": "stream", 453 | "text": [ 454 | "Done with subject 346 .\n", 455 | "Done with subject 347 .\n", 456 | "Done with subject 348 .\n", 457 | "Done with subject 349 .\n", 458 | "Done with subject 350 .\n", 459 | "Done with subject 351 .\n", 460 | "Done with subject 352 .\n", 461 | "Done with subject 353 .\n", 462 | "Done with subject 354 .\n", 463 | "Done with subject 355 .\n", 464 | "Done with subject 356 .\n", 465 | "Done with subject 357 .\n", 466 | "Done with subject 358 .\n", 467 | "Done with subject 359 .\n", 468 | "Done with subject 360 .\n", 469 | "Done with subject 361 .\n", 470 | "Done with subject 362 .\n", 471 | "Done with subject 363 .\n", 472 | "Done with subject 364 .\n", 473 | "Done with subject 365 .\n", 474 | "Done with subject 366 .\n", 475 | "Done with subject 367 .\n", 476 | "Done with subject 368 .\n", 477 | "Done with subject 369 .\n", 478 | "Done with subject 370 .\n", 479 | "Done with subject 371 .\n", 480 | "Done with subject 372 .\n", 481 | "Done with subject 373 .\n", 482 | "Done with subject 374 .\n", 483 | "Done with subject 375 .\n", 484 | "Done with subject 376 .\n", 485 | "Done with subject 377 .\n", 486 | "Done with subject 378 .\n", 487 | "Done with subject 379 .\n", 488 | "Done with subject 380 .\n", 489 | "Done with subject 381 .\n", 490 | "Done with subject 382 .\n", 491 | "Done with subject 383 .\n", 492 | "Done with subject 384 .\n", 493 | "Done with subject 385 .\n", 494 | "Done with subject 386 .\n", 495 | "Done with subject 387 .\n", 496 | "Done with subject 388 .\n", 497 | "Done with subject 389 .\n", 498 | "Done with subject 390 .\n", 499 | "Done with subject 391 .\n", 500 | "Done with subject 392 .\n", 501 | "Done with subject 393 .\n", 502 | "Done with subject 394 .\n", 503 | "Done with subject 395 .\n", 504 | "Done with subject 396 .\n", 505 | "Done with subject 397 .\n", 506 | "Done with subject 398 .\n", 507 | "Done with subject 399 .\n", 508 | "Done with subject 400 .\n", 509 | "Done with subject 401 .\n", 510 | "Done with subject 402 .\n", 511 | "Done with subject 403 .\n", 512 | "Done with subject 404 .\n", 513 | "Done with subject 405 .\n", 514 | "Done with subject 406 .\n", 515 | "Done with subject 407 .\n", 516 | "Done with subject 408 .\n", 517 | "Done with subject 409 .\n", 518 | "Done with subject 410 .\n", 519 | "Done with subject 411 .\n", 520 | "Done with subject 412 .\n", 521 | "Done with subject 413 .\n", 522 | "Done with subject 414 .\n", 523 | "Done with subject 415 .\n", 524 | "Done with subject 416 .\n", 525 | "Done with subject 417 .\n", 526 | "Done with subject 418 .\n", 527 | "Done with subject 419 .\n", 528 | "Done with subject 420 .\n", 529 | "Done with subject 421 .\n", 530 | "Done with subject 422 .\n", 531 | "Done with subject 423 .\n", 532 | "Done with subject 424 .\n", 533 | "Done with subject 425 .\n", 534 | "Done with subject 426 .\n", 535 | "Done with subject 427 .\n", 536 | "Done with subject 428 .\n", 537 | "Done with subject 429 .\n", 538 | "Done with subject 430 .\n", 539 | "Done with subject 431 .\n", 540 | "Done with subject 432 .\n", 541 | "Done with subject 433 .\n", 542 | "Done with subject 434 .\n", 543 | "Done with subject 435 .\n", 544 | "Done with subject 436 .\n", 545 | "Done with subject 437 .\n", 546 | "Done with subject 438 .\n", 547 | "Done with subject 439 .\n", 548 | "Done with subject 440 .\n", 549 | "Done with subject 441 .\n", 550 | "Done with subject 442 .\n", 551 | "Done with subject 443 .\n", 552 | "Done with subject 444 .\n", 553 | "Done with subject 445 .\n", 554 | "Done with subject 446 .\n", 555 | "Done with subject 447 .\n", 556 | "Done with subject 448 .\n", 557 | "Done with subject 449 .\n", 558 | "Done with subject 450 .\n", 559 | "Done with subject 451 .\n", 560 | "Done with subject 452 .\n", 561 | "Done with subject 453 .\n", 562 | "Done with subject 454 .\n", 563 | "Done with subject 455 .\n", 564 | "Done with subject 456 .\n", 565 | "Done with subject 457 .\n", 566 | "Done with subject 458 .\n", 567 | "Done with subject 459 .\n", 568 | "Done with subject 460 .\n", 569 | "Done with subject 461 .\n", 570 | "Done with subject 462 .\n", 571 | "Done with subject 463 .\n", 572 | "Done with subject 464 .\n", 573 | "Done with subject 465 .\n", 574 | "Done with subject 466 .\n", 575 | "Done with subject 467 .\n", 576 | "Done with subject 468 .\n", 577 | "Done with subject 469 .\n", 578 | "Done with subject 470 .\n", 579 | "Done with subject 471 .\n", 580 | "Done with subject 472 .\n", 581 | "Done with subject 473 .\n", 582 | "Done with subject 474 .\n", 583 | "Done with subject 475 .\n", 584 | "Done with subject 476 .\n", 585 | "Done with subject 477 .\n", 586 | "Done with subject 478 .\n", 587 | "Done with subject 479 .\n", 588 | "Done with subject 480 .\n", 589 | "Done with subject 481 .\n", 590 | "Done with subject 482 .\n", 591 | "Done with subject 483 .\n", 592 | "Done with subject 484 .\n", 593 | "Done with subject 485 .\n", 594 | "Done with subject 486 .\n", 595 | "Done with subject 487 .\n", 596 | "Done with subject 488 .\n", 597 | "Done with subject 489 .\n", 598 | "Done with subject 490 .\n", 599 | "Done with subject 491 .\n", 600 | "Done with subject 492 .\n", 601 | "Done with subject 493 .\n", 602 | "Done with subject 494 .\n", 603 | "Done with subject 495 .\n", 604 | "Done with subject 496 .\n", 605 | "Done with subject 497 .\n", 606 | "Done with subject 498 .\n", 607 | "Done with subject 499 .\n", 608 | "Done with subject 500 .\n", 609 | "Done with subject 501 .\n", 610 | "Done with subject 502 .\n", 611 | "Done with subject 503 .\n", 612 | "Done with subject 504 .\n", 613 | "Done with subject 505 .\n", 614 | "Done with subject 506 .\n", 615 | "Done with subject 507 .\n", 616 | "Done with subject 508 .\n", 617 | "Done with subject 509 .\n", 618 | "Done with subject 510 .\n", 619 | "Done with subject 511 .\n", 620 | "Done with subject 512 .\n", 621 | "Done with subject 513 .\n", 622 | "Done with subject 514 .\n", 623 | "Done with subject 515 .\n", 624 | "Done with subject 516 .\n", 625 | "Done with subject 517 .\n", 626 | "Done with subject 518 .\n", 627 | "Done with subject 519 .\n", 628 | "Done with subject 520 .\n", 629 | "Done with subject 521 .\n", 630 | "Done with subject 522 .\n", 631 | "Done with subject 523 .\n", 632 | "Done with subject 524 .\n", 633 | "Done with subject 525 .\n", 634 | "Done with subject 526 .\n", 635 | "Done with subject 527 .\n", 636 | "Done with subject 528 .\n", 637 | "Done with subject 529 .\n", 638 | "Done with subject 530 .\n", 639 | "Done with subject 531 .\n", 640 | "Done with subject 532 .\n", 641 | "Done with subject 533 .\n", 642 | "Done with subject 534 .\n", 643 | "Done with subject 535 .\n", 644 | "Done with subject 536 .\n", 645 | "Done with subject 537 .\n", 646 | "Done with subject 538 .\n", 647 | "Done with subject 539 .\n", 648 | "Done with subject 540 .\n", 649 | "Done with subject 541 .\n" 650 | ] 651 | } 652 | ], 653 | "source": [ 654 | "subjects_ce = []\n", 655 | "for subject_i in np.arange(sc_matrices.shape[0]):\n", 656 | " subjects_ce.append(ce.CE(**parms))\n", 657 | " subjects_ce[subject_i].fit(sc_matrices[subject_i,...])\n", 658 | " subjects_ce[subject_i].save_model('save_ces/ce_subject' + str(subject_i) + '.pkl.gz')\n", 659 | " print('Done with subject', subject_i, '.')" 660 | ] 661 | }, 662 | { 663 | "cell_type": "markdown", 664 | "metadata": {}, 665 | "source": [ 666 | "Load a consensus matrix of all training subjects, create its CE and use it as a common reference space for embeddings alignment:" 667 | ] 668 | }, 669 | { 670 | "cell_type": "code", 671 | "execution_count": 5, 672 | "metadata": { 673 | "pycharm": { 674 | "name": "#%%\n" 675 | } 676 | }, 677 | "outputs": [ 678 | { 679 | "name": "stdout", 680 | "output_type": "stream", 681 | "text": [ 682 | "(200, 200)\n" 683 | ] 684 | } 685 | ], 686 | "source": [ 687 | "sc_consensus_matrix = np.load('NKI_200_schaefer_sc_train_consensus_mat.npy')\n", 688 | "print(sc_consensus_matrix.shape)\n", 689 | "\n", 690 | "group_ce = ce.CE(**parms)\n", 691 | "group_ce.fit(sc_consensus_matrix)\n", 692 | "group_ce.save_model('group_ce.pkl.gz')" 693 | ] 694 | }, 695 | { 696 | "cell_type": "markdown", 697 | "metadata": {}, 698 | "source": [ 699 | "Finally, align all CE to the same consensus space and save the output:" 700 | ] 701 | }, 702 | { 703 | "cell_type": "code", 704 | "execution_count": 7, 705 | "metadata": { 706 | "pycharm": { 707 | "name": "#%%\n" 708 | } 709 | }, 710 | "outputs": [], 711 | "source": [ 712 | "for subject_i in np.arange(sc_matrices.shape[0]):\n", 713 | " ce_subject_aligned = ce.align(group_ce, subjects_ce[subject_i])\n", 714 | " w_sbject = ce_subject_aligned.weights.get_w_mean(norm = True)\n", 715 | " if subject_i == 0:\n", 716 | " w_subjects_aligned = np.zeros((sc_matrices.shape[0], w_sbject.shape[0], w_sbject.shape[1]))\n", 717 | " w_subjects_aligned[subject_i,...] = w_sbject\n", 718 | "\n", 719 | "# average over the different iteration and save\n", 720 | "np.savez_compressed('NKI_200_schaefer_subjects_ce.npz', x = w_subjects_aligned)\n" 721 | ] 722 | }, 723 | { 724 | "cell_type": "markdown", 725 | "metadata": {}, 726 | "source": [ 727 | "### reference\n", 728 | "\n", 729 | "* Nooner, K. B., Colcombe, S. J., Tobe, R. H., Mennes, M., Benedict, M. M., Moreno, A. L., …", 730 | " Milham, M. P. (2012). The NKI-Rockland Sample: A Model for Accelerating the Pace of Discovery Science in Psychiatry. Frontiers in Neuroscience, 6, 152. https://doi.org/10.3389/fnins.2012.00152" 731 | ] 732 | } 733 | ], 734 | "metadata": { 735 | "kernelspec": { 736 | "display_name": "PyCharm (code)", 737 | "language": "python", 738 | "name": "pycharm-e15165d1" 739 | }, 740 | "language_info": { 741 | "codemirror_mode": { 742 | "name": "ipython", 743 | "version": 3 744 | }, 745 | "file_extension": ".py", 746 | "mimetype": "text/x-python", 747 | "name": "python", 748 | "nbconvert_exporter": "python", 749 | "pygments_lexer": "ipython3", 750 | "version": "3.7.9" 751 | } 752 | }, 753 | "nbformat": 4, 754 | "nbformat_minor": 1 755 | } -------------------------------------------------------------------------------- /examples/data/NKI_200_schaefer_sc_matrices.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GidLev/cepy/b97053c89a6f3017ea79b16f74667d747c55db0e/examples/data/NKI_200_schaefer_sc_matrices.npz -------------------------------------------------------------------------------- /examples/data/NKI_200_schaefer_subjects_ce.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GidLev/cepy/b97053c89a6f3017ea79b16f74667d747c55db0e/examples/data/NKI_200_schaefer_subjects_ce.npz -------------------------------------------------------------------------------- /examples/data/NKI_demographics.csv: -------------------------------------------------------------------------------- 1 | sub_id,Calculated_Age,demo_sex,test_set 2 | A00040152,76.276712328767,M,False 3 | A00031549,23.446575342466,F,False 4 | A00044131,22.78904109589,M,True 5 | A00051514,21.780821917807998,F,True 6 | A00052069,10.104109589041,M,False 7 | A00059935,15.123287671233001,M,True 8 | A00038522,25.969863013699,M,False 9 | A00062248,30.216438356163998,F,True 10 | A00058318,72.545205479452,F,False 11 | A00035943,26.26301369863,F,False 12 | A00059109,11.317808219178,M,True 13 | A00056295,12.580821917808,M,True 14 | A00038414,60.241095890410996,F,False 15 | A00065991,43.046575342466,F,True 16 | A00053390,15.035616438356,F,True 17 | A00056949,33.076712328767,F,False 18 | A00040174,48.306849315068,F,True 19 | A00039560,48.479452054795,F,False 20 | A00035535,43.53698630137,F,False 21 | A00029127,23.920547945205,M,False 22 | A00055962,36.832876712329,F,True 23 | A00051676,32.087671232877,M,False 24 | A00028389,61.953424657534,M,False 25 | A00039640,68.942465753425,F,True 26 | A00039846,58.221917808219004,F,True 27 | A00037768,41.495890410959,F,False 28 | A00028613,64.928767123288,F,False 29 | A00035378,54.443835616438,F,True 30 | A00039755,22.87397260274,F,False 31 | A00061962,51.290410958904,F,False 32 | A00037545,58.857534246575,M,False 33 | A00057480,10.775342465752999,M,True 34 | A00040525,43.794520547945005,F,False 35 | A00039463,25.098630136986,F,True 36 | A00039685,50.81095890411,M,True 37 | A00028994,54.438356164384004,F,False 38 | A00055542,29.528767123288002,F,True 39 | A00056919,25.150684931507,M,True 40 | A00038957,50.613698630136994,F,False 41 | A00054621,70.56986301369899,F,True 42 | A00028912,67.10684931506799,M,False 43 | A00053873,6.9013698630137,M,False 44 | A00060602,68.331506849315,F,False 45 | A00066302,57.506849315067996,F,False 46 | A00044427,75.44109589041099,F,False 47 | A00029104,44.821917808219006,F,False 48 | A00064328,69.26301369863,F,False 49 | A00039669,61.6,F,False 50 | A00037445,31.975342465753002,F,False 51 | A00054913,83.528767123288,F,False 52 | A00028150,44.824657534246995,F,True 53 | A00040342,13.241095890411,F,False 54 | A00055624,12.027397260274,F,True 55 | A00066581,12.797260273973,M,False 56 | A00065197,18.128767123288,M,False 57 | A00029076,20.92602739726,M,False 58 | A00028192,49.947945205479,M,True 59 | A00040891,57.424657534247,F,True 60 | A00040740,23.353424657534,M,False 61 | A00056489,36.912328767123,M,False 62 | A00050742,53.104109589041,F,False 63 | A00031881,17.043835616438,M,False 64 | A00028399,53.509589041096,M,False 65 | A00043282,16.246575342466,F,False 66 | A00039249,53.024657534247,F,False 67 | A00050848,16.942465753425,M,False 68 | A00073953,69.024657534247,F,False 69 | A00038817,49.58904109589001,F,True 70 | A00038642,28.298630136986,M,True 71 | A00061634,51.27397260274,F,False 72 | A00040500,51.758904109589004,F,True 73 | A00040383,52.904109589041006,F,True 74 | A00037421,44.408219178082,F,True 75 | A00060925,24.665753424657996,F,True 76 | A00052070,67.13698630136999,M,False 77 | A00035798,44.079452054795,M,False 78 | A00037522,64.032876712329,M,False 79 | A00039593,66.641095890411,F,False 80 | A00052183,16.923287671233002,M,True 81 | A00055728,9.8547945205479,F,False 82 | A00060431,72.958904109589,M,False 83 | A00027159,13.117808219178,M,False 84 | A00033011,41.758904109589,F,False 85 | A00043790,49.761643835616,F,False 86 | A00062797,11.468493150685001,F,False 87 | A00037458,16.145205479452,F,False 88 | A00051604,69.59452054794501,M,False 89 | A00055866,9.627397260274,F,False 90 | A00040567,65.950684931507,F,False 91 | A00035562,51.821917808219006,F,False 92 | A00062989,15.413698630137,F,True 93 | A00034400,59.09315068493201,F,False 94 | A00066237,53.906849315067994,F,False 95 | A00051513,40.391780821918005,M,True 96 | A00060005,56.364383561644004,F,True 97 | A00064053,52.39452054794501,F,True 98 | A00038998,26.838356164384,M,False 99 | A00035840,25.405479452055,F,True 100 | A00038616,29.235616438356,M,True 101 | A00060603,11.635616438356,M,True 102 | A00031216,18.46301369863,F,False 103 | A00039783,74.304109589041,M,False 104 | A00051691,8.3698630136986,M,True 105 | A00062934,34.232876712329,M,False 106 | A00056605,14.8,F,False 107 | A00034827,47.602739726027,M,False 108 | A00031167,47.934246575341994,F,True 109 | A00037784,41.569863013699,M,True 110 | A00057862,10.802739726027,F,False 111 | A00055332,15.942465753425,M,True 112 | A00055462,15.101369863014,M,False 113 | A00037459,13.967123287671,M,False 114 | A00040599,61.109589041096,F,True 115 | A00059325,12.843835616438,M,False 116 | A00028185,26.569863013699003,M,True 117 | A00039461,45.4,M,False 118 | A00056470,12.978082191781,F,True 119 | A00040757,49.44931506849301,F,False 120 | A00032007,20.317808219177998,M,True 121 | A00040493,45.027397260274,F,False 122 | A00028753,41.734246575342,F,False 123 | A00065565,84.287671232877,M,False 124 | A00039118,49.142465753425,F,True 125 | A00054534,21.882191780822,F,True 126 | A00055076,10.254794520548,F,True 127 | A00050679,81.534246575342,F,False 128 | A00043649,22.243835616438,M,True 129 | A00037635,59.821917808219006,M,False 130 | A00028606,22.073972602740003,M,False 131 | A00055352,59.545205479452,F,True 132 | A00035504,49.871232876712,F,False 133 | A00039923,73.052054794521,F,False 134 | A00028691,59.720547945205,F,True 135 | A00061483,62.890410958904,F,False 136 | A00033747,26.257534246575,F,False 137 | A00028266,65.723287671233,F,True 138 | A00040494,45.416438356164,F,False 139 | A00065743,51.328767123288,F,False 140 | A00033232,27.939726027396997,M,False 141 | A00053490,12.668493150685,M,True 142 | A00027439,17.386301369863,M,False 143 | A00030980,13.197260273973,F,False 144 | A00058644,71.40273972602701,F,False 145 | A00028605,59.912328767123,M,False 146 | A00040944,35.791780821918,M,False 147 | A00059427,10.758904109589,F,False 148 | A00040351,75.068493150685,F,False 149 | A00066582,14.38904109589,F,False 150 | A00058061,10.821917808218998,F,False 151 | A00033673,66.139726027397,F,False 152 | A00053474,20.646575342466,M,True 153 | A00051658,18.904109589041,F,False 154 | A00061883,14.750684931507001,M,True 155 | A00066130,75.142465753425,F,True 156 | A00040324,70.087671232877,M,False 157 | A00056138,15.47397260274,M,True 158 | A00033180,18.704109589041,F,False 159 | A00052180,46.367123287671006,F,True 160 | A00034854,21.282191780822,F,False 161 | A00035881,24.52602739726,F,False 162 | A00053901,43.621917808219,F,True 163 | A00060252,78.257534246575,F,False 164 | A00054533,21.479452054794997,F,False 165 | A00031452,50.52602739726,M,False 166 | A00037396,31.706849315068002,F,True 167 | A00062416,57.09315068493201,F,False 168 | A00060503,13.345205479452,M,False 169 | A00051456,62.739726027397005,F,True 170 | A00044011,44.397260273973,F,True 171 | A00060099,12.413698630137,M,False 172 | A00039866,12.876712328766999,F,False 173 | A00033643,26.293150684931998,M,True 174 | A00038605,20.605479452055,F,False 175 | A00066013,50.38904109589001,F,True 176 | A00038623,42.172602739726,F,False 177 | A00051528,61.180821917808,M,True 178 | A00058215,6.942465753424702,F,False 179 | A00051517,67.70410958904101,F,False 180 | A00034093,75.13698630136999,M,False 181 | A00027443,15.528767123288,M,True 182 | A00062266,42.608219178081995,F,True 183 | A00065572,54.104109589041,F,False 184 | A00037110,52.60547945205499,F,True 185 | A00053578,21.668493150685002,F,True 186 | A00032875,37.457534246575,F,False 187 | A00054173,20.690410958904,F,False 188 | A00008326,59.33698630136999,F,False 189 | A00060384,12.542465753425,F,True 190 | A00040678,46.534246575341996,F,False 191 | A00028468,46.28493150684901,M,True 192 | A00043509,23.578082191781,M,False 193 | A00054038,68.802739726027,F,False 194 | A00058436,70.857534246575,F,True 195 | A00043635,50.271232876712,M,True 196 | A00040827,18.498630136986,F,False 197 | A00055806,30.778082191781,F,True 198 | A00063383,17.758904109589,F,True 199 | A00032876,60.213698630136996,F,False 200 | A00033021,74.21095890410999,F,True 201 | A00053627,53.893150684932,F,False 202 | A00056452,40.298630136986,F,False 203 | A00059662,57.27397260274,F,False 204 | A00031683,51.397260273973,M,False 205 | A00073283,42.180821917808,F,False 206 | A00065790,23.819178082192,F,True 207 | A00055920,9.9561643835616,F,False 208 | A00053202,43.665753424658,F,True 209 | A00062288,33.38904109589,M,True 210 | A00060430,45.72602739726,F,False 211 | A00039758,66.747945205479,M,False 212 | A00052612,47.528767123288,F,True 213 | A00050720,40.131506849315,F,True 214 | A00062144,15.309589041096,F,True 215 | A00037476,20.857534246575,M,False 216 | A00033609,47.969863013699,M,False 217 | A00031794,63.161643835616,F,False 218 | A00039951,42.854794520548,M,False 219 | A00033963,66.58630136986301,F,False 220 | A00043494,9.7068493150685,M,True 221 | A00050743,15.391780821918001,F,False 222 | A00030989,30.958904109589,F,True 223 | A00051539,28.953424657533997,F,False 224 | A00028184,57.101369863014,M,True 225 | A00035827,36.915068493151004,M,False 226 | A00040462,47.468493150685,F,True 227 | A00050721,46.079452054795,F,True 228 | A00051529,57.873972602740004,F,False 229 | A00031872,59.690410958903996,M,False 230 | A00037377,50.002739726027,F,True 231 | A00055215,22.876712328767,F,False 232 | A00059990,18.276712328766997,M,False 233 | A00044012,19.260273972603,F,False 234 | A00063031,56.150684931506994,F,True 235 | A00039895,50.621917808219,F,False 236 | A00066589,13.830136986301,F,False 237 | A00055738,25.6,F,False 238 | A00031605,39.920547945205,F,False 239 | A00029092,20.635616438356,F,False 240 | A00055991,16.024657534247,F,True 241 | A00035960,21.139726027397,F,True 242 | A00073611,64.73150684931501,F,False 243 | A00040517,61.043835616437995,M,False 244 | A00040311,66.01095890411,F,False 245 | A00028844,19.021917808218998,M,True 246 | A00040784,60.490410958904,F,False 247 | A00035291,14.545205479452,F,False 248 | A00041540,46.945205479452,F,False 249 | A00040182,71.142465753425,F,False 250 | A00030981,15.013698630137,F,False 251 | A00039819,24.383561643836003,M,False 252 | A00029075,71.183561643836,M,False 253 | A00061597,45.17534246575301,F,False 254 | A00040915,21.120547945205,M,True 255 | A00054532,22.969863013699,F,True 256 | A00053577,23.430136986300997,F,False 257 | A00028766,19.865753424658,M,False 258 | A00028995,49.358904109589005,F,False 259 | A00038806,62.665753424658,F,False 260 | A00056703,16.021917808218998,M,False 261 | A00038731,21.906849315068,F,True 262 | A00058053,14.701369863014,F,False 263 | A00035608,24.391780821918,M,False 264 | A00060630,28.293150684931998,F,True 265 | A00051727,15.679452054795,F,False 266 | A00066246,22.909589041096,M,True 267 | A00028380,60.487671232877,F,False 268 | A00031871,33.684931506849,M,False 269 | A00062996,11.791780821918001,F,False 270 | A00064505,15.794520547945002,M,True 271 | A00053369,62.405479452055,F,True 272 | A00029231,23.290410958903998,F,False 273 | A00039393,49.493150684932004,F,False 274 | A00045554,42.728767123288,M,False 275 | A00040286,65.51232876712301,F,False 276 | A00040301,23.265753424657998,F,False 277 | A00038718,21.306849315068,M,False 278 | A00039974,60.219178082192,F,False 279 | A00062292,52.030136986301,F,True 280 | A00043299,34.695890410959,F,False 281 | A00051927,38.567123287670995,F,False 282 | A00040741,66.282191780822,F,False 283 | A00037588,57.764383561644,M,False 284 | A00062285,51.528767123288,F,False 285 | A00044405,49.353424657534,F,True 286 | A00023510,23.342465753425003,M,False 287 | A00060662,26.493150684931997,M,False 288 | A00038115,38.021917808219,F,True 289 | A00063006,11.956164383562,F,True 290 | A00040594,51.46301369863,M,False 291 | A00073677,44.93698630137,F,False 292 | A00053874,47.20273972602701,F,False 293 | A00064596,12.463013698629998,M,False 294 | A00039559,66.791780821918,M,False 295 | A00033248,21.682191780822002,M,True 296 | A00037767,54.652054794521,F,False 297 | A00028352,23.830136986300996,F,False 298 | A00035940,17.569863013699,M,False 299 | A00052118,68.51232876712301,F,True 300 | A00062351,29.73698630137,M,False 301 | A00073705,78.51232876712301,F,False 302 | A00060848,27.676712328767,F,False 303 | A00039700,77.090410958904,F,False 304 | A00028429,20.893150684932,F,False 305 | A00051774,30.852054794521,M,True 306 | A00043520,28.890410958904,M,True 307 | A00060264,52.709589041096,F,False 308 | A00038189,29.449315068493,F,True 309 | A00037831,49.709589041096,F,True 310 | A00056453,17.408219178082,M,False 311 | A00033735,70.471232876712,F,False 312 | A00037111,53.060273972603,F,False 313 | A00039432,59.158904109589,F,False 314 | A00061725,74.879452054795,F,True 315 | A00029304,32.956164383562005,M,False 316 | A00038959,67.246575342466,F,True 317 | A00038411,44.682191780822,F,False 318 | A00051882,53.219178082192,F,False 319 | A00052214,75.227397260274,F,False 320 | A00044084,19.421917808219,F,True 321 | A00040580,60.213698630136996,F,False 322 | A00039143,24.180821917808,F,True 323 | A00034074,17.347945205479,F,True 324 | A00037510,60.964383561644,F,False 325 | A00062268,50.830136986301,F,True 326 | A00039431,33.876712328767,F,False 327 | A00029979,20.153424657534,F,False 328 | A00030990,66.364383561644,F,False 329 | A00062411,27.273972602740002,F,False 330 | A00040151,32.958904109589,F,False 331 | A00028929,63.33698630136999,F,False 332 | A00035879,22.545205479452,M,False 333 | A00058998,71.712328767123,F,False 334 | A00038805,70.065753424658,F,False 335 | A00040116,50.383561643836,F,True 336 | A00060806,16.53698630137,F,True 337 | A00051835,60.213698630136996,F,True 338 | A00039391,37.101369863014,F,False 339 | A00062353,10.161643835616,F,True 340 | A00031893,51.364383561644004,F,False 341 | A00044154,53.030136986301,F,False 342 | A00033849,57.509589041096,F,True 343 | A00054482,71.73698630137,F,False 344 | A00065802,14.394520547945001,M,False 345 | A00035072,37.016438356163995,F,False 346 | A00056097,26.879452054795,M,False 347 | A00044410,71.556164383562,F,False 348 | A00061276,37.093150684932,F,False 349 | A00028842,42.794520547945,M,False 350 | A00054895,59.243835616438,F,True 351 | A00035699,46.660273972602994,F,False 352 | A00040312,66.479452054795,M,False 353 | A00051726,18.608219178082,M,False 354 | A00043739,49.317808219178,F,False 355 | A00031894,41.071232876711996,M,True 356 | A00050977,71.89315068493201,F,False 357 | A00055867,15.783561643836,M,False 358 | A00062923,67.309589041096,F,True 359 | A00013809,61.95616438356201,F,True 360 | A00033640,21.219178082192,M,False 361 | A00051638,18.942465753425,F,False 362 | A00060280,17.032876712329,M,False 363 | A00053320,10.112328767123,M,False 364 | A00053626,78.419178082192,F,False 365 | A00073230,59.153424657533996,F,False 366 | A00043521,44.660273972602994,F,False 367 | A00039639,71.38904109589002,M,False 368 | A00038285,19.18904109589,M,False 369 | A00056307,8.750684931506902,F,True 370 | A00058537,14.564383561644002,M,False 371 | A00067174,67.238356164384,M,False 372 | A00044171,18.153424657534,F,True 373 | A00055908,10.347945205479,M,True 374 | A00034193,27.901369863014,F,False 375 | A00059361,70.46027397260299,M,False 376 | A00040461,52.153424657533996,F,False 377 | A00028287,21.290410958903998,F,True 378 | A00039331,59.643835616438,F,False 379 | A00038831,23.095890410959,M,True 380 | A00051925,49.780821917808,F,True 381 | A00055612,9.9041095890411,F,False 382 | A00040439,58.569863013699,F,False 383 | A00056198,9.019178082191802,F,True 384 | A00060632,33.161643835616005,M,False 385 | A00051477,21.630136986300997,M,False 386 | A00039636,23.912328767122997,M,False 387 | A00061281,66.186301369863,M,False 388 | A00059845,44.534246575341996,M,False 389 | A00029303,19.315068493151,F,False 390 | A00043740,15.01095890411,M,False 391 | A00035941,14.024657534247,F,False 392 | A00051679,27.769863013699002,M,False 393 | A00060923,64.67671232876701,F,False 394 | A00043240,15.416438356164,F,False 395 | A00062929,57.879452054794996,F,True 396 | A00039075,16.202739726027,M,False 397 | A00056746,8.424657534246599,M,True 398 | A00044369,66.221917808219,M,True 399 | A00044307,21.443835616438,M,True 400 | A00038520,62.824657534246995,F,False 401 | A00056628,13.043835616438,M,False 402 | A00035765,40.583561643836,F,False 403 | A00040628,20.556164383562,F,False 404 | A00062369,16.424657534247,F,False 405 | A00038832,19.432876712328998,F,False 406 | A00066282,38.07397260274,F,False 407 | A00052319,47.443835616438,F,False 408 | A00059865,14.901369863013999,F,True 409 | A00051063,18.361643835616,M,True 410 | A00043634,58.301369863014,F,False 411 | A00066319,23.923287671233002,F,False 412 | A00065574,23.720547945205002,M,False 413 | A00066389,69.52328767123299,F,False 414 | A00050795,22.383561643836003,M,False 415 | A00055763,36.512328767123,M,True 416 | A00061715,17.068493150685,M,False 417 | A00062926,46.71232876712301,F,False 418 | A00064416,17.591780821918,F,False 419 | A00054206,14.501369863014,M,False 420 | A00028246,55.860273972603,F,False 421 | A00039607,42.898630136986,F,True 422 | A00060575,66.50410958904101,F,False 423 | A00039655,69.62465753424699,M,False 424 | A00065749,60.934246575341994,M,False 425 | A00065487,43.019178082192,F,False 426 | A00073525,52.86301369863,F,False 427 | A00065974,44.945205479452,F,False 428 | A00028678,69.901369863014,M,False 429 | A00025566,72.465753424658,M,True 430 | A00061001,44.789041095890006,F,True 431 | A00043450,22.487671232877002,F,True 432 | A00061387,23.019178082192006,M,False 433 | A00037267,54.372602739726,F,False 434 | A00035440,62.134246575342,F,True 435 | A00061656,65.139726027397,M,False 436 | A00066800,48.646575342466,F,False 437 | A00031459,20.276712328766997,M,False 438 | A00028177,24.241095890411,F,True 439 | A00035951,44.219178082192,M,False 440 | A00061263,49.515068493151,M,True 441 | A00066864,58.769863013698995,F,True 442 | A00028656,20.772602739726,F,False 443 | A00062809,15.386301369863,F,False 444 | A00043462,21.542465753425002,M,True 445 | A00057405,13.569863013698999,F,True 446 | A00062983,17.498630136986,M,False 447 | A00052126,24.235616438356,M,True 448 | A00063003,14.076712328767,M,False 449 | A00043762,53.68767123287699,F,False 450 | A00044370,56.315068493150996,F,False 451 | A00033734,63.402739726027,F,True 452 | A00061881,19.158904109589,M,False 453 | A00040573,27.87397260274,F,False 454 | A00039159,18.493150684932,F,False 455 | A00051690,15.515068493151,F,False 456 | A00053927,47.569863013699,M,True 457 | A00035036,30.397260273972996,M,True 458 | A00052461,14.328767123288,M,False 459 | A00028694,25.032876712329006,F,False 460 | A00066396,14.739726027397001,M,True 461 | A00066831,73.345205479452,F,True 462 | A00053625,71.852054794521,F,False 463 | A00035437,56.81095890411,F,True 464 | A00060574,57.81095890411,F,True 465 | A00037112,35.257534246575,F,True 466 | A00043722,28.405479452055,M,False 467 | A00031625,30.797260273973,F,False 468 | A00031272,22.167123287671,F,False 469 | A00063479,75.720547945205,F,True 470 | A00028400,49.016438356163995,F,False 471 | A00029126,34.835616438356,M,False 472 | A00040800,16.8,M,False 473 | A00035606,48.583561643836,F,True 474 | A00028340,75.47671232876701,F,True 475 | A00051064,19.487671232877,F,False 476 | A00060185,68.879452054795,F,False 477 | A00043788,13.476712328766999,F,False 478 | A00033231,18.38904109589,M,False 479 | A00073529,54.126027397259996,F,False 480 | A00044345,47.282191780822004,F,False 481 | A00055903,8.427397260274,F,True 482 | A00044130,22.468493150685,F,False 483 | A00037647,24.520547945205003,F,False 484 | A00033832,39.336986301370004,F,False 485 | A00054358,56.060273972603,F,False 486 | A00053576,20.367123287671,F,False 487 | A00053546,18.81095890411,M,False 488 | A00033752,65.178082191781,F,False 489 | A00034350,13.049315068493,M,False 490 | A00033764,48.024657534247,F,False 491 | A00031217,18.8,M,True 492 | A00052577,50.923287671233,F,False 493 | A00037266,24.835616438356,M,False 494 | A00073942,62.194520547945004,F,False 495 | A00043617,47.224657534247,F,False 496 | A00039845,72.55068493150699,F,True 497 | A00039686,21.542465753425002,F,False 498 | A00034987,42.676712328766996,F,True 499 | A00052502,18.950684931507002,M,True 500 | A00055061,53.819178082192,F,False 501 | A00037531,21.602739726027004,F,False 502 | A00057445,56.92602739726,F,False 503 | A00057967,11.854794520548001,M,True 504 | A00044291,24.556164383562002,F,False 505 | A00055267,13.679452054795,M,False 506 | A00043466,20.01095890411,M,True 507 | A00039820,57.380821917808,F,False 508 | A00037378,24.145205479452,M,False 509 | A00052117,42.139726027396996,F,False 510 | A00039353,52.41917808219201,F,False 511 | A00065873,52.915068493151,F,False 512 | A00035625,49.50410958904101,F,False 513 | A00037387,18.608219178082,M,False 514 | A00060472,17.276712328766994,M,True 515 | A00052639,59.654794520548,F,True 516 | A00065722,59.290410958904,M,False 517 | A00051678,66.934246575342,F,True 518 | A00062255,46.739726027397005,M,True 519 | A00063481,53.821917808219006,F,True 520 | A00033247,22.032876712329006,M,False 521 | A00060383,55.189041095890005,M,True 522 | A00058503,43.23013698630101,F,False 523 | A00062146,14.254794520548,F,False 524 | A00033714,16.876712328767,M,True 525 | A00061711,42.950684931507,M,False 526 | A00053744,17.041095890411,M,True 527 | A00062282,38.147945205479004,M,False 528 | A00060100,10.783561643836,M,False 529 | A00044344,63.690410958903996,F,False 530 | A00055353,68.947945205479,F,True 531 | A00037368,12.342465753425,M,True 532 | A00028207,26.931506849315,F,False 533 | A00039657,83.72602739726,F,True 534 | A00039041,60.945205479452,F,True 535 | A00028339,56.468493150685,F,False 536 | A00035292,12.92602739726,M,True 537 | A00056420,9.3506849315068,M,False 538 | A00043758,16.528767123288,F,False 539 | A00028430,57.68493150684901,F,False 540 | A00039488,27.484931506848998,M,False 541 | A00038519,78.898630136986,F,False 542 | A00055122,15.405479452055,M,True 543 | A00032010,76.046575342466,F,False 544 | -------------------------------------------------------------------------------- /examples/data/group_sc_matrix.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GidLev/cepy/b97053c89a6f3017ea79b16f74667d747c55db0e/examples/data/group_sc_matrix.npz -------------------------------------------------------------------------------- /examples/data/schaefer200_yeo17_networks.csv: -------------------------------------------------------------------------------- 1 | ,full_label,hemi,Yeo_17_nets,labels,index,Yeo_7_nets 2 | 0,17Networks_LH_VisCent_ExStr_1,lh,VisCent,ExStr_1,0.0,Visual 3 | 1,17Networks_LH_VisCent_ExStr_2,lh,VisCent,ExStr_2,1.0,Visual 4 | 2,17Networks_LH_VisCent_ExStr_3,lh,VisCent,ExStr_3,2.0,Visual 5 | 3,17Networks_LH_VisCent_ExStr_4,lh,VisCent,ExStr_4,3.0,Visual 6 | 4,17Networks_LH_VisCent_ExStr_5,lh,VisCent,ExStr_5,4.0,Visual 7 | 5,17Networks_LH_VisCent_ExStr_6,lh,VisCent,ExStr_6,5.0,Visual 8 | 6,17Networks_LH_VisPeri_ExStrSup_1,lh,VisPeri,ExStrSup_1,6.0,Visual 9 | 7,17Networks_LH_VisPeri_ExStrSup_2,lh,VisPeri,ExStrSup_2,7.0,Visual 10 | 8,17Networks_LH_VisPeri_ExStrSup_3,lh,VisPeri,ExStrSup_3,8.0,Visual 11 | 9,17Networks_LH_VisPeri_ExStrSup_4,lh,VisPeri,ExStrSup_4,9.0,Visual 12 | 10,17Networks_LH_VisPeri_ExStrSup_5,lh,VisPeri,ExStrSup_5,10.0,Visual 13 | 11,17Networks_LH_VisPeri_ExStrSup_6,lh,VisPeri,ExStrSup_6,11.0,Visual 14 | 12,17Networks_LH_SomMotA_1,lh,SomMotA,SomMotA_1,12.0,Motor 15 | 13,17Networks_LH_SomMotA_2,lh,SomMotA,SomMotA_2,13.0,Motor 16 | 14,17Networks_LH_SomMotA_3,lh,SomMotA,SomMotA_3,14.0,Motor 17 | 15,17Networks_LH_SomMotA_4,lh,SomMotA,SomMotA_4,15.0,Motor 18 | 16,17Networks_LH_SomMotA_5,lh,SomMotA,SomMotA_5,16.0,Motor 19 | 17,17Networks_LH_SomMotA_6,lh,SomMotA,SomMotA_6,17.0,Motor 20 | 18,17Networks_LH_SomMotA_7,lh,SomMotA,SomMotA_7,18.0,Motor 21 | 19,17Networks_LH_SomMotA_8,lh,SomMotA,SomMotA_8,19.0,Motor 22 | 20,17Networks_LH_SomMotB_Aud_1,lh,SomMotB,Aud_1,20.0,Motor 23 | 21,17Networks_LH_SomMotB_Aud_2,lh,SomMotB,Aud_2,21.0,Motor 24 | 22,17Networks_LH_SomMotB_Aud_3,lh,SomMotB,Aud_3,22.0,Motor 25 | 23,17Networks_LH_SomMotB_Aud_4,lh,SomMotB,Aud_4,23.0,Motor 26 | 24,17Networks_LH_SomMotB_Aud_5,lh,SomMotB,Aud_5,24.0,Motor 27 | 25,17Networks_LH_SomMotB_Aud_6,lh,SomMotB,Aud_6,25.0,Motor 28 | 26,17Networks_LH_SomMotB_Aud_7,lh,SomMotB,Aud_7,26.0,Motor 29 | 27,17Networks_LH_SomMotB_Aud_8,lh,SomMotB,Aud_8,27.0,Motor 30 | 28,17Networks_LH_DorsAttnA_TempOcc_1,lh,DorsAttnA,TempOcc_1,28.0,"Dorsal 31 | attention" 32 | 29,17Networks_LH_DorsAttnA_TempOcc_2,lh,DorsAttnA,TempOcc_2,29.0,"Dorsal 33 | attention" 34 | 30,17Networks_LH_DorsAttnA_TempOcc_3,lh,DorsAttnA,TempOcc_3,30.0,"Dorsal 35 | attention" 36 | 31,17Networks_LH_DorsAttnA_SPL_1,lh,DorsAttnA,SPL_1,31.0,"Dorsal 37 | attention" 38 | 32,17Networks_LH_DorsAttnA_SPL_2,lh,DorsAttnA,SPL_2,32.0,"Dorsal 39 | attention" 40 | 33,17Networks_LH_DorsAttnA_SPL_3,lh,DorsAttnA,SPL_3,33.0,"Dorsal 41 | attention" 42 | 34,17Networks_LH_DorsAttnB_PostC_1,lh,DorsAttnB,PostC_1,34.0,"Dorsal 43 | attention" 44 | 35,17Networks_LH_DorsAttnB_PostC_2,lh,DorsAttnB,PostC_2,35.0,"Dorsal 45 | attention" 46 | 36,17Networks_LH_DorsAttnB_PostC_3,lh,DorsAttnB,PostC_3,36.0,"Dorsal 47 | attention" 48 | 37,17Networks_LH_DorsAttnB_PostC_4,lh,DorsAttnB,PostC_4,37.0,"Dorsal 49 | attention" 50 | 38,17Networks_LH_DorsAttnB_FEF_1,lh,DorsAttnB,FEF_1,38.0,"Dorsal 51 | attention" 52 | 39,17Networks_LH_SalVentAttnA_ParOper_1,lh,SalVentAttnA,ParOper_1,39.0,"Ventral 53 | attention" 54 | 40,17Networks_LH_SalVentAttnA_Ins_1,lh,SalVentAttnA,Ins_1,40.0,"Ventral 55 | attention" 56 | 41,17Networks_LH_SalVentAttnA_Ins_2,lh,SalVentAttnA,Ins_2,41.0,"Ventral 57 | attention" 58 | 42,17Networks_LH_SalVentAttnA_Ins_3,lh,SalVentAttnA,Ins_3,42.0,"Ventral 59 | attention" 60 | 43,17Networks_LH_SalVentAttnA_ParMed_1,lh,SalVentAttnA,ParMed_1,43.0,"Ventral 61 | attention" 62 | 44,17Networks_LH_SalVentAttnA_FrMed_1,lh,SalVentAttnA,FrMed_1,44.0,"Ventral 63 | attention" 64 | 45,17Networks_LH_SalVentAttnA_FrMed_2,lh,SalVentAttnA,FrMed_2,45.0,"Ventral 65 | attention" 66 | 46,17Networks_LH_SalVentAttnB_IPL_1,lh,SalVentAttnB,IPL_1,46.0,"Ventral 67 | attention" 68 | 47,17Networks_LH_SalVentAttnB_PFCl_1,lh,SalVentAttnB,PFCl_1,47.0,"Ventral 69 | attention" 70 | 48,17Networks_LH_SalVentAttnB_PFCv_1,lh,SalVentAttnB,PFCv_1,48.0,"Ventral 71 | attention" 72 | 49,17Networks_LH_SalVentAttnB_PFCmp_1,lh,SalVentAttnB,PFCmp_1,49.0,"Ventral 73 | attention" 74 | 50,17Networks_LH_Limbic_OFC_1,lh,Limbic,OFC_1,50.0,Limbic 75 | 51,17Networks_LH_Limbic_OFC_2,lh,Limbic,OFC_2,51.0,Limbic 76 | 52,17Networks_LH_Limbic_TempPole_1,lh,Limbic,TempPole_1,52.0,Limbic 77 | 53,17Networks_LH_Limbic_TempPole_2,lh,Limbic,TempPole_2,53.0,Limbic 78 | 54,17Networks_LH_Limbic_TempPole_3,lh,Limbic,TempPole_3,54.0,Limbic 79 | 55,17Networks_LH_Limbic_TempPole_4,lh,Limbic,TempPole_4,55.0,Limbic 80 | 56,17Networks_LH_ContA_Temp_1,lh,ContA,Temp_1,56.0,Control 81 | 57,17Networks_LH_ContA_IPS_1,lh,ContA,IPS_1,57.0,Control 82 | 58,17Networks_LH_ContA_IPS_2,lh,ContA,IPS_2,58.0,Control 83 | 59,17Networks_LH_ContA_IPS_3,lh,ContA,IPS_3,59.0,Control 84 | 60,17Networks_LH_ContA_PFCd_1,lh,ContA,PFCd_1,60.0,Control 85 | 61,17Networks_LH_ContA_PFCl_1,lh,ContA,PFCl_1,61.0,Control 86 | 62,17Networks_LH_ContA_PFCl_2,lh,ContA,PFCl_2,62.0,Control 87 | 63,17Networks_LH_ContA_PFCl_3,lh,ContA,PFCl_3,63.0,Control 88 | 64,17Networks_LH_ContA_PFCl_4,lh,ContA,PFCl_4,64.0,Control 89 | 65,17Networks_LH_ContA_Cinga_1,lh,ContA,Cinga_1,65.0,Control 90 | 66,17Networks_LH_ContB_Temp_1,lh,ContB,Temp_1,66.0,Control 91 | 67,17Networks_LH_ContB_IPL_1,lh,ContB,IPL_1,67.0,Control 92 | 68,17Networks_LH_ContB_PFCl_1,lh,ContB,PFCl_1,68.0,Control 93 | 69,17Networks_LH_ContB_PFClv_1,lh,ContB,PFClv_1,69.0,Control 94 | 70,17Networks_LH_ContB_PFClv_2,lh,ContB,PFClv_2,70.0,Control 95 | 71,17Networks_LH_ContC_pCun_1,lh,ContC,pCun_1,71.0,Control 96 | 72,17Networks_LH_ContC_pCun_2,lh,ContC,pCun_2,72.0,Control 97 | 73,17Networks_LH_ContC_Cingp_1,lh,ContC,Cingp_1,73.0,Control 98 | 74,17Networks_LH_DefaultA_IPL_1,lh,DefaultA,IPL_1,74.0,Default 99 | 75,17Networks_LH_DefaultA_PFCd_1,lh,DefaultA,PFCd_1,75.0,Default 100 | 76,17Networks_LH_DefaultA_PCC_1,lh,DefaultA,PCC_1,76.0,Default 101 | 77,17Networks_LH_DefaultA_PCC_2,lh,DefaultA,PCC_2,77.0,Default 102 | 78,17Networks_LH_DefaultA_PCC_3,lh,DefaultA,PCC_3,78.0,Default 103 | 79,17Networks_LH_DefaultA_PFCm_1,lh,DefaultA,PFCm_1,79.0,Default 104 | 80,17Networks_LH_DefaultA_PFCm_2,lh,DefaultA,PFCm_2,80.0,Default 105 | 81,17Networks_LH_DefaultA_PFCm_3,lh,DefaultA,PFCm_3,81.0,Default 106 | 82,17Networks_LH_DefaultB_Temp_1,lh,DefaultB,Temp_1,82.0,Default 107 | 83,17Networks_LH_DefaultB_Temp_2,lh,DefaultB,Temp_2,83.0,Default 108 | 84,17Networks_LH_DefaultB_Temp_3,lh,DefaultB,Temp_3,84.0,Default 109 | 85,17Networks_LH_DefaultB_Temp_4,lh,DefaultB,Temp_4,85.0,Default 110 | 86,17Networks_LH_DefaultB_IPL_1,lh,DefaultB,IPL_1,86.0,Default 111 | 87,17Networks_LH_DefaultB_PFCd_1,lh,DefaultB,PFCd_1,87.0,Default 112 | 88,17Networks_LH_DefaultB_PFCd_2,lh,DefaultB,PFCd_2,88.0,Default 113 | 89,17Networks_LH_DefaultB_PFCd_3,lh,DefaultB,PFCd_3,89.0,Default 114 | 90,17Networks_LH_DefaultB_PFCd_4,lh,DefaultB,PFCd_4,90.0,Default 115 | 91,17Networks_LH_DefaultB_PFCv_1,lh,DefaultB,PFCv_1,91.0,Default 116 | 92,17Networks_LH_DefaultB_PFCv_2,lh,DefaultB,PFCv_2,92.0,Default 117 | 93,17Networks_LH_DefaultB_PFCv_3,lh,DefaultB,PFCv_3,93.0,Default 118 | 94,17Networks_LH_DefaultB_PFCv_4,lh,DefaultB,PFCv_4,94.0,Default 119 | 95,17Networks_LH_DefaultC_IPL_1,lh,DefaultC,IPL_1,95.0,Default 120 | 96,17Networks_LH_DefaultC_Rsp_1,lh,DefaultC,Rsp_1,96.0,Default 121 | 97,17Networks_LH_DefaultC_PHC_1,lh,DefaultC,PHC_1,97.0,Default 122 | 98,17Networks_LH_TempPar_1,lh,TempPar,TempPar_1,98.0,Default 123 | 99,17Networks_LH_TempPar_2,lh,TempPar,TempPar_2,99.0,Default 124 | 100,17Networks_RH_VisCent_ExStr_1,rh,VisCent,ExStr_1,100.0,Visual 125 | 101,17Networks_RH_VisCent_ExStr_2,rh,VisCent,ExStr_2,101.0,Visual 126 | 102,17Networks_RH_VisCent_ExStr_3,rh,VisCent,ExStr_3,102.0,Visual 127 | 103,17Networks_RH_VisCent_ExStr_4,rh,VisCent,ExStr_4,103.0,Visual 128 | 104,17Networks_RH_VisCent_ExStr_5,rh,VisCent,ExStr_5,104.0,Visual 129 | 105,17Networks_RH_VisCent_ExStr_6,rh,VisCent,ExStr_6,105.0,Visual 130 | 106,17Networks_RH_VisPeri_ExStrSup_1,rh,VisPeri,ExStrSup_1,106.0,Visual 131 | 107,17Networks_RH_VisPeri_ExStrSup_2,rh,VisPeri,ExStrSup_2,107.0,Visual 132 | 108,17Networks_RH_VisPeri_ExStrSup_3,rh,VisPeri,ExStrSup_3,108.0,Visual 133 | 109,17Networks_RH_VisPeri_ExStrSup_4,rh,VisPeri,ExStrSup_4,109.0,Visual 134 | 110,17Networks_RH_VisPeri_ExStrSup_5,rh,VisPeri,ExStrSup_5,110.0,Visual 135 | 111,17Networks_RH_VisPeri_ExStrSup_6,rh,VisPeri,ExStrSup_6,111.0,Visual 136 | 112,17Networks_RH_SomMotA_1,rh,SomMotA,SomMotA_1,112.0,Motor 137 | 113,17Networks_RH_SomMotA_2,rh,SomMotA,SomMotA_2,113.0,Motor 138 | 114,17Networks_RH_SomMotA_3,rh,SomMotA,SomMotA_3,114.0,Motor 139 | 115,17Networks_RH_SomMotA_4,rh,SomMotA,SomMotA_4,115.0,Motor 140 | 116,17Networks_RH_SomMotA_5,rh,SomMotA,SomMotA_5,116.0,Motor 141 | 117,17Networks_RH_SomMotA_6,rh,SomMotA,SomMotA_6,117.0,Motor 142 | 118,17Networks_RH_SomMotA_7,rh,SomMotA,SomMotA_7,118.0,Motor 143 | 119,17Networks_RH_SomMotA_8,rh,SomMotA,SomMotA_8,119.0,Motor 144 | 120,17Networks_RH_SomMotA_9,rh,SomMotA,SomMotA_9,120.0,Motor 145 | 121,17Networks_RH_SomMotA_10,rh,SomMotA,SomMotA_10,121.0,Motor 146 | 122,17Networks_RH_SomMotA_11,rh,SomMotA,SomMotA_11,122.0,Motor 147 | 123,17Networks_RH_SomMotB_S2_1,rh,SomMotB,S2_1,123.0,Motor 148 | 124,17Networks_RH_SomMotB_S2_2,rh,SomMotB,S2_2,124.0,Motor 149 | 125,17Networks_RH_SomMotB_S2_3,rh,SomMotB,S2_3,125.0,Motor 150 | 126,17Networks_RH_SomMotB_S2_4,rh,SomMotB,S2_4,126.0,Motor 151 | 127,17Networks_RH_SomMotB_S2_5,rh,SomMotB,S2_5,127.0,Motor 152 | 128,17Networks_RH_SomMotB_S2_6,rh,SomMotB,S2_6,128.0,Motor 153 | 129,17Networks_RH_SomMotB_S2_7,rh,SomMotB,S2_7,129.0,Motor 154 | 130,17Networks_RH_DorsAttnA_TempOcc_1,rh,DorsAttnA,TempOcc_1,130.0,"Dorsal 155 | attention" 156 | 131,17Networks_RH_DorsAttnA_TempOcc_2,rh,DorsAttnA,TempOcc_2,131.0,"Dorsal 157 | attention" 158 | 132,17Networks_RH_DorsAttnA_SPL_1,rh,DorsAttnA,SPL_1,132.0,"Dorsal 159 | attention" 160 | 133,17Networks_RH_DorsAttnA_SPL_2,rh,DorsAttnA,SPL_2,133.0,"Dorsal 161 | attention" 162 | 134,17Networks_RH_DorsAttnA_SPL_3,rh,DorsAttnA,SPL_3,134.0,"Dorsal 163 | attention" 164 | 135,17Networks_RH_DorsAttnA_SPL_4,rh,DorsAttnA,SPL_4,135.0,"Dorsal 165 | attention" 166 | 136,17Networks_RH_DorsAttnB_PostC_1,rh,DorsAttnB,PostC_1,136.0,"Dorsal 167 | attention" 168 | 137,17Networks_RH_DorsAttnB_PostC_2,rh,DorsAttnB,PostC_2,137.0,"Dorsal 169 | attention" 170 | 138,17Networks_RH_DorsAttnB_PostC_3,rh,DorsAttnB,PostC_3,138.0,"Dorsal 171 | attention" 172 | 139,17Networks_RH_DorsAttnB_PostC_4,rh,DorsAttnB,PostC_4,139.0,"Dorsal 173 | attention" 174 | 140,17Networks_RH_DorsAttnB_FEF_1,rh,DorsAttnB,FEF_1,140.0,"Dorsal 175 | attention" 176 | 141,17Networks_RH_SalVentAttnA_ParOper_1,rh,SalVentAttnA,ParOper_1,141.0,"Ventral 177 | attention" 178 | 142,17Networks_RH_SalVentAttnA_PrC_1,rh,SalVentAttnA,PrC_1,142.0,"Ventral 179 | attention" 180 | 143,17Networks_RH_SalVentAttnA_Ins_1,rh,SalVentAttnA,Ins_1,143.0,"Ventral 181 | attention" 182 | 144,17Networks_RH_SalVentAttnA_Ins_2,rh,SalVentAttnA,Ins_2,144.0,"Ventral 183 | attention" 184 | 145,17Networks_RH_SalVentAttnA_Ins_3,rh,SalVentAttnA,Ins_3,145.0,"Ventral 185 | attention" 186 | 146,17Networks_RH_SalVentAttnA_FrMed_1,rh,SalVentAttnA,FrMed_1,146.0,"Ventral 187 | attention" 188 | 147,17Networks_RH_SalVentAttnA_FrMed_2,rh,SalVentAttnA,FrMed_2,147.0,"Ventral 189 | attention" 190 | 148,17Networks_RH_SalVentAttnA_FrMed_3,rh,SalVentAttnA,FrMed_3,148.0,"Ventral 191 | attention" 192 | 149,17Networks_RH_SalVentAttnA_FrMed_4,rh,SalVentAttnA,FrMed_4,149.0,"Ventral 193 | attention" 194 | 150,17Networks_RH_SalVentAttnB_IPL_1,rh,SalVentAttnB,IPL_1,150.0,"Ventral 195 | attention" 196 | 151,17Networks_RH_SalVentAttnB_PFCl_1,rh,SalVentAttnB,PFCl_1,151.0,"Ventral 197 | attention" 198 | 152,17Networks_RH_SalVentAttnB_PFCl_2,rh,SalVentAttnB,PFCl_2,152.0,"Ventral 199 | attention" 200 | 153,17Networks_RH_SalVentAttnB_PFCv_1,rh,SalVentAttnB,PFCv_1,153.0,"Ventral 201 | attention" 202 | 154,17Networks_RH_SalVentAttnB_PFCv_2,rh,SalVentAttnB,PFCv_2,154.0,"Ventral 203 | attention" 204 | 155,17Networks_RH_SalVentAttnB_PFCmp_1,rh,SalVentAttnB,PFCmp_1,155.0,"Ventral 205 | attention" 206 | 156,17Networks_RH_Limbic_OFC_1,rh,Limbic,OFC_1,156.0,Limbic 207 | 157,17Networks_RH_Limbic_OFC_2,rh,Limbic,OFC_2,157.0,Limbic 208 | 158,17Networks_RH_Limbic_OFC_3,rh,Limbic,OFC_3,158.0,Limbic 209 | 159,17Networks_RH_Limbic_OFC_4,rh,Limbic,OFC_4,159.0,Limbic 210 | 160,17Networks_RH_Limbic_TempPole_1,rh,Limbic,TempPole_1,160.0,Limbic 211 | 161,17Networks_RH_Limbic_TempPole_2,rh,Limbic,TempPole_2,161.0,Limbic 212 | 162,17Networks_RH_Limbic_TempPole_3,rh,Limbic,TempPole_3,162.0,Limbic 213 | 163,17Networks_RH_Limbic_TempPole_4,rh,Limbic,TempPole_4,163.0,Limbic 214 | 164,17Networks_RH_ContA_IPS_1,rh,ContA,IPS_1,164.0,Control 215 | 165,17Networks_RH_ContA_IPS_2,rh,ContA,IPS_2,165.0,Control 216 | 166,17Networks_RH_ContA_PFCd_1,rh,ContA,PFCd_1,166.0,Control 217 | 167,17Networks_RH_ContA_PFCl_1,rh,ContA,PFCl_1,167.0,Control 218 | 168,17Networks_RH_ContA_PFCl_2,rh,ContA,PFCl_2,168.0,Control 219 | 169,17Networks_RH_ContA_Cinga_1,rh,ContA,Cinga_1,169.0,Control 220 | 170,17Networks_RH_ContB_Temp_1,rh,ContB,Temp_1,170.0,Control 221 | 171,17Networks_RH_ContB_Temp_2,rh,ContB,Temp_2,171.0,Control 222 | 172,17Networks_RH_ContB_IPL_1,rh,ContB,IPL_1,172.0,Control 223 | 173,17Networks_RH_ContB_IPL_2,rh,ContB,IPL_2,173.0,Control 224 | 174,17Networks_RH_ContB_PFCld_1,rh,ContB,PFCld_1,174.0,Control 225 | 175,17Networks_RH_ContB_PFCld_2,rh,ContB,PFCld_2,175.0,Control 226 | 176,17Networks_RH_ContB_PFClv_1,rh,ContB,PFClv_1,176.0,Control 227 | 177,17Networks_RH_ContB_PFClv_2,rh,ContB,PFClv_2,177.0,Control 228 | 178,17Networks_RH_ContB_PFCmp_1,rh,ContB,PFCmp_1,178.0,Control 229 | 179,17Networks_RH_ContB_PFCmp_2,rh,ContB,PFCmp_2,179.0,Control 230 | 180,17Networks_RH_ContC_pCun_1,rh,ContC,pCun_1,180.0,Control 231 | 181,17Networks_RH_ContC_pCun_2,rh,ContC,pCun_2,181.0,Control 232 | 182,17Networks_RH_ContC_Cingp_1,rh,ContC,Cingp_1,182.0,Control 233 | 183,17Networks_RH_DefaultA_IPL_1,rh,DefaultA,IPL_1,183.0,Default 234 | 184,17Networks_RH_DefaultA_PFCd_1,rh,DefaultA,PFCd_1,184.0,Default 235 | 185,17Networks_RH_DefaultA_PCC_1,rh,DefaultA,PCC_1,185.0,Default 236 | 186,17Networks_RH_DefaultA_PFCm_1,rh,DefaultA,PFCm_1,186.0,Default 237 | 187,17Networks_RH_DefaultA_PFCm_2,rh,DefaultA,PFCm_2,187.0,Default 238 | 188,17Networks_RH_DefaultA_PFCm_3,rh,DefaultA,PFCm_3,188.0,Default 239 | 189,17Networks_RH_DefaultB_Temp_1,rh,DefaultB,Temp_1,189.0,Default 240 | 190,17Networks_RH_DefaultB_AntTemp_1,rh,DefaultB,AntTemp_1,190.0,Default 241 | 191,17Networks_RH_DefaultB_PFCd_1,rh,DefaultB,PFCd_1,191.0,Default 242 | 192,17Networks_RH_DefaultB_PFCv_1,rh,DefaultB,PFCv_1,192.0,Default 243 | 193,17Networks_RH_DefaultC_IPL_1,rh,DefaultC,IPL_1,193.0,Default 244 | 194,17Networks_RH_DefaultC_Rsp_1,rh,DefaultC,Rsp_1,194.0,Default 245 | 195,17Networks_RH_DefaultC_PHC_1,rh,DefaultC,PHC_1,195.0,Default 246 | 196,17Networks_RH_TempPar_1,rh,TempPar,TempPar_1,196.0,Default 247 | 197,17Networks_RH_TempPar_2,rh,TempPar,TempPar_2,197.0,Default 248 | 198,17Networks_RH_TempPar_3,rh,TempPar,TempPar_3,198.0,Default 249 | 199,17Networks_RH_TempPar_4,rh,TempPar,TempPar_4,199.0,Default 250 | -------------------------------------------------------------------------------- /examples/data/schaefer200_yeo17_nodeName.csv: -------------------------------------------------------------------------------- 1 | 17Networks_LH_VisCent_ExStr_1 2 | 17Networks_LH_VisCent_ExStr_2 3 | 17Networks_LH_VisCent_ExStr_3 4 | 17Networks_LH_VisCent_ExStr_4 5 | 17Networks_LH_VisCent_ExStr_5 6 | 17Networks_LH_VisCent_ExStr_6 7 | 17Networks_LH_VisPeri_ExStrSup_1 8 | 17Networks_LH_VisPeri_ExStrSup_2 9 | 17Networks_LH_VisPeri_ExStrSup_3 10 | 17Networks_LH_VisPeri_ExStrSup_4 11 | 17Networks_LH_VisPeri_ExStrSup_5 12 | 17Networks_LH_VisPeri_ExStrSup_6 13 | 17Networks_LH_SomMotA_1 14 | 17Networks_LH_SomMotA_2 15 | 17Networks_LH_SomMotA_3 16 | 17Networks_LH_SomMotA_4 17 | 17Networks_LH_SomMotA_5 18 | 17Networks_LH_SomMotA_6 19 | 17Networks_LH_SomMotA_7 20 | 17Networks_LH_SomMotA_8 21 | 17Networks_LH_SomMotB_Aud_1 22 | 17Networks_LH_SomMotB_Aud_2 23 | 17Networks_LH_SomMotB_Aud_3 24 | 17Networks_LH_SomMotB_Aud_4 25 | 17Networks_LH_SomMotB_Aud_5 26 | 17Networks_LH_SomMotB_Aud_6 27 | 17Networks_LH_SomMotB_Aud_7 28 | 17Networks_LH_SomMotB_Aud_8 29 | 17Networks_LH_DorsAttnA_TempOcc_1 30 | 17Networks_LH_DorsAttnA_TempOcc_2 31 | 17Networks_LH_DorsAttnA_TempOcc_3 32 | 17Networks_LH_DorsAttnA_SPL_1 33 | 17Networks_LH_DorsAttnA_SPL_2 34 | 17Networks_LH_DorsAttnA_SPL_3 35 | 17Networks_LH_DorsAttnB_PostC_1 36 | 17Networks_LH_DorsAttnB_PostC_2 37 | 17Networks_LH_DorsAttnB_PostC_3 38 | 17Networks_LH_DorsAttnB_PostC_4 39 | 17Networks_LH_DorsAttnB_FEF_1 40 | 17Networks_LH_SalVentAttnA_ParOper_1 41 | 17Networks_LH_SalVentAttnA_Ins_1 42 | 17Networks_LH_SalVentAttnA_Ins_2 43 | 17Networks_LH_SalVentAttnA_Ins_3 44 | 17Networks_LH_SalVentAttnA_ParMed_1 45 | 17Networks_LH_SalVentAttnA_FrMed_1 46 | 17Networks_LH_SalVentAttnA_FrMed_2 47 | 17Networks_LH_SalVentAttnB_IPL_1 48 | 17Networks_LH_SalVentAttnB_PFCl_1 49 | 17Networks_LH_SalVentAttnB_PFCv_1 50 | 17Networks_LH_SalVentAttnB_PFCmp_1 51 | 17Networks_LH_Limbic_OFC_1 52 | 17Networks_LH_Limbic_OFC_2 53 | 17Networks_LH_Limbic_TempPole_1 54 | 17Networks_LH_Limbic_TempPole_2 55 | 17Networks_LH_Limbic_TempPole_3 56 | 17Networks_LH_Limbic_TempPole_4 57 | 17Networks_LH_ContA_Temp_1 58 | 17Networks_LH_ContA_IPS_1 59 | 17Networks_LH_ContA_IPS_2 60 | 17Networks_LH_ContA_IPS_3 61 | 17Networks_LH_ContA_PFCd_1 62 | 17Networks_LH_ContA_PFCl_1 63 | 17Networks_LH_ContA_PFCl_2 64 | 17Networks_LH_ContA_PFCl_3 65 | 17Networks_LH_ContA_PFCl_4 66 | 17Networks_LH_ContA_Cinga_1 67 | 17Networks_LH_ContB_Temp_1 68 | 17Networks_LH_ContB_IPL_1 69 | 17Networks_LH_ContB_PFCl_1 70 | 17Networks_LH_ContB_PFClv_1 71 | 17Networks_LH_ContB_PFClv_2 72 | 17Networks_LH_ContC_pCun_1 73 | 17Networks_LH_ContC_pCun_2 74 | 17Networks_LH_ContC_Cingp_1 75 | 17Networks_LH_DefaultA_IPL_1 76 | 17Networks_LH_DefaultA_PFCd_1 77 | 17Networks_LH_DefaultA_PCC_1 78 | 17Networks_LH_DefaultA_PCC_2 79 | 17Networks_LH_DefaultA_PCC_3 80 | 17Networks_LH_DefaultA_PFCm_1 81 | 17Networks_LH_DefaultA_PFCm_2 82 | 17Networks_LH_DefaultA_PFCm_3 83 | 17Networks_LH_DefaultB_Temp_1 84 | 17Networks_LH_DefaultB_Temp_2 85 | 17Networks_LH_DefaultB_Temp_3 86 | 17Networks_LH_DefaultB_Temp_4 87 | 17Networks_LH_DefaultB_IPL_1 88 | 17Networks_LH_DefaultB_PFCd_1 89 | 17Networks_LH_DefaultB_PFCd_2 90 | 17Networks_LH_DefaultB_PFCd_3 91 | 17Networks_LH_DefaultB_PFCd_4 92 | 17Networks_LH_DefaultB_PFCv_1 93 | 17Networks_LH_DefaultB_PFCv_2 94 | 17Networks_LH_DefaultB_PFCv_3 95 | 17Networks_LH_DefaultB_PFCv_4 96 | 17Networks_LH_DefaultC_IPL_1 97 | 17Networks_LH_DefaultC_Rsp_1 98 | 17Networks_LH_DefaultC_PHC_1 99 | 17Networks_LH_TempPar_1 100 | 17Networks_LH_TempPar_2 101 | 17Networks_RH_VisCent_ExStr_1 102 | 17Networks_RH_VisCent_ExStr_2 103 | 17Networks_RH_VisCent_ExStr_3 104 | 17Networks_RH_VisCent_ExStr_4 105 | 17Networks_RH_VisCent_ExStr_5 106 | 17Networks_RH_VisCent_ExStr_6 107 | 17Networks_RH_VisPeri_ExStrSup_1 108 | 17Networks_RH_VisPeri_ExStrSup_2 109 | 17Networks_RH_VisPeri_ExStrSup_3 110 | 17Networks_RH_VisPeri_ExStrSup_4 111 | 17Networks_RH_VisPeri_ExStrSup_5 112 | 17Networks_RH_VisPeri_ExStrSup_6 113 | 17Networks_RH_SomMotA_1 114 | 17Networks_RH_SomMotA_2 115 | 17Networks_RH_SomMotA_3 116 | 17Networks_RH_SomMotA_4 117 | 17Networks_RH_SomMotA_5 118 | 17Networks_RH_SomMotA_6 119 | 17Networks_RH_SomMotA_7 120 | 17Networks_RH_SomMotA_8 121 | 17Networks_RH_SomMotA_9 122 | 17Networks_RH_SomMotA_10 123 | 17Networks_RH_SomMotA_11 124 | 17Networks_RH_SomMotB_S2_1 125 | 17Networks_RH_SomMotB_S2_2 126 | 17Networks_RH_SomMotB_S2_3 127 | 17Networks_RH_SomMotB_S2_4 128 | 17Networks_RH_SomMotB_S2_5 129 | 17Networks_RH_SomMotB_S2_6 130 | 17Networks_RH_SomMotB_S2_7 131 | 17Networks_RH_DorsAttnA_TempOcc_1 132 | 17Networks_RH_DorsAttnA_TempOcc_2 133 | 17Networks_RH_DorsAttnA_SPL_1 134 | 17Networks_RH_DorsAttnA_SPL_2 135 | 17Networks_RH_DorsAttnA_SPL_3 136 | 17Networks_RH_DorsAttnA_SPL_4 137 | 17Networks_RH_DorsAttnB_PostC_1 138 | 17Networks_RH_DorsAttnB_PostC_2 139 | 17Networks_RH_DorsAttnB_PostC_3 140 | 17Networks_RH_DorsAttnB_PostC_4 141 | 17Networks_RH_DorsAttnB_FEF_1 142 | 17Networks_RH_SalVentAttnA_ParOper_1 143 | 17Networks_RH_SalVentAttnA_PrC_1 144 | 17Networks_RH_SalVentAttnA_Ins_1 145 | 17Networks_RH_SalVentAttnA_Ins_2 146 | 17Networks_RH_SalVentAttnA_Ins_3 147 | 17Networks_RH_SalVentAttnA_FrMed_1 148 | 17Networks_RH_SalVentAttnA_FrMed_2 149 | 17Networks_RH_SalVentAttnA_FrMed_3 150 | 17Networks_RH_SalVentAttnA_FrMed_4 151 | 17Networks_RH_SalVentAttnB_IPL_1 152 | 17Networks_RH_SalVentAttnB_PFCl_1 153 | 17Networks_RH_SalVentAttnB_PFCl_2 154 | 17Networks_RH_SalVentAttnB_PFCv_1 155 | 17Networks_RH_SalVentAttnB_PFCv_2 156 | 17Networks_RH_SalVentAttnB_PFCmp_1 157 | 17Networks_RH_Limbic_OFC_1 158 | 17Networks_RH_Limbic_OFC_2 159 | 17Networks_RH_Limbic_OFC_3 160 | 17Networks_RH_Limbic_OFC_4 161 | 17Networks_RH_Limbic_TempPole_1 162 | 17Networks_RH_Limbic_TempPole_2 163 | 17Networks_RH_Limbic_TempPole_3 164 | 17Networks_RH_Limbic_TempPole_4 165 | 17Networks_RH_ContA_IPS_1 166 | 17Networks_RH_ContA_IPS_2 167 | 17Networks_RH_ContA_PFCd_1 168 | 17Networks_RH_ContA_PFCl_1 169 | 17Networks_RH_ContA_PFCl_2 170 | 17Networks_RH_ContA_Cinga_1 171 | 17Networks_RH_ContB_Temp_1 172 | 17Networks_RH_ContB_Temp_2 173 | 17Networks_RH_ContB_IPL_1 174 | 17Networks_RH_ContB_IPL_2 175 | 17Networks_RH_ContB_PFCld_1 176 | 17Networks_RH_ContB_PFCld_2 177 | 17Networks_RH_ContB_PFClv_1 178 | 17Networks_RH_ContB_PFClv_2 179 | 17Networks_RH_ContB_PFCmp_1 180 | 17Networks_RH_ContB_PFCmp_2 181 | 17Networks_RH_ContC_pCun_1 182 | 17Networks_RH_ContC_pCun_2 183 | 17Networks_RH_ContC_Cingp_1 184 | 17Networks_RH_DefaultA_IPL_1 185 | 17Networks_RH_DefaultA_PFCd_1 186 | 17Networks_RH_DefaultA_PCC_1 187 | 17Networks_RH_DefaultA_PFCm_1 188 | 17Networks_RH_DefaultA_PFCm_2 189 | 17Networks_RH_DefaultA_PFCm_3 190 | 17Networks_RH_DefaultB_Temp_1 191 | 17Networks_RH_DefaultB_AntTemp_1 192 | 17Networks_RH_DefaultB_PFCd_1 193 | 17Networks_RH_DefaultB_PFCv_1 194 | 17Networks_RH_DefaultC_IPL_1 195 | 17Networks_RH_DefaultC_Rsp_1 196 | 17Networks_RH_DefaultC_PHC_1 197 | 17Networks_RH_TempPar_1 198 | 17Networks_RH_TempPar_2 199 | 17Networks_RH_TempPar_3 200 | 17Networks_RH_TempPar_4 201 | -------------------------------------------------------------------------------- /examples/data/sub1_fc_matrix.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GidLev/cepy/b97053c89a6f3017ea79b16f74667d747c55db0e/examples/data/sub1_fc_matrix.npz -------------------------------------------------------------------------------- /examples/data/sub1_sc_matrix.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GidLev/cepy/b97053c89a6f3017ea79b16f74667d747c55db0e/examples/data/sub1_sc_matrix.npz -------------------------------------------------------------------------------- /examples/data/sub2_fc_matrix.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GidLev/cepy/b97053c89a6f3017ea79b16f74667d747c55db0e/examples/data/sub2_fc_matrix.npz -------------------------------------------------------------------------------- /examples/data/sub2_sc_matrix.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GidLev/cepy/b97053c89a6f3017ea79b16f74667d747c55db0e/examples/data/sub2_sc_matrix.npz -------------------------------------------------------------------------------- /examples/images/ce_alignment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GidLev/cepy/b97053c89a6f3017ea79b16f74667d747c55db0e/examples/images/ce_alignment.png -------------------------------------------------------------------------------- /examples/images/ce_individuals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GidLev/cepy/b97053c89a6f3017ea79b16f74667d747c55db0e/examples/images/ce_individuals.png -------------------------------------------------------------------------------- /examples/images/ce_workflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GidLev/cepy/b97053c89a6f3017ea79b16f74667d747c55db0e/examples/images/ce_workflow.png -------------------------------------------------------------------------------- /examples/images/ce_workflow_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GidLev/cepy/b97053c89a6f3017ea79b16f74667d747c55db0e/examples/images/ce_workflow_full.png -------------------------------------------------------------------------------- /examples/images/ce_workflow_width.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GidLev/cepy/b97053c89a6f3017ea79b16f74667d747c55db0e/examples/images/ce_workflow_width.png -------------------------------------------------------------------------------- /examples/images/p_q_parames.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GidLev/cepy/b97053c89a6f3017ea79b16f74667d747c55db0e/examples/images/p_q_parames.png -------------------------------------------------------------------------------- /examples/images/random_walks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GidLev/cepy/b97053c89a6f3017ea79b16f74667d747c55db0e/examples/images/random_walks.png -------------------------------------------------------------------------------- /examples/images/structure_to_function.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GidLev/cepy/b97053c89a6f3017ea79b16f74667d747c55db0e/examples/images/structure_to_function.png -------------------------------------------------------------------------------- /examples/random_walks_generation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "name": "random_walks_generation_v2", 7 | "provenance": [], 8 | "collapsed_sections": [] 9 | }, 10 | "kernelspec": { 11 | "name": "python3", 12 | "display_name": "Python 3" 13 | } 14 | }, 15 | "cells": [ 16 | { 17 | "cell_type": "markdown", 18 | "metadata": { 19 | "id": "PUGZQdahEZsf" 20 | }, 21 | "source": [ 22 | "## Random walks sampling\n", 23 | "\n", 24 | "The node2vec algorithm is fitted on pairs of target nodes and their context. These pairs are taken form sequences of parameterized random walks sampled from the brain graph. \n", 25 | "\n", 26 | "\n", 27 | "\"Random\n", 28 | "\n", 29 | "\n", 30 | "\n", 31 | "In the notebook we will go over the details of the random walks generation.\n", 32 | "\n", 33 | "We will start by importing some relevant packages:" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "metadata": { 39 | "id": "yLIacLTgP6eX" 40 | }, 41 | "source": [ 42 | "%%capture\n", 43 | "!pip install cepy\n", 44 | "!pip install seaborn\n", 45 | "!pip install -U scikit-learn\n", 46 | "%matplotlib inline\n", 47 | "from matplotlib import pyplot as plt\n", 48 | "import numpy as np\n", 49 | "import seaborn as sns\n", 50 | "import pandas as pd\n", 51 | "import cepy as ce" 52 | ], 53 | "execution_count": 14, 54 | "outputs": [] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "metadata": { 59 | "id": "Bv7K8hirq2Ey" 60 | }, 61 | "source": [ 62 | "Download the structural connectivity data:\n" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "metadata": { 68 | "id": "mDKnb_curEdT" 69 | }, 70 | "source": [ 71 | "%%capture\n", 72 | "\n", 73 | "# structural connectivity matrix\n", 74 | "!wget -O sub1_sc_matrix.npz 'https://github.com/GidLev/cepy/blob/master/examples/data/sub1_sc_matrix.npz?raw=true';\n", 75 | "sc_mat = np.load('sub1_sc_matrix.npz')['x']\n", 76 | "\n", 77 | "# nodes labels\n", 78 | "!wget -O schaefer200_yeo17_nodeName.csv 'https://github.com/GidLev/cepy/blob/master/examples/data/schaefer200_yeo17_nodeName.csv?raw=true';" 79 | ], 80 | "execution_count": 15, 81 | "outputs": [] 82 | }, 83 | { 84 | "cell_type": "markdown", 85 | "metadata": { 86 | "id": "WU-uRbAVP91u" 87 | }, 88 | "source": [ 89 | "## Sample random walk from the structural connectivity (SC) matrix:\n", 90 | "\n", 91 | "First, let plot the SC matrix:\n", 92 | "\n", 93 | "\n" 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "metadata": { 99 | "colab": { 100 | "base_uri": "https://localhost:8080/", 101 | "height": 290 102 | }, 103 | "id": "-eneqkXrP_KF", 104 | "outputId": "4d51c545-72e7-4d67-8323-5c1613e78d15" 105 | }, 106 | "source": [ 107 | "# plot the SC matrix\n", 108 | "plt.matshow(np.log1p(sc_mat)) # we use a logarithmic transformation due to the weights distributions\n", 109 | "plt.title('Structural connectivity matrix', size=20);" 110 | ], 111 | "execution_count": 16, 112 | "outputs": [ 113 | { 114 | "output_type": "display_data", 115 | "data": { 116 | "image/png": "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\n", 117 | "text/plain": [ 118 | "
" 119 | ] 120 | }, 121 | "metadata": { 122 | "tags": [], 123 | "needs_background": "light" 124 | } 125 | } 126 | ] 127 | }, 128 | { 129 | "cell_type": "markdown", 130 | "metadata": { 131 | "id": "4W8CN3kO5K_5" 132 | }, 133 | "source": [ 134 | "Applying an L1 normalization resutlts in the transition matrix of an unbiased random walk:" 135 | ] 136 | }, 137 | { 138 | "cell_type": "code", 139 | "metadata": { 140 | "id": "Qym29F0iP73j" 141 | }, 142 | "source": [ 143 | "column_sums = np.sum(sc_mat,axis=1)\n", 144 | "sc_mat_l1 = sc_mat / column_sums[:, np.newaxis]" 145 | ], 146 | "execution_count": 17, 147 | "outputs": [] 148 | }, 149 | { 150 | "cell_type": "markdown", 151 | "metadata": { 152 | "id": "A7nYL7CF6Jdd" 153 | }, 154 | "source": [ 155 | "In the node2vec algorithm, we generate parameterized random walks. The are guided by two parameters, the return parameter p and the in-out parameter q (Grover & Leskovec, 2016). The parameter p sets the likelihood for a random walker to immediately revisit a node, i.e. that its step at time t+1 would be the same node visited in time t-1. The parameter q controls the likelihood of a random walker to visit nodes that are not directly linked to the node visited in the previous step, i.e. that its step in time t+1 would be to a node with edge = 0 with the node visited in time t-1. \n", 156 | "\n", 157 | "\"p\n", 158 | "\n", 159 | "Let's define the transition function:" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "metadata": { 165 | "id": "ZvCK6a5DI1A5" 166 | }, 167 | "source": [ 168 | "def alpha(mat,t_prev,t_next,p,q):\n", 169 | " '''The random walker transition function'''\n", 170 | " if t_prev==t_next:\n", 171 | " return 1.0/p\n", 172 | " elif mat[t_prev,t_next]>0:\n", 173 | " return 1.0\n", 174 | " else:\n", 175 | " return 1.0/q\n", 176 | "p, q= 0.1, 1.6 # set p and q as in the paper" 177 | ], 178 | "execution_count": 18, 179 | "outputs": [] 180 | }, 181 | { 182 | "cell_type": "markdown", 183 | "metadata": { 184 | "id": "_yH4w4ERDMFs" 185 | }, 186 | "source": [ 187 | "Then create a dictionary containing all the probability transitions:" 188 | ] 189 | }, 190 | { 191 | "cell_type": "code", 192 | "metadata": { 193 | "id": "isyn1VDdETlE" 194 | }, 195 | "source": [ 196 | "num_nodes=sc_mat.shape[0]\n", 197 | "prob_transition = {} # Initiate the dictionary\n", 198 | "for t_prev in np.arange(sc_mat.shape[0]):\n", 199 | " for t in np.arange(sc_mat.shape[1]):\n", 200 | " #if edge t-1 to t exists\n", 201 | " if sc_mat_l1[t_prev,t]>0:\n", 202 | " pi=np.zeros(num_nodes)\n", 203 | " for t_next in np.arange(num_nodes):\n", 204 | " #if edge from t to t+1 exists. i.e possible next node\n", 205 | " if sc_mat_l1[t,t_next]>0:\n", 206 | " pi[t_next]=alpha(sc_mat_l1,t_prev,t,p,q)*sc_mat_l1[t,t_next]\n", 207 | " #pi[t_next] is an unormlized transion probability from t to t_next given t_prev as the previous step\n", 208 | " pi=pi/np.sum(pi)\n", 209 | " #now, pi is normalzied transion probabilities for t traversed from t_prev\n", 210 | " prob_transition[t_prev,t]=pi" 211 | ], 212 | "execution_count": 19, 213 | "outputs": [] 214 | }, 215 | { 216 | "cell_type": "markdown", 217 | "metadata": { 218 | "id": "y6hLuWkMNRb2" 219 | }, 220 | "source": [ 221 | "Now let's sample a random walk sequence from the pre-computed transition probabilities:" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "metadata": { 227 | "id": "dVTkBX6gNlnr" 228 | }, 229 | "source": [ 230 | "walk_length = 20 # as in the paper\n", 231 | "all_nodes = np.arange(num_nodes)\n", 232 | "#Let's start a random walk a random node\n", 233 | "first_node = np.random.randint(num_nodes)\n", 234 | "# The first step is sampled based only on edge values \n", 235 | "second_node = np.random.choice(all_nodes, 1, p=sc_mat_l1[first_node,:] / sc_mat_l1[first_node,:].sum())[0]\n", 236 | "# The following steps will follow the transition function we defined\n", 237 | "random_walk=[first_node,second_node]\n", 238 | "for i in np.arange(walk_length-2):\n", 239 | " node_t = random_walk[-1]\n", 240 | " node_t_prev = random_walk[-2]\n", 241 | " next_node=np.random.choice(all_nodes, 1, p=prob_transition[node_t_prev,node_t])[0]\n", 242 | " random_walk.append(next_node)" 243 | ], 244 | "execution_count": 20, 245 | "outputs": [] 246 | }, 247 | { 248 | "cell_type": "markdown", 249 | "metadata": { 250 | "id": "8REYdG_Yr3DS" 251 | }, 252 | "source": [ 253 | "Now let's look at the sampled random walk:" 254 | ] 255 | }, 256 | { 257 | "cell_type": "code", 258 | "metadata": { 259 | "colab": { 260 | "base_uri": "https://localhost:8080/" 261 | }, 262 | "id": "B1taubuir9b0", 263 | "outputId": "cb7b1f9d-6667-4805-c174-1d25fbe24746" 264 | }, 265 | "source": [ 266 | "# get the nodes names:\n", 267 | "nodes_indices = np.arange(num_nodes)\n", 268 | "nodes_labels = pd.read_csv('schaefer200_yeo17_nodeName.csv', header=None).iloc[:,0].tolist()\n", 269 | "nodes_labels = [node[11:] for node in nodes_labels]\n", 270 | "\n", 271 | "nodes_index_to_label = dict(zip(nodes_indices, nodes_labels)) \n", 272 | "random_walk_nodes_labels = list(map(nodes_index_to_label.get, random_walk))\n", 273 | "\n", 274 | "print('Random walk nodes indices:', random_walk)\n", 275 | "print('Random walk nodes names:', random_walk_nodes_labels)" 276 | ], 277 | "execution_count": 21, 278 | "outputs": [ 279 | { 280 | "output_type": "stream", 281 | "text": [ 282 | "Random walk nodes indices: [147, 146, 185, 30, 131, 103, 130, 178, 42, 90, 142, 12, 37, 57, 96, 4, 11, 5, 2, 53]\n", 283 | "Random walk nodes names: ['RH_SalVentAttnA_FrMed_2', 'RH_SalVentAttnA_FrMed_1', 'RH_DefaultA_PCC_1', 'LH_DorsAttnA_TempOcc_3', 'RH_DorsAttnA_TempOcc_2', 'RH_VisCent_ExStr_4', 'RH_DorsAttnA_TempOcc_1', 'RH_ContB_PFCmp_1', 'LH_SalVentAttnA_Ins_3', 'LH_DefaultB_PFCd_4', 'RH_SalVentAttnA_PrC_1', 'LH_SomMotA_1', 'LH_DorsAttnB_PostC_4', 'LH_ContA_IPS_1', 'LH_DefaultC_Rsp_1', 'LH_VisCent_ExStr_5', 'LH_VisPeri_ExStrSup_6', 'LH_VisCent_ExStr_6', 'LH_VisCent_ExStr_3', 'LH_Limbic_TempPole_2']\n" 284 | ], 285 | "name": "stdout" 286 | } 287 | ] 288 | }, 289 | { 290 | "cell_type": "markdown", 291 | "metadata": { 292 | "id": "zOIXOy10L0zB" 293 | }, 294 | "source": [ 295 | "In practice we use cepy's more efficient implamentation:" 296 | ] 297 | }, 298 | { 299 | "cell_type": "code", 300 | "metadata": { 301 | "colab": { 302 | "base_uri": "https://localhost:8080/" 303 | }, 304 | "id": "o9oF2XKxL0H-", 305 | "outputId": "c9549cf1-b685-4a97-fd8c-d20c59fb402f" 306 | }, 307 | "source": [ 308 | "ce_model = ce.CE(dimensions=30, walk_length=walk_length, permutations = 1,\n", 309 | " num_walks=2, save_walks = True) # Precompute probabilities and generate walks\n", 310 | "ce_model.fit(sc_mat) \n", 311 | "random_walk = [[int(float(j)) for j in i] for i in ce_model.walks] # nodes apear as strings, converting to integers\n", 312 | "print('Random walk nodes indices:',random_walk[0])\n", 313 | "\n", 314 | "random_walk_nodes_labels = list(map(nodes_index_to_label.get, random_walk[0]))\n", 315 | "print('Random walk nodes names:', random_walk_nodes_labels)\n" 316 | ], 317 | "execution_count": 22, 318 | "outputs": [ 319 | { 320 | "output_type": "stream", 321 | "text": [ 322 | "/usr/local/lib/python3.6/dist-packages/cepy/ce.py:109: UserWarning: num_walks is recommended to be at least 800, but is 2.\n", 323 | " warnings.warn('num_walks is recommended to be at least 800, but is ' + str(num_walks) + '.')\n" 324 | ], 325 | "name": "stderr" 326 | }, 327 | { 328 | "output_type": "stream", 329 | "text": [ 330 | "Start training 100 word2vec models on 1 threads.\n", 331 | "Random walk nodes indices: [65, 76, 65, 81, 69, 82, 6, 4, 6, 8, 7, 9, 105, 141, 150, 171, 72, 36, 34, 40]\n", 332 | "Random walk nodes names: ['LH_ContA_Cinga_1', 'LH_DefaultA_PCC_1', 'LH_ContA_Cinga_1', 'LH_DefaultA_PFCm_3', 'LH_ContB_PFClv_1', 'LH_DefaultB_Temp_1', 'LH_VisPeri_ExStrSup_1', 'LH_VisCent_ExStr_5', 'LH_VisPeri_ExStrSup_1', 'LH_VisPeri_ExStrSup_3', 'LH_VisPeri_ExStrSup_2', 'LH_VisPeri_ExStrSup_4', 'RH_VisCent_ExStr_6', 'RH_SalVentAttnA_ParOper_1', 'RH_SalVentAttnB_IPL_1', 'RH_ContB_Temp_2', 'LH_ContC_pCun_2', 'LH_DorsAttnB_PostC_3', 'LH_DorsAttnB_PostC_1', 'LH_SalVentAttnA_Ins_1']\n" 333 | ], 334 | "name": "stdout" 335 | } 336 | ] 337 | }, 338 | { 339 | "cell_type": "markdown", 340 | "metadata": { 341 | "id": "paipX0us9mXt" 342 | }, 343 | "source": [ 344 | "### references\n", 345 | "\n", 346 | "* https://github.com/apple2373/node2vec/ (source for parts of the random walk generation code)\n", 347 | "* Mikolov, T., Sutskever, I., Chen, K., Corrado, G. S., & Dean, J. (2013). Distributed representations of words and phrases and their compositionality. In Advances in neural information processing systems (pp. 3111-3119).‏\n", 348 | "* Rosenthal, G., Váša, F., Griffa, A., Hagmann, P., Amico, E., Goñi, J., ... & Sporns, O. (2018). Mapping higher-order relations between brain structure and function with embedded vector representations of connectomes. Nature communications, 9(1), 1-12.‏" 349 | ] 350 | } 351 | ] 352 | } -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | networkx 2 | gensim 3 | numpy 4 | tqdm 5 | joblib>=0.13.2 -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r", encoding="utf-8") as fh: 4 | long_description = fh.read() 5 | 6 | with open('requirements.txt') as f: 7 | required = f.read().splitlines() 8 | 9 | 10 | setuptools.setup( 11 | name="cepy", # Replace with your own username 12 | version="1.0.0", 13 | author="Gidon Levakov", 14 | author_email="gidonlevakov@gmail.com", 15 | description="Implementation of the connectome embedding workflow.", 16 | long_description=long_description, 17 | long_description_content_type="text/markdown", 18 | url="https://github.com/gidlev/cepy", 19 | packages=setuptools.find_packages(), 20 | install_requires = required, 21 | include_package_data = True, 22 | classifiers=[ 23 | "Programming Language :: Python :: 3", 24 | "License :: OSI Approved :: MIT License", 25 | "Operating System :: OS Independent", 26 | ], 27 | python_requires='>=3.5', 28 | ) 29 | 30 | --------------------------------------------------------------------------------