├── requirements.txt ├── testdata ├── readme.txt └── testdata.csv ├── .gitignore ├── .travis.yml ├── neuroCombat ├── __init__.py └── neuroCombat.py ├── news.txt ├── setup.py ├── LICENSE └── README.md /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy==1.16.5 2 | pandas==1.0.3 -------------------------------------------------------------------------------- /testdata/readme.txt: -------------------------------------------------------------------------------- 1 | Test data: 2 | - 200 rows (features) 3 | - 10 columns (images) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | neuroCombat/.DS_Store 3 | neuroCombat/__pycache__/ 4 | dist/ 5 | neuroCombat.egg-info -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.7" 4 | install: 5 | - pip install -r requirements.txt 6 | script: pytest -------------------------------------------------------------------------------- /neuroCombat/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | from .neuroCombat import neuroCombat 4 | from .neuroCombat import neuroCombatFromTraining -------------------------------------------------------------------------------- /news.txt: -------------------------------------------------------------------------------- 1 | Version 0.2.12 2 | - Bugfix: A bug was introduced in 0.2.11 for adjusted ComBat. Fixed now. 3 | 4 | Version 0.2.10 5 | - Feature:Implemented neuroCombatFromTraining 6 | - Change: neuroCombat now outputs both the harmonized data and the ComBat estimates 7 | 8 | Version 0.2.9 9 | - Bugfix: dividing by 0 when values are constant within a batch/scanner group 10 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import setuptools 4 | 5 | with open("README.md", "r") as fh: 6 | long_description = fh.read() 7 | 8 | setuptools.setup( 9 | author="Jean-Philippe Fortin, Nick Cullen, Tim Robert-Fitzgerald", 10 | author_email='fortin946@gmail.com,', 11 | classifiers=[ 12 | 'License :: OSI Approved :: MIT License', 13 | 'Programming Language :: Python :: 3.7', 14 | ], 15 | description="ComBat algorithm for harmonizing multi-site imaging data", 16 | license="MIT license", 17 | url="https://github.com/Jfortin1/neuroCombat", 18 | project_urls={ 19 | "Github": "https://github.com/Jfortin1/neuroCombat", 20 | }, 21 | name='neuroCombat', 22 | packages=['neuroCombat'], 23 | version='0.2.12', 24 | zip_safe=False, 25 | ) 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jean-Philippe Fortin, Nick Cullen 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Multi-site harmonization in Python with neuroCombat 2 | 3 | [![License: MIT](https://img.shields.io/github/license/Jfortin1/neuroCombat)](https://opensource.org/licenses/MIT) 4 | [![Version](https://img.shields.io/pypi/v/neuroCombat)](https://pypi.org/project/neuroCombat/) 5 | [![PythonVersion](https://img.shields.io/pypi/pyversions/neuroCombat)]() 6 | 7 | 8 | This is the maintained and official version of neuroCombat (previously hosted [here](https://github.com/ncullen93/neuroCombat)) introduced in our [our recent paper](https://www.sciencedirect.com/science/article/pii/S105381191730931X). 9 | 10 | 11 | ## Installation 12 | 13 | neuroCombat is hosted on PyPI, and the easiest way to install neuroCombat is to use the ```pip``` command: 14 | 15 | ``` 16 | pip install neuroCombat 17 | ``` 18 | 19 | ## Usage 20 | 21 | The ```neuroCombat``` function performs harmonization 22 | 23 | ```python 24 | from neuroCombat import neuroCombat 25 | import pandas as pd 26 | import numpy as np 27 | 28 | # Getting example data 29 | # 200 rows (features) and 10 columns (scans) 30 | data = np.genfromtxt('testdata/testdata.csv', delimiter=",", skip_header=1) 31 | 32 | # Specifying the batch (scanner variable) as well as a biological covariate to preserve: 33 | covars = {'batch':[1,1,1,1,1,2,2,2,2,2], 34 | 'gender':[1,2,1,2,1,2,1,2,1,2]} 35 | covars = pd.DataFrame(covars) 36 | 37 | # To specify names of the variables that are categorical: 38 | categorical_cols = ['gender'] 39 | 40 | # To specify the name of the variable that encodes for the scanner/batch covariate: 41 | batch_col = 'batch' 42 | 43 | #Harmonization step: 44 | data_combat = neuroCombat(dat=data, 45 | covars=covars, 46 | batch_col=batch_col, 47 | categorical_cols=categorical_cols)["data"] 48 | ``` 49 | 50 | ## Optional arguments 51 | 52 | - `eb` : `True` or `False`. Should Empirical Bayes be performed? If `False`, the harmonization model will be fit for each feature separately. This is equivalent to performing a location/shift (L/S) correction to each feature separately (no information pooling across features). 53 | 54 | - `parametric` : `True` or `False`. Should parametric adjustements be performed? `True` by default. 55 | 56 | - `mean_only` : `True` or `False`. Should only be means adjusted (no scaling)? `False` by default 57 | 58 | - `ref_batch` : batch name to be used as the reference batch for harmonization. `None` by default, in which case the average across scans/images/sites is taken as the reference batch. 59 | 60 | ## Output 61 | 62 | Since version 0.2.10, the `neuroCombat` function outputs a dictionary with 3 elements: 63 | - `data`: A numpy array of the harmonized data, with the same dimension (shape) as the input data. 64 | - `estimates`: A dictionary of the neuroCombat estimates; useful for visualization and understand scanner effects. 65 | - `info`: A dictionary of the inputs needed for ComBat harmonization (batch/scanner information, etc.) 66 | 67 | To simply return the harmonized data, one can use the following: 68 | 69 | ``` 70 | data_combat = neuroCombat(dat=dat, ...)["data"] 71 | ``` 72 | 73 | where `...` are the user-specified arguments needed for harmonization. 74 | 75 | 76 | -------------------------------------------------------------------------------- /neuroCombat/neuroCombat.py: -------------------------------------------------------------------------------- 1 | # Originally written by Nick Cullen 2 | # Extended and currently maintained by JP Fortin 3 | from __future__ import absolute_import, print_function 4 | import pandas as pd 5 | import numpy as np 6 | import numpy.linalg as la 7 | import math 8 | import copy 9 | 10 | def neuroCombat(dat, 11 | covars, 12 | batch_col, 13 | categorical_cols=None, 14 | continuous_cols=None, 15 | eb=True, 16 | parametric=True, 17 | mean_only=False, 18 | ref_batch=None): 19 | """ 20 | Run ComBat to remove scanner effects in multi-site imaging data 21 | 22 | Arguments 23 | --------- 24 | dat : a pandas data frame or numpy array 25 | - neuroimaging data to correct with shape = (features, samples) e.g. cortical thickness measurements, image voxels, etc 26 | 27 | covars : a pandas data frame w/ shape = (samples, covariates) 28 | - contains the batch/scanner covariate as well as additional covariates (optional) that should be preserved during harmonization. 29 | 30 | batch_col : string 31 | - indicates batch (scanner) column name in covars (e.g. "scanner") 32 | 33 | categorical_cols : list of strings 34 | - specifies column names in covars data frame of categorical variables to be preserved during harmonization (e.g. ["sex", "disease"]) 35 | 36 | continuous_cols : list of strings 37 | - indicates column names in covars data frame of continuous variables to be preserved during harmonization (e.g. ["age"]) 38 | 39 | eb : should Empirical Bayes be performed? 40 | - True by default 41 | 42 | parametric : should parametric adjustements be performed? 43 | - True by default 44 | 45 | mean_only : should only be the mean adjusted (no scaling)? 46 | - False by default 47 | 48 | ref_batch : batch (site or scanner) to be used as reference for batch adjustment. 49 | - None by default 50 | 51 | Returns 52 | ------- 53 | A dictionary of length 3: 54 | - data: A numpy array with the same shape as `dat` which has now been ComBat-harmonized 55 | - estimates: A dictionary of the ComBat estimates used for harmonization 56 | - info: A dictionary of the inputs needed for ComBat harmonization 57 | """ 58 | ############################## 59 | ### CLEANING UP INPUT DATA ### 60 | ############################## 61 | if not isinstance(covars, pd.DataFrame): 62 | raise ValueError('covars must be pandas dataframe -> try: covars = pandas.DataFrame(covars)') 63 | 64 | if not isinstance(categorical_cols, (list,tuple)): 65 | if categorical_cols is None: 66 | categorical_cols = [] 67 | else: 68 | categorical_cols = [categorical_cols] 69 | if not isinstance(continuous_cols, (list,tuple)): 70 | if continuous_cols is None: 71 | continuous_cols = [] 72 | else: 73 | continuous_cols = [continuous_cols] 74 | 75 | covar_labels = np.array(covars.columns) 76 | covars = np.array(covars, dtype='object') 77 | 78 | if isinstance(dat, pd.DataFrame): 79 | dat = np.array(dat, dtype='float32') 80 | 81 | 82 | 83 | ############################## 84 | 85 | # get column indices for relevant variables 86 | batch_col = np.where(covar_labels==batch_col)[0][0] 87 | cat_cols = [np.where(covar_labels==c_var)[0][0] for c_var in categorical_cols] 88 | num_cols = [np.where(covar_labels==n_var)[0][0] for n_var in continuous_cols] 89 | 90 | # convert batch col to integer 91 | if ref_batch is None: 92 | ref_level=None 93 | else: 94 | ref_indices = np.argwhere(covars[:, batch_col] == ref_batch).squeeze() 95 | if ref_indices.shape[0]==0: 96 | ref_level=None 97 | ref_batch=None 98 | print('[neuroCombat] batch.ref not found. Setting to None.') 99 | covars[:,batch_col] = np.unique(covars[:,batch_col],return_inverse=True)[-1] 100 | else: 101 | covars[:,batch_col] = np.unique(covars[:,batch_col],return_inverse=True)[-1] 102 | ref_level = covars[np.int(ref_indices[0]),batch_col] 103 | # create dictionary that stores batch info 104 | (batch_levels, sample_per_batch) = np.unique(covars[:,batch_col],return_counts=True) 105 | 106 | 107 | # create design matrix 108 | print('[neuroCombat] Creating design matrix') 109 | design = make_design_matrix(covars, batch_col, cat_cols, num_cols, ref_level) 110 | 111 | 112 | info_dict = { 113 | 'batch_levels': batch_levels, 114 | 'ref_level': ref_level, 115 | 'n_batch': len(batch_levels), 116 | 'n_sample': int(covars.shape[0]), 117 | 'sample_per_batch': sample_per_batch.astype('int'), 118 | 'batch_info': [list(np.where(covars[:,batch_col]==idx)[0]) for idx in batch_levels], 119 | 'design': design 120 | } 121 | 122 | 123 | 124 | 125 | # standardize data across features 126 | print('[neuroCombat] Standardizing data across features') 127 | s_data, s_mean, v_pool, mod_mean = standardize_across_features(dat, design, info_dict) 128 | 129 | # fit L/S models and find priors 130 | print('[neuroCombat] Fitting L/S model and finding priors') 131 | LS_dict = fit_LS_model_and_find_priors(s_data, design, info_dict, mean_only) 132 | 133 | # find parametric adjustments 134 | if eb: 135 | if parametric: 136 | print('[neuroCombat] Finding parametric adjustments') 137 | gamma_star, delta_star = find_parametric_adjustments(s_data, LS_dict, info_dict, mean_only) 138 | else: 139 | print('[neuroCombat] Finding non-parametric adjustments') 140 | gamma_star, delta_star = find_non_parametric_adjustments(s_data, LS_dict, info_dict, mean_only) 141 | else: 142 | print('[neuroCombat] Finding L/S adjustments without Empirical Bayes') 143 | gamma_star, delta_star = find_non_eb_adjustments(s_data, LS_dict, info_dict) 144 | 145 | # adjust data 146 | print('[neuroCombat] Final adjustment of data') 147 | bayes_data = adjust_data_final(s_data, design, gamma_star, delta_star, 148 | s_mean, mod_mean, v_pool, info_dict,dat) 149 | 150 | bayes_data = np.array(bayes_data) 151 | estimates = {'batches': info_dict['batch_levels'], 'var.pooled': v_pool, 'stand.mean': s_mean, 'mod.mean': mod_mean, 'gamma.star': gamma_star, 'delta.star': delta_star} 152 | estimates = {**LS_dict, **estimates, } 153 | 154 | return { 155 | 'data': bayes_data, 156 | 'estimates': estimates, 157 | 'info': info_dict 158 | } 159 | 160 | 161 | 162 | 163 | def make_design_matrix(Y, batch_col, cat_cols, num_cols, ref_level): 164 | """ 165 | Return Matrix containing the following parts: 166 | - one-hot matrix of batch variable (full) 167 | - one-hot matrix for each categorical_cols (removing the first column) 168 | - column for each continuous_cols 169 | """ 170 | def to_categorical(y, nb_classes=None): 171 | if not nb_classes: 172 | nb_classes = np.max(y)+1 173 | Y = np.zeros((len(y), nb_classes)) 174 | for i in range(len(y)): 175 | Y[i, y[i]] = 1. 176 | return Y 177 | 178 | hstack_list = [] 179 | 180 | ### batch one-hot ### 181 | # convert batch column to integer in case it's string 182 | batch = np.unique(Y[:,batch_col],return_inverse=True)[-1] 183 | batch_onehot = to_categorical(batch, len(np.unique(batch))) 184 | if ref_level is not None: 185 | batch_onehot[:,ref_level] = np.ones(batch_onehot.shape[0]) 186 | hstack_list.append(batch_onehot) 187 | 188 | ### categorical one-hots ### 189 | for cat_col in cat_cols: 190 | cat = np.unique(np.array(Y[:,cat_col]),return_inverse=True)[1] 191 | cat_onehot = to_categorical(cat, len(np.unique(cat)))[:,1:] 192 | hstack_list.append(cat_onehot) 193 | 194 | ### numerical vectors ### 195 | for num_col in num_cols: 196 | num = np.array(Y[:,num_col],dtype='float32') 197 | num = num.reshape(num.shape[0],1) 198 | hstack_list.append(num) 199 | 200 | design = np.hstack(hstack_list) 201 | return design 202 | 203 | 204 | def standardize_across_features(X, design, info_dict): 205 | n_batch = info_dict['n_batch'] 206 | n_sample = info_dict['n_sample'] 207 | sample_per_batch = info_dict['sample_per_batch'] 208 | batch_info = info_dict['batch_info'] 209 | ref_level = info_dict['ref_level'] 210 | 211 | def get_beta_with_nan(yy, mod): 212 | wh = np.isfinite(yy) 213 | mod = mod[wh,:] 214 | yy = yy[wh] 215 | B = np.dot(np.dot(la.inv(np.dot(mod.T, mod)), mod.T), yy.T) 216 | return B 217 | 218 | betas = [] 219 | for i in range(X.shape[0]): 220 | betas.append(get_beta_with_nan(X[i,:], design)) 221 | B_hat = np.vstack(betas).T 222 | 223 | #B_hat = np.dot(np.dot(la.inv(np.dot(design.T, design)), design.T), X.T) 224 | if ref_level is not None: 225 | grand_mean = np.transpose(B_hat[ref_level,:]) 226 | else: 227 | grand_mean = np.dot((sample_per_batch/ float(n_sample)).T, B_hat[:n_batch,:]) 228 | stand_mean = np.dot(grand_mean.T.reshape((len(grand_mean), 1)), np.ones((1, n_sample))) 229 | #var_pooled = np.dot(((X - np.dot(design, B_hat).T)**2), np.ones((n_sample, 1)) / float(n_sample)) 230 | 231 | if ref_level is not None: 232 | X_ref = X[:,batch_info[ref_level]] 233 | design_ref = design[batch_info[ref_level],:] 234 | n_sample_ref = sample_per_batch[ref_level] 235 | var_pooled = np.dot(((X_ref - np.dot(design_ref, B_hat).T)**2), np.ones((n_sample_ref, 1)) / float(n_sample_ref)) 236 | else: 237 | var_pooled = np.dot(((X - np.dot(design, B_hat).T)**2), np.ones((n_sample, 1)) / float(n_sample)) 238 | 239 | var_pooled[var_pooled==0] = np.median(var_pooled!=0) 240 | 241 | mod_mean = 0 242 | if design is not None: 243 | tmp = copy.deepcopy(design) 244 | tmp[:,range(0,n_batch)] = 0 245 | mod_mean = np.transpose(np.dot(tmp, B_hat)) 246 | ######### Continue here. 247 | 248 | 249 | #tmp = np.array(design.copy()) 250 | #tmp[:,:n_batch] = 0 251 | #stand_mean += np.dot(tmp, B_hat).T 252 | 253 | s_data = ((X- stand_mean - mod_mean) / np.dot(np.sqrt(var_pooled), np.ones((1, n_sample)))) 254 | 255 | return s_data, stand_mean, var_pooled, mod_mean 256 | 257 | def aprior(delta_hat): 258 | m = np.mean(delta_hat) 259 | s2 = np.var(delta_hat,ddof=1) 260 | return (2 * s2 +m**2) / float(s2) 261 | 262 | def bprior(delta_hat): 263 | m = delta_hat.mean() 264 | s2 = np.var(delta_hat,ddof=1) 265 | return (m*s2+m**3)/s2 266 | 267 | def postmean(g_hat, g_bar, n, d_star, t2): 268 | return (t2*n*g_hat+d_star * g_bar) / (t2*n+d_star) 269 | 270 | def postvar(sum2, n, a, b): 271 | return (0.5 * sum2 + b) / (n / 2.0 + a - 1.0) 272 | 273 | def convert_zeroes(x): 274 | x[x==0] = 1 275 | return x 276 | 277 | def fit_LS_model_and_find_priors(s_data, design, info_dict, mean_only): 278 | n_batch = info_dict['n_batch'] 279 | batch_info = info_dict['batch_info'] 280 | 281 | batch_design = design[:,:n_batch] 282 | gamma_hat = np.dot(np.dot(la.inv(np.dot(batch_design.T, batch_design)), batch_design.T), s_data.T) 283 | 284 | delta_hat = [] 285 | for i, batch_idxs in enumerate(batch_info): 286 | if mean_only: 287 | delta_hat.append(np.repeat(1, s_data.shape[0])) 288 | else: 289 | delta_hat.append(np.var(s_data[:,batch_idxs],axis=1,ddof=1)) 290 | 291 | delta_hat = list(map(convert_zeroes,delta_hat)) 292 | gamma_bar = np.mean(gamma_hat, axis=1) 293 | t2 = np.var(gamma_hat,axis=1, ddof=1) 294 | 295 | if mean_only: 296 | a_prior = None 297 | b_prior = None 298 | else: 299 | a_prior = list(map(aprior, delta_hat)) 300 | b_prior = list(map(bprior, delta_hat)) 301 | 302 | LS_dict = {} 303 | LS_dict['gamma_hat'] = gamma_hat 304 | LS_dict['delta_hat'] = delta_hat 305 | LS_dict['gamma_bar'] = gamma_bar 306 | LS_dict['t2'] = t2 307 | LS_dict['a_prior'] = a_prior 308 | LS_dict['b_prior'] = b_prior 309 | return LS_dict 310 | 311 | #Helper function for parametric adjustements: 312 | def it_sol(sdat, g_hat, d_hat, g_bar, t2, a, b, conv=0.0001): 313 | n = (1 - np.isnan(sdat)).sum(axis=1) 314 | g_old = g_hat.copy() 315 | d_old = d_hat.copy() 316 | 317 | change = 1 318 | count = 0 319 | while change > conv: 320 | g_new = postmean(g_hat, g_bar, n, d_old, t2) 321 | sum2 = ((sdat - np.dot(g_new.reshape((g_new.shape[0], 1)), np.ones((1, sdat.shape[1])))) ** 2).sum(axis=1) 322 | d_new = postvar(sum2, n, a, b) 323 | 324 | change = max((abs(g_new - g_old) / g_old).max(), (abs(d_new - d_old) / d_old).max()) 325 | g_old = g_new #.copy() 326 | d_old = d_new #.copy() 327 | count = count + 1 328 | adjust = (g_new, d_new) 329 | return adjust 330 | 331 | 332 | 333 | #Helper function for non-parametric adjustements: 334 | def int_eprior(sdat, g_hat, d_hat): 335 | r = sdat.shape[0] 336 | gamma_star, delta_star = [], [] 337 | for i in range(0,r,1): 338 | g = np.delete(g_hat,i) 339 | d = np.delete(d_hat,i) 340 | x = sdat[i,:] 341 | n = x.shape[0] 342 | j = np.repeat(1,n) 343 | A = np.repeat(x, g.shape[0]) 344 | A = A.reshape(n,g.shape[0]) 345 | A = np.transpose(A) 346 | B = np.repeat(g, n) 347 | B = B.reshape(g.shape[0],n) 348 | resid2 = np.square(A-B) 349 | sum2 = resid2.dot(j) 350 | LH = 1/(2*math.pi*d)**(n/2)*np.exp(-sum2/(2*d)) 351 | LH = np.nan_to_num(LH) 352 | gamma_star.append(sum(g*LH)/sum(LH)) 353 | delta_star.append(sum(d*LH)/sum(LH)) 354 | adjust = (gamma_star, delta_star) 355 | return adjust 356 | 357 | 358 | def find_parametric_adjustments(s_data, LS, info_dict, mean_only): 359 | batch_info = info_dict['batch_info'] 360 | ref_level = info_dict['ref_level'] 361 | 362 | gamma_star, delta_star = [], [] 363 | for i, batch_idxs in enumerate(batch_info): 364 | if mean_only: 365 | gamma_star.append(postmean(LS['gamma_hat'][i], LS['gamma_bar'][i], 1, 1, LS['t2'][i])) 366 | delta_star.append(np.repeat(1, s_data.shape[0])) 367 | else: 368 | temp = it_sol(s_data[:,batch_idxs], LS['gamma_hat'][i], 369 | LS['delta_hat'][i], LS['gamma_bar'][i], LS['t2'][i], 370 | LS['a_prior'][i], LS['b_prior'][i]) 371 | gamma_star.append(temp[0]) 372 | delta_star.append(temp[1]) 373 | 374 | gamma_star = np.array(gamma_star) 375 | delta_star = np.array(delta_star) 376 | 377 | if ref_level is not None: 378 | gamma_star[ref_level,:] = np.zeros(gamma_star.shape[-1]) 379 | delta_star[ref_level,:] = np.ones(delta_star.shape[-1]) 380 | 381 | return gamma_star, delta_star 382 | 383 | def find_non_parametric_adjustments(s_data, LS, info_dict, mean_only): 384 | batch_info = info_dict['batch_info'] 385 | ref_level = info_dict['ref_level'] 386 | 387 | gamma_star, delta_star = [], [] 388 | for i, batch_idxs in enumerate(batch_info): 389 | if mean_only: 390 | LS['delta_hat'][i] = np.repeat(1, s_data.shape[0]) 391 | temp = int_eprior(s_data[:,batch_idxs], LS['gamma_hat'][i], 392 | LS['delta_hat'][i]) 393 | 394 | gamma_star.append(temp[0]) 395 | delta_star.append(temp[1]) 396 | 397 | gamma_star = np.array(gamma_star) 398 | delta_star = np.array(delta_star) 399 | 400 | if ref_level is not None: 401 | gamma_star[ref_level,:] = np.zeros(gamma_star.shape[-1]) 402 | delta_star[ref_level,:] = np.ones(delta_star.shape[-1]) 403 | 404 | return gamma_star, delta_star 405 | 406 | def find_non_eb_adjustments(s_data, LS, info_dict): 407 | gamma_star = np.array(LS['gamma_hat']) 408 | delta_star = np.array(LS['delta_hat']) 409 | ref_level = info_dict['ref_level'] 410 | 411 | if ref_level is not None: 412 | gamma_star[ref_level,:] = np.zeros(gamma_star.shape[-1]) 413 | delta_star[ref_level,:] = np.ones(delta_star.shape[-1]) 414 | 415 | return gamma_star, delta_star 416 | 417 | def adjust_data_final(s_data, design, gamma_star, delta_star, stand_mean, mod_mean, var_pooled, info_dict, dat): 418 | sample_per_batch = info_dict['sample_per_batch'] 419 | n_batch = info_dict['n_batch'] 420 | n_sample = info_dict['n_sample'] 421 | batch_info = info_dict['batch_info'] 422 | ref_level = info_dict['ref_level'] 423 | 424 | batch_design = design[:,:n_batch] 425 | 426 | bayesdata = s_data 427 | gamma_star = np.array(gamma_star) 428 | delta_star = np.array(delta_star) 429 | 430 | for j, batch_idxs in enumerate(batch_info): 431 | dsq = np.sqrt(delta_star[j,:]) 432 | dsq = dsq.reshape((len(dsq), 1)) 433 | denom = np.dot(dsq, np.ones((1, sample_per_batch[j]))) 434 | numer = np.array(bayesdata[:,batch_idxs] - np.dot(batch_design[batch_idxs,:], gamma_star).T) 435 | 436 | bayesdata[:,batch_idxs] = numer / denom 437 | 438 | vpsq = np.sqrt(var_pooled).reshape((len(var_pooled), 1)) 439 | bayesdata = bayesdata * np.dot(vpsq, np.ones((1, n_sample))) + stand_mean + mod_mean 440 | 441 | if ref_level is not None: 442 | bayesdata[:, batch_info[ref_level]] = dat[:,batch_info[ref_level]] 443 | 444 | return bayesdata 445 | 446 | 447 | 448 | 449 | def neuroCombatFromTraining(dat, 450 | batch, 451 | estimates): 452 | """ 453 | Combat harmonization with pre-trained ComBat estimates [UNDER DEVELOPMENT] 454 | 455 | Arguments 456 | --------- 457 | dat : a pandas data frame or numpy array for the new dataset to harmonize 458 | - rows must be identical to the training dataset 459 | 460 | batch : numpy array specifying scanner/batch for the new dataset 461 | - scanners/batches must also be present in the training dataset 462 | 463 | estimates : dictionary of ComBat estimates from a previously-harmonized dataset 464 | - should be in the same format as neuroCombat(...)['estimates'] 465 | 466 | Returns 467 | ------- 468 | A dictionary of length 2: 469 | - data: A numpy array with the same shape as `dat` which has now been ComBat-harmonized 470 | - estimates: A dictionary of the ComBat estimates used for harmonization 471 | """ 472 | print("[neuroCombatFromTraining] In development ...\n") 473 | batch = np.array(batch, dtype="str") 474 | new_levels = np.unique(batch) 475 | old_levels = np.array(estimates['batches'], dtype="str") 476 | missing_levels = np.setdiff1d(new_levels, old_levels) 477 | if missing_levels.shape[0] != 0: 478 | raise ValueError("The batches " + str(missing_levels) + 479 | " are not part of the training dataset") 480 | 481 | 482 | wh = [int(np.where(old_levels==x)[0]) if x in old_levels else None for x in batch] 483 | 484 | 485 | 486 | var_pooled = estimates['var.pooled'] 487 | stand_mean = estimates['stand.mean'][:, 0] 488 | mod_mean = estimates['mod.mean'] 489 | gamma_star = estimates['gamma.star'] 490 | delta_star = estimates['delta.star'] 491 | n_array = dat.shape[1] 492 | stand_mean = stand_mean+mod_mean.mean(axis=1) 493 | 494 | 495 | stand_mean = np.transpose([stand_mean, ]*n_array) 496 | bayesdata = np.subtract(dat, stand_mean)/np.sqrt(var_pooled) 497 | 498 | #gamma = np.transpose(np.repeat(gamma_star, repeats=2, axis=0)) 499 | #delta = np.transpose(np.repeat(delta_star, repeats=2, axis=0)) 500 | gamma = np.transpose(gamma_star[wh,:]) 501 | delta = np.transpose(delta_star[wh,:]) 502 | bayesdata = np.subtract(bayesdata, gamma)/np.sqrt(delta) 503 | 504 | bayesdata = bayesdata*np.sqrt(var_pooled) + stand_mean 505 | out = { 506 | 'data': bayesdata, 507 | 'estimates': estimates 508 | } 509 | return out 510 | -------------------------------------------------------------------------------- /testdata/testdata.csv: -------------------------------------------------------------------------------- 1 | V1,V2,V3,V4,V5,V6,V7,V8,V9,V10 2 | 3.01874617094183,1.21551377619565,2.92054298287955,-0.901970264410768,0.874931418956062,9.07502051896702,14.2420141440537,7.91527600084455,14.4397811953011,7.42802065766498 3 | 2.81574745793094,0.330876485104141,4.18175154981561,-0.0547499580494913,3.52117866630317,7.92913886630953,8.12734273718966,7.17716572926366,12.6566350933768,8.03653710749373 4 | 1.62866945007749,1.39027511927468,5.18614406267608,-1.35675471109976,2.95357710004389,7.86084724720018,12.091550245414,10.0624059843723,10.7408459596564,8.71348441098773 5 | 2.40083228421628,0.872046985021111,3.40617493070536,0.317230577125748,1.85052827902907,8.74905780939852,12.4916238759685,9.80764684372907,13.2808879036079,8.47158354932156 6 | 3.29454512656751,-1.08081702266027,2.26164089541514,1.39571911930986,3.6293383077002,7.16552517851154,12.1929881094064,7.14567944649055,13.0588637858025,8.3990758442553 7 | 3.38979430070017,0.495821591446535,1.04351329708109,-1.21122381679601,2.27352852400835,7.93251628430013,11.2546802527188,4.38535225855662,12.6743859604139,6.91240890644647 8 | 1.79192382457051,1.05262755621631,1.04995429840145,-0.204141660797423,2.61358267769029,6.98941189977487,12.6482616019502,6.59146054958797,12.8626443307174,8.50119427547175 9 | 2.63632398252914,-1.27464995015128,2.0590021843074,-1.55245132607939,2.32828561011569,9.09200192823285,14.1189665864774,8.92853943884894,14.7173094493535,10.0332006896429 10 | 1.37332731829691,-0.193666728423379,4.19778676862693,0.518049668614929,3.90038619036856,5.68642668282159,8.88986965899835,5.71846070093375,12.4169835367596,9.92730929490813 11 | 2.74352160587601,-1.29508364255337,2.37576060964171,-0.418840303238381,3.88684169061633,9.07861035245101,13.0341048386918,13.2188705038141,11.5562136843863,6.70498141670262 12 | 4.10177950308713,0.141880284678648,2.86713034489791,-1.70945321567921,3.39027811189946,7.3158912088216,12.9797627922373,8.46973408252356,14.009401912289,7.07831904403725 13 | 3.75578150802734,1.26171505198662,2.98277907780869,2.48316267137122,2.07248850977773,8.62288159120005,12.2970747637468,7.72330325253195,13.7648210614879,7.94938307832787 14 | 2.76176644398128,-0.431500328069645,2.54037071782094,0.498033735066409,3.7458392287806,9.78010749603544,14.0993825566975,5.89112896546396,10.8230831661555,6.82933929865863 15 | 3.98744470341339,-1.82271258950182,4.47293679052688,-0.673868205198334,3.49482415664264,7.28495454471787,12.5462763135628,7.76646593583479,10.7795207996046,8.62546041776266 16 | 3.74139012838382,0.35254395875311,5.16931961261593,-0.886981395611342,2.97806853827693,6.77762000117916,14.4611571688572,7.07298070758366,13.9243353878583,8.81048291922511 17 | 3.08934726649582,-1.34845144147378,-0.00143130631432609,-1.73137735086099,3.58802232626581,6.05479691832775,13.7029071584501,6.43570621998856,13.1578461099657,9.26717356765724 18 | 2.04505614384762,0.707688317907428,1.2280144169483,0.604549121364279,3.75649107400237,9.1872153166726,10.1753393854639,6.40903542288763,12.3677493125778,7.35785351425925 19 | 2.80484961533276,-0.410890936798779,2.63516077716422,-0.871275078229035,2.5059737273813,7.17689385971778,13.3514805190147,10.7759869367066,10.5570327857518,3.79785590973297 20 | 3.92552126209408,-0.446045179922245,3.37453378068909,0.725341646372726,2.26668437138653,5.74477338805423,11.0820598433408,6.86948652656173,10.8959880831891,6.93680907592792 21 | 3.48297852483661,-1.04115630166875,1.7659220733289,-0.0307319801279123,2.99897960601221,6.84667522364064,13.6971206563939,7.29395585346975,13.5369515992861,7.98643479073066 22 | 2.40368936327979,-0.329224716154013,3.47489342195909,-0.117222374314147,3.80582569677651,6.22829473403139,10.2657324591728,6.71604653071367,12.8288764274866,7.26966626577343 23 | 0.81471316183047,-0.282821624132353,4.23305745705665,0.0982229385844333,4.10930938187503,8.4932202650938,10.3474072300297,9.70269341506071,10.9554497694006,5.58496985064687 24 | 2.32513406212488,0.432429125495353,2.40687134320151,-0.676857221844529,2.69032051784047,8.29129997196125,12.8667710902086,9.88477514040933,12.5879222811878,7.5179474790057 25 | 0.880938808089826,-0.307607095063416,3.75226243932752,-0.0492463549281357,2.09142175187253,7.25568241291797,12.2713104886909,4.41291626113568,13.4491907893836,8.84397739132739 26 | 1.7348019784691,-0.0566363078893176,3.61204550688678,-0.560719759935025,3.26302612768668,6.92976720542869,9.0181156704844,9.01854140773218,13.5776790827396,9.8538079130926 27 | 2.6263384448453,0.733515420200271,2.77134263472561,2.39915486524696,2.97908219242358,6.64683605142396,14.0408927797557,5.48496541022319,12.9504027868869,6.94652693460074 28 | 2.31244456961208,0.0973116196651844,2.29516065010062,-0.0898721015980383,2.78765749235222,6.41074868993216,10.2386994906767,4.858279241834,13.1860996290608,4.83728456328541 29 | 2.12784117328231,1.63089173658816,4.22951604720639,-0.821648417785606,1.96889608022503,9.44518983043148,10.5931358047789,9.54489439321411,11.822115069545,9.45820216984235 30 | 2.89823899377518,0.560610699002205,4.51851512986719,-0.286961761597124,3.13951073969625,6.25264500875221,13.9024710696962,7.24938112462953,14.9640080874455,10.4645543613422 31 | 2.74621946989754,1.32956476445216,4.34509418039941,0.131278873970765,3.16621798461647,5.25044354637818,14.046007831688,6.43772873843507,11.7886535304998,7.17728143196928 32 | 1.14625954552086,-0.278823495634461,2.87630847106957,-1.06208395043809,2.01534035094373,10.1217219033416,9.23667378944799,7.92591449794496,13.6413779909355,3.2853346006783 33 | 2.92205393392463,-1.26673154414092,3.93390245355002,0.733581035729101,3.02263407077062,8.47734860316352,11.1527737681124,7.18718465961083,10.5590799285408,6.50688493753805 34 | 3.96856634052454,-0.249148394040265,3.01949684562022,0.322692198478851,3.78533003266096,9.07405386785152,9.51004634420688,7.56977910554175,14.5351183604844,7.09411141544518 35 | 3.18492595999031,0.017988414568801,3.83085210974297,-0.348979771348709,1.8208104757492,5.84333222802303,13.8788547509375,5.66179013584674,10.7723561036321,9.54483707791648 36 | 1.62005642166242,0.377072727110327,2.69550533200393,1.28352566424672,4.91079077756379,5.0619183820854,9.55140009582455,8.2381730048292,11.7456164988096,5.50756132886705 37 | 1.56448563763963,0.796008557527438,2.94071872289641,-1.01448804888177,2.46199850508042,5.77846750534065,13.5804949042793,10.8446917935207,11.7456030385444,7.5722219385078 38 | 3.36208722860661,-0.840677417098569,4.00739669737882,-0.510064274107065,3.88427642902339,7.39786344182365,7.84694145827081,11.7870624347628,9.13894226338789,7.80959889479773 39 | 1.24091324624029,-2.20547175082621,1.62220491397708,-0.167169746155345,3.14730963670884,9.7432965277929,9.32841986319924,8.99765759811027,11.7831003733489,7.22453466743898 40 | 2.67545599042767,-1.12805599106249,1.9957972730307,-0.302667785606156,1.99157263714311,8.10561324639787,12.3845087078546,7.40064590333732,12.5898072835667,11.6364424717533 41 | 2.34843701145534,-1.34130995880245,2.97671969508496,2.0264704724602,3.83818899220759,8.5971349684161,12.008184952226,5.17665229729219,10.9534148217219,8.38609036417733 42 | 4.08655139944051,1.60511404288097,4.54674727378832,0.900905638410046,1.83176086259521,9.79270182893523,13.1126549294769,7.9839992892871,12.4499126153624,7.84057139347958 43 | 2.23745511996871,0.744423728488543,2.24063674897868,0.135429040455839,3.45431813292,8.61367010204177,7.97159640102119,7.87820227823109,10.0625573946763,8.91068065855093 44 | 2.17133746499891,0.862082205953383,4.0067926574554,-0.201288954199376,3.21257286750447,7.71583474467784,13.652431995677,8.53690847732191,12.4886481229693,10.5946386436022 45 | 3.83447390308845,0.395155815476204,3.27421491634829,-0.806836740993142,2.44691365257044,6.04954626576838,13.8270306707988,9.27507472081434,12.6472746577757,4.3213689058431 46 | 2.03234801324009,0.509118696330326,2.91961763543992,0.644124123759206,3.02343002517967,9.1352372255697,14.2515308688265,5.20184418429105,11.1230771940685,8.74566423601903 47 | 2.97118466452414,-0.122550125830633,2.09644750467318,-0.336659120653609,3.64817889039139,7.38264910570714,10.2211452252342,9.48534172401338,13.6132131092632,8.48510695615189 48 | 3.23252515257539,0.092584647178277,3.47314510297196,-1.73170351124497,1.25720526479614,6.5719457591319,9.86720684186242,8.72883117686374,14.0602418631003,5.92141750327826 49 | 2.69879131849582,-0.35787991433363,4.38848897790595,0.261920662608966,3.59516266091186,7.1634172613705,11.2690872207204,10.2003787008966,12.7692692654264,6.94694042128468 50 | 2.32238541685067,-0.359655224347011,3.03474609376417,-0.916697598109318,2.12147098701681,8.45647862540406,12.884970846849,5.07171161620144,11.4359841706277,8.37378697453595 51 | 3.65522763623522,1.02857072088428,1.70741497192075,-0.568564475149115,3.64443757129211,7.01342674004228,11.3186393057309,7.47633756399596,14.2503481398022,7.46067024083607 52 | 2.59936245296826,1.07789259245098,4.16396750490781,-0.865820441753572,2.3709014973334,9.25806245719918,11.6841418139419,8.68889328412172,11.7981560931333,6.64300822831268 53 | 2.66544343492665,0.931781215232504,1.47661540293161,0.825493911798051,2.90179981031616,7.51172627107402,13.4055961311518,5.80953881893168,12.8151814094008,4.73238496024204 54 | 4.36795395319196,-1.4607938669776,0.481664946387767,-0.800132284207327,3.68421769916776,4.80622568051252,11.1194626505413,8.34902120454905,13.0565443339824,3.07103373125419 55 | 5.13776710365012,-0.906075576249343,2.29247076943363,0.975830086784378,1.61531396900959,6.41918219035212,12.7448789840652,7.09546690354902,8.92209210755751,7.23769318344819 56 | 3.50581926452903,-0.680347831392415,2.71256711729089,2.70007553074187,4.55494558598051,6.00045517660751,11.6096973349087,7.54962690752821,9.65518013471232,6.79432687957656 57 | 3.78634238423916,1.06316603678904,2.56469168852309,-0.135389596885701,2.18565865811543,6.8127332613821,14.073339594791,8.32850083898565,13.4624691988972,5.70375414991911 58 | 2.09778805582136,-0.692413152126154,2.6569212292567,-0.106031163959467,2.15636891410743,6.55740701871384,12.8503829612479,5.78851282237674,15.7334981199508,7.11122131287829 59 | 3.53289699232833,-1.1336282652909,2.9606891159901,-1.34406380514536,4.02492742587361,7.91686186630315,12.1122676914186,9.28123338139357,11.5141643914173,6.84263004652608 60 | 2.35410574645079,-1.09461543379722,3.88696598772379,0.443121828612795,3.11570337639616,9.95338632785399,10.6346887196963,7.94313895949847,12.3416848513285,6.43880483944168 61 | 3.29098748842977,-1.01290361883673,2.64005159918411,0.961117741663397,3.2414940398728,5.15257572810684,10.5641202316741,6.31270177279225,12.2516963669881,7.75228733692632 62 | 1.76240553112278,0.410277325885889,2.27104572936861,-0.241435816655688,1.60918385831315,6.77711146734894,10.0823926398548,5.49180309737188,13.1151164705678,4.68732029530813 63 | 2.54382372488219,0.477740441926791,4.75174561927189,0.0829548911082895,4.52606084457092,10.5141646743919,12.010521046909,10.2885132935736,10.7321607050151,7.37062020715019 64 | 2.16967734527527,-2.32987954506654,2.91396140976789,-0.361724757768244,3.0465162367214,7.80056474131604,11.7287757185064,9.43971586017244,12.2241372740561,9.01356048395733 65 | 3.34011564367426,0.0162554651767423,4.10636301587614,0.333860199801229,4.23998235965392,7.16811575435265,10.290789120766,7.41219464383311,13.843912851478,6.47295134334441 66 | 4.06637639568217,0.980353523146482,2.98082216276043,0.26560942097052,3.30857263546279,5.91790675158724,13.1157258664469,9.00457170153565,12.811057527078,6.38090199311719 67 | 4.2161258380798,0.806348264817357,1.50653982154097,3.01290574660275,3.78562760450207,7.17369312529077,12.9366625105063,9.48278519952531,13.2221264284839,8.6834131536213 68 | 3.73569065763305,0.119600888891297,5.70063662306933,-0.344730137687429,4.11747870473614,9.79609907504134,9.87265041588671,10.163746261143,12.8010083435304,7.10052377608833 69 | 2.51879138268442,-2.43661560431758,2.25630557775656,1.06255745328506,2.99501772765561,7.85512210799067,13.3997117900371,8.92255363200004,11.8532957708771,5.39345612920982 70 | 3.56274476285812,0.0310224761223877,4.06519498093684,-0.729685074040496,2.82158178980981,9.26121274804556,13.7259710727752,6.39954288545845,12.4704392906607,7.91473746475781 71 | 1.7536802881108,-0.335746697999837,2.96791826833504,0.056547720976407,3.69622906894708,6.76285657721419,12.7050438199728,7.68031383075413,8.09909657894607,8.89091154706175 72 | 3.38092221262568,-0.263776161923756,2.46004031066081,0.467829711920054,3.52179720849355,5.94290668251573,13.3194468591637,7.47469319329617,12.9274828344618,9.00960644171894 73 | 1.56957274720331,-0.355852460880201,1.07435643244612,-0.372645250551315,2.55217340975654,9.02890657067722,10.615129531765,9.62478763775545,11.8352313802529,7.77473749620613 74 | 1.9515544951214,-0.48115479829018,4.00783137615909,0.21016168769331,3.40540060404469,5.09037204188718,12.3770690167941,6.67434505115961,15.2268281261638,9.55079650857386 75 | 2.78149644946541,0.222735467817872,2.72076520323324,1.28960830567623,2.26303557533059,7.04949143661843,10.1193888433008,4.40207216267894,13.6790683940711,6.95340810234847 76 | 1.51006376326446,2.42991031869739,1.7504341447154,-1.62683954986708,3.25574801441047,7.9052496806067,11.7669110524123,3.90426940114649,12.0283043552136,7.17342841628107 77 | 4.17270628121431,1.49629811873421,4.24849112786506,-0.783536509683378,2.71506649506643,9.53210617822102,12.7759814102375,6.28686694511153,9.66977672551115,6.5301405028163 78 | 1.52017297842834,-0.717208202759088,2.80778662343413,0.510913103456947,2.96887867500666,5.17376092776373,12.6345964324963,9.01685361290208,9.45188483746378,6.40076631205042 79 | 2.56961218392278,-0.46705390018763,3.91197623108744,1.21762605593289,2.51414111303988,9.18924925798425,13.7382877716141,5.40414997052001,14.3191219073847,5.48195775903601 80 | 1.94836135795655,0.662923452859406,0.841730319973743,0.305109247688339,2.86491191584415,7.66977244495011,14.8619160645725,10.127463443932,13.1832719301855,6.74503289629792 81 | 4.5225863440541,2.3001768909466,4.28961053233286,0.210293633324938,1.51928127716249,6.16945268404355,12.1834748264662,7.7553768434329,13.1801889644105,9.90101046108271 82 | 3.59282805458608,0.327510141216588,2.57189003444617,0.468865965505879,4.2563691256856,6.55371182293622,13.4281674202641,6.3509357312125,10.7471807232563,7.27958654098329 83 | 2.77733849098073,0.0638634816169111,2.74499710702705,-1.90434257226243,3.20904164990957,8.07166117737661,14.0718622560122,9.20107934999315,13.8318452056737,8.70017175539339 84 | 3.71289427624846,-1.13956093996767,4.32715823880373,1.23840228464858,3.31315678959013,10.6701183034191,10.9672126174295,8.249268732502,11.1907970938052,6.82699320304546 85 | 3.71660083374105,1.18041020362035,3.83869012061002,0.390998891457648,4.07514823866383,8.1720847886618,11.9929116525007,7.07937251199659,13.9582439434929,7.7151516230159 86 | 3.44024186438414,0.0413884770331462,1.53499932282681,-0.0311989167468595,2.71337482542958,3.0799251829997,13.0603599803371,7.61816177813112,13.4538433089794,5.21885621857179 87 | 3.1588306213181,-1.2135935633847,2.3837473021273,-1.61880052176578,2.9680225725685,7.38413084360458,12.9572170862126,10.2617429564816,13.4006516436341,9.68610080599741 88 | 3.65976413833195,0.0731957766278129,3.10610583940642,0.451266278132817,2.25726660336119,4.11587982448954,9.31879001481957,7.49679750526224,11.474555922575,5.13275459638981 89 | 5.22051966293556,-0.257324594345541,1.15598324227285,-0.690843363623055,5.0852201921514,4.44521212242059,11.6918421565553,8.75801306880881,14.2488068843756,5.541197393065 90 | 1.8160549259346,0.26680641094213,1.38357714628431,1.40544562193991,3.85489041041,5.12419272820377,12.2359204505236,10.2732270503501,12.7094581054975,10.3695274588295 91 | 2.92604416550251,1.38772432571642,3.2276767041363,-1.70134268782504,4.00044599614444,6.68627766319763,13.16714093172,6.43233973022998,9.59961723696961,5.04788109445281 92 | 2.58364532511348,0.193079635023937,3.92121670193883,0.192245597277143,1.81588208991468,8.16467415598055,12.6686892119566,8.6691711383364,10.6034478765587,6.71989701758322 93 | 2.80851765624737,0.592316622600428,2.668796560501,-0.702956761497232,1.45889744487622,6.96029189563324,11.898674713215,6.20899330174612,11.7451897106064,5.69851672567517 94 | 3.0695447814074,-0.829974526915779,4.28723403098892,-0.8085558633488,2.78826699923918,7.10803756330504,9.7792541897763,4.44205790325453,10.6481223799097,8.00618455006071 95 | 4.15534831801052,0.39257328164249,3.05726080245101,-0.638198741525393,3.17035256996267,7.93164285309756,11.6887066297029,4.94670547150422,15.9487413483546,5.8708003651199 96 | 3.59495734695049,0.384867610680806,0.796066874833077,1.73734542796118,2.30922262556707,5.65825147827943,11.5204010687813,9.73248499558371,13.5886014395756,5.00368276904075 97 | 1.58035489164301,1.05104466572565,2.47708965173444,-2.26047826314218,4.69772073903203,3.52950446505418,13.9336301944419,7.2657049845206,11.5769792080678,8.39714583929708 98 | 1.39332275464196,1.15579748458375,4.0807853101205,-1.61247209388646,2.92731003739413,5.23186151595147,12.1091745873828,8.09208378102727,12.5104316097292,7.08332687923888 99 | 3.89292589956318,-1.03443795894203,2.75685866238435,0.65374019880081,2.28239114371071,8.45807496248676,11.9920120043986,10.5299094230256,13.559824055617,8.1122075810173 100 | 3.14816795518773,-0.254468065804272,2.0905534377469,0.902880728593014,2.58348556169214,8.72511284702768,13.2281871529368,6.51878076259117,9.41051261788521,7.58990529451832 101 | 4.22702839010139,1.27368426608477,2.13170049262401,0.754608487834512,5.19358676105577,6.51620008848514,13.4286976478865,8.19597592420718,10.4970682192501,7.49463519191375 102 | 2.23819566082197,1.50254463898139,3.8694750449077,-0.226810768770074,3.54234782578152,5.00289459543232,9.09135540044636,7.93368657788568,11.7107729585053,8.30424383195745 103 | 3.41937540588991,0.590409467146639,2.3199904021862,-0.69596862759163,4.0236676089818,7.12374870901547,13.3182706017714,7.57000754328709,10.5893512682144,5.18839323184071 104 | 1.96005663536765,-0.63068545138345,3.17321453870851,0.506647357333803,3.81282610871047,7.93958566717212,11.3963257978038,7.04230232629783,13.4788513192766,10.4308468198155 105 | 3.71157396599263,0.792349537294271,2.84056196092451,0.616234293607611,4.30102707741098,6.00226758140106,10.6764195528979,6.31740694325405,12.3969180649697,8.67606270287958 106 | 2.36678698503217,0.12538457012308,3.79349943264328,-1.66687927451536,3.56795641094836,9.47862683715037,12.9186115095238,7.57527718867848,11.0101594654187,8.43657318720818 107 | 3.56317466445015,0.32275497124803,4.69435049142761,-0.336254225742653,2.87992855773695,7.66151343617189,13.6963075611804,6.90299494538787,9.83554031028443,4.41088430294965 108 | 3.6609866858316,-0.445583209452593,4.23996868635003,0.507179856746571,2.50799729374129,10.0946803171499,9.5450137716298,6.8447431700406,13.180624614425,7.28680631516201 109 | 1.34194914267455,0.766843934461376,3.02943674314777,-1.69739070150845,3.20077197539334,8.60410032790845,13.6253943845222,7.29969519297923,8.11965595762613,7.15166678680725 110 | 4.02816797701792,-1.40350302182772,4.65721449904792,-0.206927223902732,3.16479929880001,11.2435017420371,11.8717087770237,7.40275662080126,12.722116571448,5.90983404602385 111 | 4.12795361401458,-1.17604677961141,4.13122314018687,-0.398597028176164,3.69365769622557,8.26919763219384,12.8446879842151,8.39982276081341,12.0178883501083,9.01197825772897 112 | 1.71984539657782,0.511596469184802,1.59758936787416,-1.18861982748089,3.13332425333661,4.71343681751721,12.5587288401124,6.3007595522222,11.7287616832776,7.66333510865806 113 | 4.12886822740957,1.31676526377829,3.15677046111293,0.452565404139086,2.26053684136928,8.59107235032511,13.7894874376899,4.83519622723884,9.5108791092322,9.08805368098282 114 | 2.53586547283502,2.39291294709557,3.86750859643138,0.207589703929424,2.60248118475222,8.02124561746278,13.1022528758413,5.17652200249208,13.3913221407469,7.90318600161125 115 | 2.68423979046863,-0.0677694084923992,2.96676484289826,-1.21012237611841,3.25715863936297,8.76369819599304,10.4887152701368,9.58613114107759,11.5243303645817,6.9305910816244 116 | 3.92429314683495,-0.203785066452724,3.95406455632601,0.767415054280641,2.71982608085582,7.45116991121428,13.3310889575676,9.25367517225466,15.3278281972639,8.43250333009703 117 | 3.07714472398578,1.99643445216969,4.13009672517014,-0.599027691256964,4.00796759211016,5.82520961612509,12.6281203437743,5.54096289066144,12.909906863866,6.51730487177749 118 | 4.03992360511188,1.54321779434101,3.39369731208299,-1.56097277280565,4.15521962402319,5.99660795201357,12.4470974596548,9.83529119783489,11.2649858890469,8.77370135231602 119 | 3.74188620673818,-1.22833762626182,3.3768083279671,-0.995664042928941,2.79720505086659,5.88011494876267,12.1622525979099,8.96271292424988,11.9981325616176,8.86922941469515 120 | 4.25554485828951,2.64448799222478,2.74825709295136,1.09325100838442,2.67859045291761,7.36938386960235,12.0600981637877,5.73618694177614,9.28815924907152,4.92541332959288 121 | 3.95091896645618,0.757013708274368,3.50409489082678,0.917142886007373,1.37894608293949,5.51240158160712,14.2072910070582,7.43658533145663,11.4824299197983,8.9368603426096 122 | 2.51863439272671,1.05534519215293,1.6701965286517,-0.773049355132352,3.56810525899318,7.07856220738697,9.36627832824869,7.36895553497146,13.9500900059493,7.24716276348636 123 | 3.20288177796984,0.957944895305812,2.88170054748523,-0.475835817645799,3.67128024717795,5.91435138572997,11.8610791788362,11.2892429366964,14.1084803362307,9.55567976193024 124 | 2.96826025616227,0.833879057642652,2.36675153339115,-1.13070318401985,4.46025952177705,10.3488650194114,13.8567139056562,6.98647028181296,11.1535868936394,7.78178290110445 125 | 1.80441969966543,-0.169275570018636,3.67769134471695,-0.428835431666788,1.36228062571378,9.78154810227982,12.6305926083557,6.13975395132835,12.4755196827184,9.76845921296469 126 | 3.62368123684843,-0.404433643078437,2.7021182352257,-0.839583649863663,4.29850108220997,7.91451350709753,12.2477726136131,5.91938459381628,12.6988089633323,7.92083931851209 127 | 2.08519551633309,0.295601573702913,3.6279222471273,-0.600986672903937,2.84630017120586,5.69572648359634,10.7582417856347,5.08675941353879,11.4473262690759,9.54088352510635 128 | 3.2487580077081,-0.231874507234865,4.67843072799152,-0.773890778190773,4.57914884558458,6.29101181446574,12.3086989791959,7.84149598474994,11.2633498253877,6.20030558153709 129 | 1.93737720681962,-0.448078231626391,0.599997459370709,-0.892492351173958,5.05523518552608,6.70390693851857,10.50156919875,6.85915405127585,13.0360716630677,6.55870487952943 130 | 2.63601775280424,0.0223107965723932,1.6501571149286,-0.0357318470577211,4.28448665087213,8.60815404097791,12.0427624525694,8.00389794899356,12.5208013743155,7.50917565833253 131 | 1.79300514662173,0.0444672972361275,4.20200789578675,-0.304178102393507,3.41740331727997,11.4936078854559,13.0828340316842,9.75103136491832,12.1799051059203,6.60359854991543 132 | 4.42921278138977,0.480721165290636,4.29384271937366,-3.01216378355869,4.15985360728644,6.60129934950357,12.3052632307464,6.87395699340645,9.98738313137197,9.80764204523413 133 | 3.63343589098283,0.635619855169574,2.94040565381755,-2.00852305776173,3.49159025571845,6.81169254233316,11.6259193500865,8.40261936508277,12.2313504463708,5.60605018683022 134 | 1.00318438234358,-2.0007423930765,2.31493765482768,-0.0463714094449894,4.10200871771494,5.18078720083815,15.1071055062803,6.76758564890448,11.0788402501962,7.43702994354545 135 | 2.31816782690379,-0.690655503762959,1.54656069560844,-0.359537064500413,3.56857043028032,8.95055224238743,10.5258465134907,8.64686727690587,10.4102469142639,6.24432360237448 136 | 2.5399445206893,-0.130645189306517,4.29412656120527,-0.195021661747648,2.63608291488318,3.90369684336933,12.3639936507329,6.09702993804992,13.2093630297233,7.95477175646506 137 | 2.01693080585224,-0.344251783023643,4.176169089352,-1.51447013546087,3.13695637076155,9.54387059225663,12.0050569464601,8.29614713277723,11.0657530094161,6.11543164145999 138 | 3.49533171288834,-0.0524128239651282,3.86404507817599,-0.892033047120622,3.72427542710531,7.82937878692625,9.67329603444304,7.57139298214452,11.7193340140978,7.66544513169459 139 | 3.72581750023253,0.096123920549124,0.703267476539324,-0.862706491414068,4.53998160594704,6.52082004538635,13.8340363150992,7.50137582379087,10.8274250250642,7.77439956378594 140 | 3.66729873189292,0.266407053232127,1.60818998806775,0.551954147954621,3.98699233471472,7.92732565679819,11.4385084290507,5.90179322153184,7.15575329170318,10.6871213684089 141 | 3.95478643646659,0.554793475278744,1.61412325497843,-0.18823391331742,2.0539780020313,7.79369364134848,12.0480134056411,4.96476868570307,11.5707335849387,5.90534375246916 142 | 1.32466782070806,1.23544587854851,1.92695315608454,-0.325146743629547,2.38731931577903,6.04579608081771,11.6536342540556,10.4387848706922,10.6768045735046,7.63179249396484 143 | 1.79481460750809,0.285133381306893,2.05857449680366,-0.536938449139604,4.2868865903477,5.76874147304252,11.5830361372556,8.28580272048599,10.8639317594225,9.65012871610636 144 | 1.03674751077947,-0.458792927772878,2.41356569027782,-0.371477412749352,2.44334984007008,7.44126547912954,9.99069705777535,7.21086783492302,11.5128521348843,6.97354996242065 145 | 4.47075230981397,0.623478021862369,4.03472122005478,0.722807023659037,2.97702227783927,6.988895272103,10.7480958406308,8.93660796514043,11.8254576747037,8.62284546709514 146 | 3.3724723385506,-0.723995091640382,3.45896220669478,1.20569188339316,4.32474112266228,9.10160701970891,12.4969002665317,7.75649674771693,11.5494682544522,7.90193650231252 147 | 4.06587933403768,1.61941870348572,1.806554928002,1.18415312566266,3.26389766901551,7.29244822375879,10.7080305822444,4.87408219728216,11.3935033898916,11.0808725181612 148 | 3.53064986835732,-0.616673410216944,3.7918892716716,0.671190435407443,3.84133901907884,8.78746154957072,12.6623936915627,9.19131973502393,11.2814659892703,7.12442625072204 149 | 3.10198344588413,0.384541057446883,1.22769508920048,1.17550207341849,2.73586251098144,9.18101273931198,13.0961555868591,4.74225488417286,11.2948871482752,6.63031362411803 150 | 4.33778246578648,2.11452131280045,2.41227757800091,0.188526472721212,3.88676929190719,6.18436456137033,14.0674849567394,6.69143947148235,12.2375590395996,10.4166548735516 151 | 3.08723476849113,-1.02856736628788,1.71762968744589,0.49906359684503,3.22548575569027,6.41735582496443,12.7218571186833,6.5896816707006,11.9138805817391,6.22192229627582 152 | 2.60889579259955,-0.886787965667858,2.68786533901992,-1.94761831141199,3.54483272466109,8.25436160177427,11.1474149713411,9.30129753073625,11.1111658172144,7.14697738650465 153 | 2.75013251540444,1.27114600133104,2.30122469937528,0.420737246176464,4.17625072675557,5.43875304382528,11.4128286921119,7.05214470786822,11.5377548554587,6.41629125580402 154 | 4.15510474589615,-1.60508544127984,0.356150232290993,1.17347624977911,2.68642475814732,7.55520005232682,12.9643293204368,5.81379825100888,12.2568178558622,5.24755507011308 155 | 2.135272760169,1.12227338293318,3.8250223767992,-1.44623155699525,3.50537668851137,8.37930136970424,12.4143091954332,6.45554937268064,11.2584016338501,7.89050520929826 156 | 2.13332165763168,2.15843864176903,3.30692631321041,-0.366751205948309,4.53970129858793,8.5259152658989,11.6128004232877,9.42218961885374,12.1595337902221,8.62054969933886 157 | 0.678982969652143,0.428246613022636,2.94196054182908,-0.607796234877856,4.49100271069654,6.64686580636504,14.1987447496369,9.79477209926309,11.5153831894795,7.60070541588671 158 | 3.60883016891785,1.2011786904597,3.92656941083441,0.319871377380778,3.24368948466768,7.90553346362987,9.04008615593867,5.32477480096043,11.8432519981462,5.92889630081135 159 | 4.15000604820099,1.03169011615184,2.7813081394262,1.09984767220601,2.58468567364775,6.68709450418432,13.3801648147616,9.01673709388798,12.4972486404739,7.84440355609551 160 | 1.80040232826314,0.65387425271026,3.99313349742004,-0.406036627590866,0.964827237781824,8.10391452604572,11.7763929752163,6.00556060134901,12.0880218100014,7.63945830737134 161 | 1.41999924545012,2.01208177317955,1.07666565751963,2.41074642682032,2.35664210216896,7.22011511605325,13.8560812216389,5.7732133678033,12.9700033342638,9.12173258115265 162 | 3.65316619364219,1.09462984183357,1.94728481552733,1.15889988480835,3.0538214810293,8.46412614391324,12.7910708040365,9.21266465446325,14.1428318599804,9.77894289055411 163 | 2.45059151487703,0.726409354081993,3.89039776192599,0.33838959155623,2.61043811609479,8.50721243478098,14.2357391903676,7.38807222633488,8.6217921992674,8.68410274609858 164 | 3.52105452531253,-1.39965713658319,6.54114027762577,0.456177343920897,4.8778255533649,6.89844046466994,11.4113529349236,5.77960030513245,13.5866371334767,7.70352024095459 165 | 2.3005969335905,-1.2877951632908,3.74151074268788,-0.314044666415584,1.54239327325613,10.7475387220203,12.281946865467,9.18226481177154,12.2239716391688,7.73561101592764 166 | 2.5610906852078,-0.968415477525956,2.79252874590078,1.40434402019996,3.72385537837696,5.40705971118021,11.9830898883525,5.33530981917701,10.5575915891767,8.32155228187019 167 | 2.32268070388331,-1.7366857577184,3.17993845419171,-0.410269949084875,1.78192373846086,7.24394981973049,10.1311111462272,9.49049106478846,10.289831784033,7.65961189493699 168 | 3.95914119477535,0.831697358073543,4.42636484940947,-0.088102516150087,2.69424113011163,7.24584835503672,13.4159523911857,9.30535554574127,12.7233401833905,5.10728118061684 169 | 1.5318266705376,-0.758953108565061,2.55683321051738,0.324875594716314,3.02439771662207,7.45052492210994,12.689122992117,7.26187175807689,10.3237787230796,7.43125283216429 170 | 3.18376389266813,2.15507700106696,3.36718998665182,1.18509980019251,3.35226610118768,11.0827796550087,9.52015654431131,8.19895773819516,11.3845067354497,6.99139225523979 171 | 1.5648528173482,-0.423258880235017,3.69582169611066,-0.255508573440884,3.82378975551104,6.01498210138004,10.839884594165,6.02445472354456,11.3400498399973,7.16279810981563 172 | 1.86260010187653,-0.00564993667529739,3.93344472861178,0.884447920895625,3.59778656109952,7.34841874224212,10.9653727481982,7.70236621929599,13.6986888069098,6.50944753687648 173 | 2.585354673122,0.400361728611523,4.49188908255981,-0.488561069626473,4.29563746444499,4.62877638337251,14.5077492948639,7.2786818564112,10.7199861121942,8.94023595640859 174 | 3.14393428815136,-0.365677802043687,4.1595229902333,-0.727376173177559,2.42193382083892,6.67668844225875,11.8559251956553,7.89920914158411,13.2406054816354,5.87994837861325 175 | 4.06202433069828,-0.429143835225301,3.22679595690912,-1.1882552729167,3.17297498046565,8.57084237055162,10.8059933807219,6.80078149623176,12.7797958977168,7.31045865998022 176 | 2.4292060972646,0.244697126797303,1.70453481525982,-0.537942415912577,2.41880375399224,10.3117331020776,11.6628843475153,5.59351731627539,11.8734671031829,5.79751690908917 177 | 4.27718137641215,2.21977624873515,2.93722130830809,0.119351217316955,2.21740940366516,8.69198625556229,10.1097409656787,9.12673262907644,14.0834617274231,6.50482295961652 178 | 3.22828932054008,0.314269807914075,3.99328293687371,1.31817809545599,1.33317079573363,7.35792948215109,9.8603965271506,10.0676451688594,10.7571384258115,8.23860737128291 179 | 2.69118693549726,-0.563785074516318,1.99406906004529,1.44308055765009,2.28652675169627,5.47941588018362,8.01954429464658,9.14558324708038,13.9328654722052,8.8614523334873 180 | 3.95982913059558,-1.48569615831883,2.2490150524019,0.249026211645244,2.49332417219877,7.8396367732451,10.4420109554284,5.66951634377493,11.3839441691907,7.81544663613945 181 | 3.54882237481208,0.415298593093812,4.74401638235168,0.815702326850602,2.30481605588514,7.53616394706755,12.8697365235413,8.39795753731085,14.967001263555,5.53880102536079 182 | 3.42551309377198,-0.528063682678589,1.81239990854262,-0.734607806377859,3.54160781116491,4.79294817461401,15.3738954149511,4.49299162938191,10.6104890202935,6.55926385078144 183 | 3.64350003512817,-0.350470617450974,2.58302958228032,0.258153278022185,2.41892270363792,8.61089473578694,11.4790485384348,7.06642110660287,13.0231941173422,6.95462726269856 184 | 1.63969385649649,0.730621937296222,1.43017984063559,-0.852860768822711,3.43083205102061,6.95133390951931,12.5383819925984,7.09353528608659,11.6863967477844,6.4039003406842 185 | 2.80149389359681,-0.556624165079817,2.71952596474664,0.215648758553466,3.67170118958658,8.90706562521072,11.1564882996562,9.71670983764161,12.4578226137033,7.73512235531229 186 | 3.61930267687538,-1.44976053616599,2.41467303611458,-0.709210212002397,2.56649269449632,7.1030380376241,10.5599245315056,6.1935888676206,12.027054834897,8.57291985692858 187 | 5.06820960503683,-1.05199642811248,3.66031919756334,0.710865523158937,2.21901657110547,7.25045625356292,12.5138048853418,7.754992401902,11.7959788158585,6.37564577593959 188 | 2.69471524580275,-0.682838618785053,2.36248745101543,-0.447371858762072,3.41629818237612,7.49417623443956,10.4302679591657,8.97438861918195,11.1639461173139,5.56437891744087 189 | 3.28124561222986,-0.612941178575923,2.46097836678872,-0.338298304193498,2.17837940036804,8.15275513503092,12.8300026218661,7.78393839451521,11.6420035513032,9.25152213037092 190 | 3.6913173367059,1.89078003505454,3.9515202335543,0.517233422140697,3.21107589035911,6.56996938342518,11.5792219544883,9.83798116485476,12.5625469721155,7.29286420658224 191 | 3.04636143814332,-0.145410533594199,2.89442015415272,0.0987035943240238,3.75482316974118,7.13135631211298,11.6189981549043,7.88792668908804,11.5409998633959,7.12938678548111 192 | 3.11302936162596,-0.639423720588603,2.62593072723081,0.74368561583684,4.78010975268365,7.33258868182454,13.486874017349,9.79538123170815,12.2026252613809,9.04974888233191 193 | 3.99533187428913,-0.411176045221688,2.82376741917609,-0.27855014148461,3.66594768190446,8.28131619354068,11.0094696391757,9.1773246812955,12.4780145898845,6.38771551319335 194 | 2.31884863867835,0.342791878432067,3.27648330792093,0.539071680617946,2.98926618045146,7.11273651105296,13.0399373380583,5.51192639468147,12.3986646721863,7.1601635789609 195 | 1.72294275331231,-1.13324675462843,3.23633259266121,-0.0883394696700483,1.79894539357142,7.39331764430268,14.2909260812794,7.70435539865712,12.4520723504261,5.3706791759967 196 | 1.53130225016892,0.0660703025191778,5.06167415943736,-0.745757508153261,2.35660221551097,9.2242010956652,12.3554348845562,5.30909432599958,13.8026247347823,6.44896060805481 197 | 2.68652593346413,0.037999765963958,1.70994001416473,-1.56007845110139,3.79729231047799,3.85489067070165,13.7608152642487,6.98071279486954,9.09342838786233,6.99067633571191 198 | 1.29634050733669,0.921064747544448,3.57118944747409,1.08295652542379,3.5282482743811,4.93432798322283,8.27077702451518,9.08567652742522,13.1051469692962,7.70764039920648 199 | 1.64948534386229,2.13496563845849,4.78539022670878,-2.34105600256368,3.2836697388067,7.88778359852503,14.2461370443166,6.95096642569375,13.2237839374135,9.0643123276077 200 | 1.89790632279084,-0.599826931556193,3.21294024309211,0.206305266032027,2.98786069510366,8.24703193777819,13.4607313005268,8.94212107393961,11.89061620582,3.79694519861531 201 | 1.90045698547065,1.24444645863646,3.88469214605068,0.764742571181021,2.80808622252268,7.42590586197344,11.2738267697307,6.12533203062664,10.186381409045,9.33265049548614 202 | --------------------------------------------------------------------------------