Compare pheno profiles of clusters


In [1]:
# Imports
import re
import os
import copy
import gzip
import numpy as np
import pandas as pd
import cPickle as cp
import brainbox as bb
import multiprocessing as mp
from scipy import stats as st
from matplotlib import pyplot as plt
import scipy.cluster.hierarchy as clh
from mpl_toolkits.axes_grid1 import make_axes_locatable

Load files and screen for duplicates


In [2]:
# Paths
in_path = '/data1/abide/Out/Remote/all_worked/out/sc12/'
out_path = '/data1/abide/Presentation'
out_name = 'nondupl_linkage.gz'
if not os.path.isdir(out_path):
    try:
        os.makedirs(out_path)
    except OSError as exc: # Python >2.5
        if exc.errno == errno.EEXIST and os.path.isdir(out_path):
            pass
        else: raise

metric = 'stability_maps'
file_dict = bb.fileOps.grab_files(in_path, '.nii.gz', metric)


I will be pulling files from /data1/abide/Out/Remote/all_worked/out/sc12/stability_maps

In [3]:
# Duplicate dropper
def drop_duplicates(in_dict):
    """
    Because python uses pointers and does not copy the variables
    I can operate directly on the dictionary and change it in place
    """
    cp_dict = copy.deepcopy(in_dict)
    subs = cp_dict['sub_name']
    dirs = cp_dict['dir']
    path = cp_dict['path']
    drop = list()
    present = list()
    sub_names = np.array([int64(re.search(r'(?<=\d{2})\d{5}', sub_id).group()) for sub_id in cp_dict['sub_name']])
    for idx, sub in enumerate(sub_names):
        if not sub in present:
            present.append(sub)
        else:
            drop.append(idx)
    print('Found {} items to drop'.format(len(drop)))
    # Pop them in reverse order
    for idx in drop[::-1]:
        subs.pop(idx)
        dirs.pop(idx)
        path.pop(idx)
    
    return cp_dict

In [5]:
# Visualizer
def show_netw(results, network, scale):
    distance, linkage = results[network - 1]
    part = clh.fcluster(linkage, scale, criterion='maxclust')

    # Define covariates of interest
    cov_interest = ['DX_GROUP', 'AGE_AT_SCAN', 'SITE_ID', 'SEX', 'EYE_STATUS_AT_SCAN']

    # Pull up the subjects for one cluster
    f = plt.figure(figsize=(10,5*(scale + 1)))

    for clust in np.arange(1,scale + 1):
        clust_subs = data_subs[part == clust]
        clust_pheno = pheno_data[part == clust]
        ax_cl = f.add_subplot(scale, 2, clust)
        ax_cl.set_xticks([])
        ax_cl.set_title('Cluster {}'.format(clust))

        lt, lb, rt, rb = bb.visuOps.add_four_grid(ax_cl, ticks=True, titles=('age', 'sex', 'dx', 'fiq'))
        lt.hist(clust_pheno['AGE_AT_SCAN'].values)
        lb.hist(clust_pheno['SEX'].values, bins=2)
        rt.hist(clust_pheno['DX_GROUP'].values, bins=2)
        rb.hist(clust_pheno['FIQ'].values)

In [4]:
file_dict = drop_duplicates(file_dict)
data_subs = np.array([int64(re.search(r'(?<=\d{2})\d{5}', sub_id).group()) for sub_id in file_dict['sub_name']])


Found 49 items to drop

In [6]:
# Loop the data
out_file = os.path.join(out_path, out_name)
if os.path.isfile(out_file):
    print('{} does already exist, I will load things'.format(out_file))
    f = gzip.open(out_file, 'rb')
    in_data = cp.load(f)
    data_subs = in_data[0]
    results = in_data[1]
    
else:
    # Remove the duplicates from the file dict
    print('I will run things now')
    file_dict = drop_duplicates(file_dict)

    results = list()
    for network in np.arange(10):
        print('Loading network {}'.format(network))
        data_dict = bb.fileOps.read_files(file_dict, network=network, silence=True)
        # We only loaded one network so it is always the first one
        results.append(bb.dataOps.calc_link(data_dict, metric, network=0))
    # Write the results to disk for future use
    data_subs = np.array([int64(re.search(r'(?<=\d{2})\d{5}', sub_id).group()) for sub_id in file_dict['sub_name']])
    out = (data_subs, results)
    outF = gzip.open(out_file, 'wb')
    cp.dump(out, outF, protocol=2)
    outF.close()


/data1/abide/Presentation/nondupl_linkage.gz does already exist, I will load things

Visualize the data of all networks for this metric


In [7]:
# Visualize the distance matrix and dendrogram
f = plt.figure(figsize=(10,30))
for nw in np.arange(1,11):
    distance, linkage = results[nw-1]
    spl = f.add_subplot(5,2,nw)
    spl.set_xticks([])
    spl.set_yticks([])
    
    subdend = bb.visuOps.add_subplot_axes(spl, [0, 0.71, 1, 0.29])
    submat = bb.visuOps.add_subplot_axes(spl, [0, 0, 1, 0.7])
    D1 = clh.dendrogram(linkage, ax=subdend, color_threshold=500)
    subdend.set_xticks([])

    D2 = submat.matshow(-distance, aspect='auto')
    submat.set_xticks([])
    
    spl.set_title('Network {}'.format(nw))


Load pheno data


In [5]:
# Grab the phenotype data
pheno_path = '/home/surchs/Project/abide/pheno/pheno_full.csv'
pheno = pd.read_csv(pheno_path)
# Get the subject IDs of the pheno files I just read in
pheno_subs = pheno['SUB_ID']
# Find a mask of those pheno subs for which we have brain data
pheno_mask = pheno_subs.isin(data_subs)
# Get the correct pheno data
pheno_data = pheno[pheno_mask]

In [6]:
pheno_data


Out[6]:
SITE_ID SUB_ID DX_GROUP DSM_IV_TR AGE_AT_SCAN SEX HANDEDNESS_CATEGORY HANDEDNESS_SCORES FIQ VIQ ... WISC_IV_BLK_DSN_SCALED WISC_IV_PIC_CON_SCALED WISC_IV_MATRIX_SCALED WISC_IV_DIGIT_SPAN_SCALED WISC_IV_LET_NUM_SCALED WISC_IV_CODING_SCALED WISC_IV_SYM_SCALED EYE_STATUS_AT_SCAN AGE_AT_MPRAGE BMI
0 CALTECH 51456 1 4 55.4000 1 R NaN 126 118 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
1 CALTECH 51457 1 4 22.9000 1 Ambi NaN 107 119 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
2 CALTECH 51458 1 1 39.2000 1 R NaN 93 80 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
3 CALTECH 51459 1 1 22.8000 1 R NaN 106 94 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
4 CALTECH 51460 1 1 34.6000 2 Ambi NaN 133 135 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
5 CALTECH 51461 1 4 37.7000 1 R NaN 99 111 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
6 CALTECH 51462 1 1 20.2000 2 R NaN 107 102 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
7 CALTECH 51463 1 1 20.2000 2 R NaN 102 101 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
8 CALTECH 51464 1 1 20.9000 1 Ambi NaN 101 118 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
9 CALTECH 51465 1 1 20.2000 1 R NaN 96 99 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
10 CALTECH 51466 1 1 27.6000 1 Ambi NaN 106 111 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
11 CALTECH 51467 1 1 23.4000 1 R NaN 93 89 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
12 CALTECH 51468 1 1 20.1000 1 R NaN 100 89 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
13 CALTECH 51469 1 4 45.1000 1 R NaN 104 109 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
14 CALTECH 51470 1 4 29.1000 1 R NaN 124 127 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
15 CALTECH 51471 1 4 22.4000 2 R NaN 125 123 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
16 CALTECH 51472 1 1 17.5000 1 Ambi NaN 125 123 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
17 CALTECH 51473 1 1 21.2000 1 R NaN -9999 -9999 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
18 CALTECH 51474 1 1 20.9000 1 R NaN 100 90 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
19 CALTECH 51475 2 0 44.2000 1 R NaN 104 113 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
20 CALTECH 51476 2 0 39.3000 1 R NaN 123 127 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
21 CALTECH 51477 2 0 42.5000 1 R NaN 97 85 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
22 CALTECH 51478 2 0 19.7000 1 R NaN 116 117 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
23 CALTECH 51479 2 0 20.0000 2 R NaN 111 110 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
24 CALTECH 51480 2 0 20.8000 2 R NaN 112 109 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
25 CALTECH 51481 2 0 27.9000 1 R NaN 117 133 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
26 CALTECH 51482 2 0 21.1000 2 R NaN 103 108 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
27 CALTECH 51483 2 0 20.9000 1 Ambi NaN 124 127 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
28 CALTECH 51484 2 0 23.6000 1 R NaN 128 135 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
29 CALTECH 51485 2 0 23.9000 1 R NaN 123 113 ... NaN NaN NaN NaN NaN NaN NaN 2 NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
1026 USM 50503 1 1 28.6708 1 NaN 53.33 80 91 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1027 USM 50504 1 1 17.6454 1 NaN 66.67 87 86 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1028 USM 50505 1 1 33.1828 1 NaN 60.00 95 86 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1029 USM 50506 1 1 15.9343 1 NaN 60.00 73 77 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1030 USM 50507 1 1 28.1095 1 NaN 40.00 97 108 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1031 USM 50508 1 1 26.3792 1 NaN 100.00 107 96 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1032 USM 50509 1 1 16.7337 1 NaN 100.00 95 85 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1033 USM 50510 1 1 14.1437 1 NaN 66.67 102 104 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1034 USM 50511 1 1 16.3833 1 NaN 100.00 114 109 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1035 USM 50512 1 1 15.4908 1 NaN 73.33 121 108 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1036 USM 50513 1 1 18.4668 1 NaN -93.33 102 97 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1037 USM 50514 1 1 21.4018 1 NaN 100.00 109 99 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1038 USM 50515 1 1 15.8522 1 NaN 73.33 94 71 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1039 USM 50516 1 1 17.1143 1 NaN 80.00 107 108 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1040 USM 50517 1 1 16.5010 1 NaN -100.00 109 108 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1041 USM 50518 1 1 18.6585 1 NaN -93.33 77 83 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1042 USM 50519 1 1 16.3778 1 NaN 80.00 83 78 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1043 USM 50520 1 1 17.6728 1 NaN 86.67 76 55 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1044 USM 50521 1 1 18.4175 1 NaN 100.00 80 69 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1045 USM 50522 1 1 14.7515 1 NaN 100.00 100 93 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1046 USM 50523 1 1 17.3251 1 NaN 100.00 115 118 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1047 USM 50524 1 1 12.3340 1 NaN 93.33 83 76 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1048 USM 50525 1 1 32.8487 1 NaN 80.00 106 87 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1049 USM 50526 1 1 50.2231 1 NaN 100.00 123 118 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1050 USM 50527 1 3 18.4148 1 NaN 100.00 93 84 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1051 USM 50528 1 1 11.3539 1 NaN 100.00 100 90 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1052 USM 50529 1 1 42.3354 1 NaN 93.33 128 122 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1053 USM 50530 1 1 18.2450 1 NaN -14.29 106 120 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1054 USM 50531 1 1 28.0903 1 NaN 78.57 112 104 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN
1055 USM 50532 1 1 16.9199 1 NaN 100.00 84 87 ... NaN NaN NaN NaN NaN NaN NaN 1 NaN NaN

901 rows × 74 columns


In [9]:
subs = pheno_data['SUB_ID'].values

In [12]:
for i in np.arange(1,10):
    print(subs[i], data_subs[i])


(51457, 50187)
(51458, 50778)
(51459, 50698)
(51460, 50165)
(51461, 51257)
(51462, 51081)
(51463, 51561)
(51464, 50058)
(51465, 51033)

In [9]:
# Define covariates of interest
cov_interest = ['DX_GROUP', 'AGE_AT_SCAN', 'SITE_ID', 'SEX', 'EYE_STATUS_AT_SCAN']

Make a subject level partition of each network and compare between clusters


In [17]:
def permute(args):
    col, row, data_subs, pheno_data, part, cov = args
    clus_a = col+1
    clus_b = row+1
    # Get values
    scale = np.max(part)
    dist_mat = np.zeros((scale, scale))
    p_mat = np.zeros((scale, scale))
    # Get cluster a 
    sub_a =  data_subs[part == clus_a]
    pheno_a = pheno_data[part == clus_a]
    var_a = pheno_a[cov].values
    # Get cluster b
    sub_b =  data_subs[part == clus_b]
    pheno_b = pheno_data[part == clus_b]
    var_b = pheno_b[cov].values
    # Compute the distance
    dis = np.mean(var_a) - np.mean(var_b)
    dist_mat[col, row] = np.abs(dis)
    # Concatenate the two
    com_a_b = np.concatenate((var_a[...,None], var_b[...,None]),axis=0)
    # Resample stuff
    res = np.empty(reps)
    for i in range(len(res)):
        tmp = np.random.permutation(com_a_b)
        # Slice out new randomized labels for both clusters
        tmp_a = tmp[:len(var_a)]
        tmp_b = tmp[len(var_a):]
        # Take the mean of the distance (could go KGS on this)
        tmp_dist = np.mean(tmp_a) - np.mean(tmp_b)
        res[i] = tmp_dist
    # Compute the p-value for the distance
    p_mat[col, row] = (np.sum(np.abs(res) > np.abs(dis)) + 1) / float(reps + 1)
    
    return dist_mat, p_mat

In [18]:
def cont_test(args):
    col, row, data_subs, pheno_data, part, cov = args
    clus_a = col+1
    clus_b = row+1
    # Get values
    scale = np.max(part)
    dist_mat = np.zeros((scale, scale))
    p_mat = np.zeros((scale, scale))
    # Get cluster a 
    sub_a =  data_subs[part == clus_a]
    pheno_a = pheno_data[part == clus_a]
    var_a = pheno_a[cov].values
    # Get cluster b
    sub_b =  data_subs[part == clus_b]
    pheno_b = pheno_data[part == clus_b]
    var_b = pheno_b[cov].values
    #KGS
    ks, p = st.ks_2samp(var_a, var_b)
    p_mat[col, row] = p
    
    return p_mat

In [19]:
# Initialize to scale sc
sc = 3
#scales = np.ones(10)*sc
scales = np.array([6, 7, 5, 5, 9, 7, 9, 9, 11, 7])
partitions = np.zeros((len(pheno_data), 10))
dist_list = list()
pval_list = list()

cov = cov_interest[1]
reps = 1e5

for n_id in range(10):
    print('Running network {} now'.format(n_id))
    distance, linkage = results[n_id]
    scale = scales[n_id]
    part = clh.fcluster(linkage, scale, criterion='maxclust')
    partitions[:, n_id] = part
    
    scale = np.max(part)
    # Get coordinate pairs for the upper triangle so we do each comparison only once
    cols, rows = np.triu_indices(scale,1)
    
    arg_list = list()
    for idx in range(len(cols)):
        col = cols[idx]
        row = rows[idx]
        args = (col, row, data_subs, pheno_data, part, cov)
        arg_list.append(args)
    
    use_perc = 0.80
    ava = int(np.floor(use_perc*mp.cpu_count()))
    pool = mp.Pool(processes=ava)
    pll_res = pool.map(cont_test, arg_list)
    # Stack the stuff together again
    dist = np.zeros((scale, scale))
    pval = np.zeros((scale, scale))
    for result in pll_res:
        pval += result
    # And copy over the diagonal
    dist[rows, cols] = dist[cols, rows]
    pval[rows, cols] = pval[cols, rows]
    dist_list.append(dist)
    pval_list.append(pval)


Running network 0 now
Running network 1 now
Running network 2 now
Running network 3 now
Running network 4 now
Running network 5 now
Running network 6 now
Running network 7 now
Running network 8 now
Running network 9 now

In [20]:
f = plt.figure(figsize=(16,80))
for idx, n_id in enumerate(np.linspace(1,10,10)):
    pval = pval_list[idx]
    ax = f.add_subplot(10,1,n_id)
    divider = make_axes_locatable(ax)
    cax = divider.append_axes("right", size="5%", pad=0.05)
    mp1 = ax.matshow(-np.log10(pval), cmap=plt.cm.Blues)
    ax.set_title('p-value -log10 network {}'.format(idx))
    f.colorbar(mp1, cax=cax)