diff --git a/bct/algorithms/centrality.py b/bct/algorithms/centrality.py index 6d8474d..6216742 100644 --- a/bct/algorithms/centrality.py +++ b/bct/algorithms/centrality.py @@ -173,13 +173,14 @@ def entropy(w_): pnm[np.logical_not(pnm)] = 1 return -np.sum(pnm * np.log(pnm), axis=1) / np.log(m) - #explicitly ignore compiler warning for division by zero + # explicitly ignore compiler warning for division by zero with np.errstate(invalid='ignore'): Hpos = entropy(W * (W > 0)) Hneg = entropy(-W * (W < 0)) return Hpos, Hneg + def edge_betweenness_bin(G): ''' Edge betweenness centrality is the fraction of all shortest paths in @@ -346,7 +347,6 @@ def eigenvector_centrality_und(CIJ): ''' from scipy import linalg - n = len(CIJ) vals, vecs = linalg.eig(CIJ) i = np.argmax(vals) return np.abs(vecs[:, i]) @@ -486,16 +486,11 @@ def gateway_coef_sign(W, ci, centrality_type='degree'): np.fill_diagonal(W, 0) def gcoef(W): - #strength - s = np.sum(W, axis=1) - #neighbor community affiliation - Gc = np.inner((W != 0), np.diag(ci)) - #community specific neighbors - Sc2 = np.zeros((n,)) - #extra modular weighting - ksm = np.zeros((n,)) - #intra modular wieghting - centm = np.zeros((n,)) + s = np.sum(W, axis=1) # strength + Gc = np.inner((W != 0), np.diag(ci)) # neighbor community affiliation + Sc2 = np.zeros((n,)) # community specific neighbors + ksm = np.zeros((n,)) # extra modular weighting + centm = np.zeros((n,)) # intra modular wieghting if centrality_type == 'degree': cent = s.copy() @@ -508,19 +503,14 @@ def gcoef(W): print(np.sum(ks)) Sc2 += ks ** 2 for j in range(1, nr_modules+1): - #calculate extramodular weights + # calculate extramodular weights ksm[ci == j] += ks[ci == j] / np.sum(ks[ci == j]) - #calculate intramodular weights + # calculate intramodular weights centm[ci == i] = np.sum(cent[ci == i]) - #print(Gc) - #print(centm) - #print(ksm) - #print(ks) - centm = centm / max(centm) - #calculate total weights + # calculate total weights gs = (1 - ksm * centm) ** 2 Gw = 1 - Sc2 * gs / s ** 2 @@ -532,7 +522,7 @@ def gcoef(W): G_pos = gcoef(W * (W > 0)) G_neg = gcoef(-W * (W < 0)) return G_pos, G_neg - + def kcoreness_centrality_bd(CIJ): ''' @@ -780,13 +770,14 @@ def pcoef(W_): P[np.where(np.logical_not(P))] = 0 # p_ind=0 if no (out)neighbors return P - #explicitly ignore compiler warning for division by zero + # explicitly ignore compiler warning for division by zero with np.errstate(invalid='ignore'): Ppos = pcoef(W * (W > 0)) Pneg = pcoef(-W * (W < 0)) return Ppos, Pneg + def subgraph_centrality(CIJ): ''' The subgraph centrality of a node is a weighted sum of closed walks of diff --git a/bct/algorithms/clustering.py b/bct/algorithms/clustering.py index a8da356..e32fb7c 100644 --- a/bct/algorithms/clustering.py +++ b/bct/algorithms/clustering.py @@ -221,7 +221,7 @@ def clustering_coef_wu_sign(W, coef_type='default'): ''' Returns the weighted clustering coefficient generalized or separated for positive and negative weights. - + Three Algorithms are supported; herefore referred to as default, zhang, and constantini. @@ -319,6 +319,7 @@ def clustering_coef_wu_sign(W, coef_type='default'): C = cyc3 / cyc2 return C + def consensus_und(D, tau, reps=1000): ''' This algorithm seeks a consensus partition of the @@ -385,11 +386,7 @@ def unique_partitions(cis): dup = np.where(np.sum(np.abs(cis.T - cis[:, 0]), axis=1) == 0) cis = np.delete(cis, dup, axis=1) c = np.delete(c, dup) - # count+=1 - # print count,c,dup - # if count>10: - # class QualitativeError(): pass - # raise QualitativeError() + return np.transpose(ciu) n = len(D) @@ -448,14 +445,15 @@ def get_components(A, no_depend=False): ''' if not np.all(A == A.T): # ensure matrix is undirected - raise BCTParamError('get_components can only be computed for undirected' - ' matrices. If your matrix is noisy, correct it with np.around') - + raise BCTParamError('get_components can only be computed for ' + 'undirected matrices. If your matrix is ' + 'noisy, correct it with np.around') + A = binarize(A, copy=True) n = len(A) np.fill_diagonal(A, 1) - edge_map = [{u,v} for u in range(n) for v in range(n) if A[u,v] == 1] + edge_map = [{u, v} for u in range(n) for v in range(n) if A[u, v] == 1] union_sets = [] for item in edge_map: temp = [] @@ -468,8 +466,8 @@ def get_components(A, no_depend=False): temp.append(item) union_sets = temp - comps = np.array([i+1 for v in range(n) for i in - range(len(union_sets)) if v in union_sets[i]]) + comps = np.array([i+1 for v in range(n) for i in + range(len(union_sets)) if v in union_sets[i]]) comp_sizes = np.array([len(s) for s in union_sets]) return comps, comp_sizes @@ -517,8 +515,9 @@ def get_components_old(A, no_depend=False): # nonsquare matrices cannot be symmetric; no need to check if not np.all(A == A.T): # ensure matrix is undirected - raise BCTParamError('get_components can only be computed for undirected' - ' matrices. If your matrix is noisy, correct it with np.around') + raise BCTParamError('get_components can only be computed for ' + 'undirected matrices. If your matrix is ' + 'noisy, correct it with np.around') A = binarize(A, copy=True) n = len(A) @@ -589,9 +588,9 @@ def transitivity_bd(A): = 2 * (K(K-1)/2 - diag(A^2)) = K(K-1) - 2(diag(A^2)) ''' - S = A + A.T # symmetrized input graph - K = np.sum(S, axis=1) # total degree (in+out) - cyc3 = np.diag(np.dot(S, np.dot(S, S))) / 2 # number of 3-cycles + S = A + A.T # symmetrized input graph + K = np.sum(S, axis=1) # total degree (in+out) + cyc3 = np.diag(np.dot(S, np.dot(S, S))) / 2 # number of 3-cycles CYC3 = K * (K - 1) - 2 * np.diag(np.dot(A, A)) # number of all possible 3-cycles return np.sum(cyc3) / np.sum(CYC3) diff --git a/bct/algorithms/core.py b/bct/algorithms/core.py index 2a669cf..0e537ec 100644 --- a/bct/algorithms/core.py +++ b/bct/algorithms/core.py @@ -1,5 +1,6 @@ from __future__ import division, print_function import numpy as np +from bct.utils import BCTParamError from .degree import degrees_dir, degrees_und, strengths_dir, strengths_und from .degree import strengths_und_sign @@ -132,8 +133,8 @@ def assortativity_wei(CIJ, flag=0): def core_periphery_dir(W, gamma=1, C0=None): - ''' - The optimal core/periphery subdivision is a partition of the network + ''' + The optimal core/periphery subdivision is a partition of the network into two nonoverlapping groups of nodes, a core group and a periphery group. The number of core-group edges is maximized, and the number of within periphery edges is minimized. @@ -161,13 +162,13 @@ def core_periphery_dir(W, gamma=1, C0=None): n = len(W) np.fill_diagonal(W, 0) - if C0 == None: + if C0 is None: C = np.random.randint(2, size=(n,)) else: C = C0.copy() - #methodological note, the core-detection null model is not corrected - #for degree cf community detection (to enable detection of hubs) + # methodological note, the core-detection null model is not corrected + # for degree cf community detection (to enable detection of hubs) s = np.sum(W) p = np.mean(W) @@ -178,25 +179,25 @@ def core_periphery_dir(W, gamma=1, C0=None): q = np.sum(B[np.ix_(cix, cix)]) - np.sum(B[np.ix_(ncix, ncix)]) print(q) - #sqish + # sqish flag = True it = 0 while flag: - it += 1 + it += 1 if it > 100: raise BCTParamError('Infinite Loop aborted') flag = False - #initial node indices - ixes = np.arange(n) + # initial node indices + ixes = np.arange(n) Ct = C.copy() while len(ixes) > 0: Qt = np.zeros((n,)) ctix, = np.where(Ct) nctix, = np.where(np.logical_not(Ct)) - q0 = (np.sum(B[np.ix_(ctix, ctix)]) - + q0 = (np.sum(B[np.ix_(ctix, ctix)]) - np.sum(B[np.ix_(nctix, nctix)])) Qt[ctix] = q0 - 2 * np.sum(B[ctix, :], axis=1) Qt[nctix] = q0 + 2 * np.sum(B[nctix, :], axis=1) @@ -206,15 +207,15 @@ def core_periphery_dir(W, gamma=1, C0=None): print(np.where(np.abs(Qt[ixes]-max_Qt) < 1e-10)) print(Qt[ixes]) print(max_Qt) - #tunourn + # tunourn u = u[np.random.randint(len(u))] print(np.sum(Ct)) Ct[ixes[u]] = np.logical_not(Ct[ixes[u]]) print(np.sum(Ct)) - #casga + # casga ixes = np.delete(ixes, u) - + print(max_Qt - q) print(len(ixes)) @@ -223,7 +224,7 @@ def core_periphery_dir(W, gamma=1, C0=None): C = Ct.copy() cix, = np.where(C) ncix, = np.where(np.logical_not(C)) - q = (np.sum(B[np.ix_(cix, cix)]) - + q = (np.sum(B[np.ix_(cix, cix)]) - np.sum(B[np.ix_(ncix, ncix)])) cix, = np.where(C) @@ -378,7 +379,7 @@ def local_assortativity_wu_sign(W): ---------- W : NxN np.ndarray undirected connection matrix with positive and negative weights - + Returns ------- loc_assort_pos : Nx1 np.ndarray @@ -399,19 +400,20 @@ def local_assortativity_wu_sign(W): for curr_node in range(n): jp = np.where(W[curr_node, :] > 0) - loc_assort_pos[curr_node] = np.sum(np.abs(str_pos[jp] - - str_pos[curr_node])) / str_pos[curr_node] + loc_assort_pos[curr_node] = np.sum(np.abs( + str_pos[jp] - str_pos[curr_node])) / str_pos[curr_node] jn = np.where(W[curr_node, :] < 0) - loc_assort_neg[curr_node] = np.sum(np.abs(str_neg[jn] - - str_neg[curr_node])) / str_neg[curr_node] + loc_assort_neg[curr_node] = np.sum(np.abs( + str_neg[jn] - str_neg[curr_node])) / str_neg[curr_node] - loc_assort_pos = ((r_pos + 1) / n - - loc_assort_pos / np.sum(loc_assort_pos)) + loc_assort_pos = ((r_pos + 1) / n - + loc_assort_pos / np.sum(loc_assort_pos)) loc_assort_neg = ((r_neg + 1) / n - - loc_assort_neg / np.sum(loc_assort_neg)) + loc_assort_neg / np.sum(loc_assort_neg)) return loc_assort_pos, loc_assort_neg + def rich_club_bd(CIJ, klevel=None): ''' The rich club coefficient, R, at level k is the fraction of edges that @@ -484,7 +486,7 @@ def rich_club_bu(CIJ, klevel=None): ''' deg = degrees_und(CIJ) # compute degree of each node - if klevel == None: + if klevel is None: klevel = int(np.max(deg)) R = np.zeros((klevel,)) @@ -518,7 +520,6 @@ def rich_club_wd(CIJ, klevel=None): Rw : Kx1 np.ndarray vector of rich-club coefficients for levels 1 to klevel ''' - nr_nodes = len(CIJ) # degree of each node is defined here as in+out deg = np.sum((CIJ != 0), axis=0) + np.sum((CIJ.T != 0), axis=0) @@ -565,7 +566,6 @@ def rich_club_wu(CIJ, klevel=None): Rw : Kx1 np.ndarray vector of rich-club coefficients for levels 1 to klevel ''' - nr_nodes = len(CIJ) deg = np.sum((CIJ != 0), axis=0) if klevel is None: diff --git a/bct/algorithms/degree.py b/bct/algorithms/degree.py index d844a54..89270ad 100644 --- a/bct/algorithms/degree.py +++ b/bct/algorithms/degree.py @@ -175,11 +175,10 @@ def strengths_und_sign(W): total negative weight ''' W = W.copy() - n = len(W) - np.fill_diagonal(W, 0) # clear diagonal + np.fill_diagonal(W, 0) # clear diagonal Spos = np.sum(W * (W > 0), axis=0) # positive strengths - Sneg = np.sum(W * (W < 0), axis=0) # negative strengths + Sneg = np.sum(W * (W < 0), axis=0) # negative strengths - vpos = np.sum(W[W > 0]) # positive weight - vneg = np.sum(W[W < 0]) # negative weight + vpos = np.sum(W[W > 0]) # positive weight + vneg = np.sum(W[W < 0]) # negative weight return Spos, Sneg, vpos, vneg diff --git a/bct/algorithms/distance.py b/bct/algorithms/distance.py index 53f3707..3da24bc 100644 --- a/bct/algorithms/distance.py +++ b/bct/algorithms/distance.py @@ -368,16 +368,12 @@ def distance_inv(g): E = np.zeros((n,)) # local efficiency for u in range(n): - # V,=np.where(G[u,:]) #neighbors - # k=len(V) #degree - # if k>=2: #degree must be at least 2 - # e=distance_inv(G[V].T[V]) - # E[u]=np.sum(e)/(k*k-k) #local efficiency computation - # find pairs of neighbors V, = np.where(np.logical_or(G[u, :], G[u, :].T)) + # inverse distance matrix e = distance_inv(G[np.ix_(V, V)]) + # symmetrized inverse distance matrix se = e + e.T @@ -472,18 +468,15 @@ def distance_inv_wei(G): if local: E = np.zeros((n,)) # local efficiency for u in range(n): - # V,=np.where(Gw[u,:]) #neighbors - # k=len(V) #degree - # if k>=2: #degree must be at least 2 - # e=(distance_inv_wei(Gl[V].T[V])*np.outer(Gw[V,u],Gw[u,V]))**1/3 - # E[u]=np.sum(e)/(k*k-k) - # find pairs of neighbors V, = np.where(np.logical_or(Gw[u, :], Gw[:, u].T)) + # symmetrized vector of weights sw = cuberoot(Gw[u, V]) + cuberoot(Gw[V, u].T) + # inverse distance matrix e = distance_inv_wei(Gl[np.ix_(V, V)]) + # symmetrized inverse distance matrix se = cuberoot(e) + cuberoot(e.T) @@ -581,8 +574,8 @@ def findpaths(CIJ, qmax, sources, savepths=False): # big loop for all other pathlengths q for q in range(2, qmax + 1): # to keep track of time... - print(( - 'current pathlength (q=i, number of paths so far (up to q-1)=i' % (q, np.sum(Pq)))) + print(('current pathlength (q=i, number of paths so far (up to q-1)=i' + % (q, np.sum(Pq)))) # old paths are now in 'pths' # new paths are about to be collected in 'npths' @@ -705,8 +698,8 @@ def reachdist(CIJ, ensure_binary=True): CIJ : NxN np.ndarray binary directed/undirected connection matrix ensure_binary : bool - Binarizes input. Defaults to true. No user who is not testing - something will ever want to not use this, use distance_wei instead for + Binarizes input. Defaults to true. No user who is not testing + something will ever want to not use this, use distance_wei instead for unweighted matrices. Returns @@ -753,7 +746,7 @@ def reachdist2(CIJ, CIJpwr, R, D, n, powr, col, row): R, D, powr = reachdist2(CIJ, CIJpwr, R, D, n, powr, col, row) - #'invert' CIJdist to get distances + # 'invert' CIJdist to get distances D = powr - D + 1 # put inf if no path found diff --git a/bct/algorithms/generative.py b/bct/algorithms/generative.py index e5e5f75..7c7faf5 100644 --- a/bct/algorithms/generative.py +++ b/bct/algorithms/generative.py @@ -7,16 +7,17 @@ from .centrality import betweenness_bin -def generative_model(A, D, m, eta, gamma=None, model_type='matching', - model_var='powerlaw', epsilon=1e-6, copy=True): +def generative_model(A, D, m, eta, gamma=None, model_type='matching', + model_var='powerlaw', epsilon=1e-6, copy=True): ''' + Generates synthetic networks using the models described in Betzel et al. (2016) Neuroimage. See this paper for more details. - Succinctly, the probability of forming a connection between nodes u and v is - P(u,v) = E(u,v)**eta * K(u,v)**gamma - where eta and gamma are hyperparameters, E(u,v) is the euclidean or similar - distance measure, and K(u,v) is the algorithm that defines the model. + Succinctly, the probability of forming a connection between nodes u and + v is P(u,v) = E(u,v)**eta * K(u,v)**gamma where eta and gamma are + hyperparameters, E(u,v) is the euclidean or similar distance measure, + and K(u,v) is the algorithm that defines the model. This describes the power law formulation, an alternative formulation uses the exponential function @@ -29,10 +30,10 @@ def generative_model(A, D, m, eta, gamma=None, model_type='matching', D : np.ndarray Matrix of euclidean distances or other distances between nodes m : int - Number of connections that should be present in the final synthetic + Number of connections that should be present in the final synthetic network eta : np.ndarray - A vector describing a range of values to estimate for eta, the + A vector describing a range of values to estimate for eta, the hyperparameter describing exponential weighting of the euclidean distance. gamma : np.ndarray @@ -41,7 +42,7 @@ def generative_model(A, D, m, eta, gamma=None, model_type='matching', algorithm. If model_type='euclidean' or another distance metric, this can be None. model_type : Enum(str) - euclidean : Uses only euclidean distances to generate connection + euclidean : Uses only euclidean distances to generate connection probabilities neighbors : count of common neighbors matching : matching index, the normalized overlap in neighborhoods @@ -70,13 +71,13 @@ def generative_model(A, D, m, eta, gamma=None, model_type='matching', A = A.copy() n = len(D) - - #These parameters don't do any of the voronoi narrowing. - #Its a list of eta values paired with gamma values. - #To try 3 eta and 3 gamma pairs, should use 9 list values. + + # These parameters don't do any of the voronoi narrowing. + # Its a list of eta values paired with gamma values. + # To try 3 eta and 3 gamma pairs, should use 9 list values. if len(eta) != len(gamma): raise BCTParamError('Eta and gamma hyperparameters must be lists of ' - 'the same size') + 'the same size') nparams = len(eta) @@ -84,10 +85,10 @@ def generative_model(A, D, m, eta, gamma=None, model_type='matching', def k_avg(K): return ((np.tile(K, (n, 1)) + np.transpose(np.tile(K, (n, 1))))/2 + - epsilon) + epsilon) def k_diff(K): - return np.abs(np.tile(K, (n, 1)) - + return np.abs(np.tile(K, (n, 1)) - np.transpose(np.tile(K, (n, 1)))) + epsilon def k_max(K): @@ -111,9 +112,8 @@ def s_diff(K, sc): def s_min(K, sc): return np.where(K < sc, K + epsilon, sc + epsilon) - + def s_max(K, sc): - #return np.max((K, sc.T), axis=0) return np.where(K > sc, K + epsilon, sc + epsilon) def s_prod(K, sc): @@ -149,11 +149,10 @@ def x_prod(K, ixes): Kb = np.reshape(np.transpose(K), (1, n)) return np.outer(Ka, Kb) + epsilon - def clu_gen(A, K, D, m, eta, gamma, model_var, x_fun): mseed = np.size(np.where(A.flat))//2 - A = A>0 + A = A > 0 if type(model_var) == tuple: mv1, mv2 = model_var @@ -163,31 +162,30 @@ def clu_gen(A, K, D, m, eta, gamma, model_var, x_fun): if mv1 in ('powerlaw', 'power_law'): Fd = D**eta elif mv1 in ('exponential',): - Fd = np.exp(eta*D) + Fd = np.exp(eta*D) if mv2 in ('powerlaw', 'power_law'): Fk = K**gamma elif mv2 in ('exponential',): - Fk = np.exp(gamma*K) + Fk = np.exp(gamma*K) c = clustering_coef_bu(A) k = np.sum(A, axis=1) Ff = Fd * Fk * np.logical_not(A) - u,v = np.where(np.triu(np.ones((n,n)), 1)) + u, v = np.where(np.triu(np.ones((n, n)), 1)) - #print(mseed, m) for i in range(mseed+1, m): - C = np.append(0, np.cumsum(Ff[u,v])) + C = np.append(0, np.cumsum(Ff[u, v])) r = np.sum(np.random.random()*C[-1] >= C) uu = u[r] vv = v[r] - A[uu,vv] = A[vv,uu] = 1 + A[uu, vv] = A[vv, uu] = 1 k[uu] += 1 k[vv] += 1 - bu = A[uu,:].astype(bool) - bv = A[vv,:].astype(bool) + bu = A[uu, :].astype(bool) + bv = A[vv, :].astype(bool) su = A[np.ix_(bu, bu)] sv = A[np.ix_(bu, bu)] @@ -195,26 +193,21 @@ def clu_gen(A, K, D, m, eta, gamma, model_var, x_fun): c[bth] += 2/(k[bth]**2 - k[bth]) c[uu] = np.size(np.where(su.flat))/(k[uu]*(k[uu]-1)) c[vv] = np.size(np.where(sv.flat))/(k[vv]*(k[vv]-1)) - c[k<=1] = 0 + c[k <= 1] = 0 bth[uu] = 1 bth[vv] = 1 - - k_result = x_fun(c, bth) - #print(np.shape(k_result)) - #print(np.shape(K)) - #print(K) - #print(np.shape(K[bth,:])) + k_result = x_fun(c, bth) - K[bth,:] = k_result - K[:,bth] = k_result.T + K[bth, :] = k_result + K[:, bth] = k_result.T if mv2 in ('powerlaw', 'power_law'): - Ff[bth,:] = Fd[bth,:] * K[bth,:]**gamma - Ff[:,bth] = Fd[:,bth] * K[:,bth]**gamma + Ff[bth, :] = Fd[bth, :] * K[bth, :]**gamma + Ff[:, bth] = Fd[:, bth] * K[:, bth]**gamma elif mv2 in ('exponential',): - Ff[bth,:] = Fd[bth,:] * np.exp(K[bth,:])*gamma - Ff[:,bth] = Fd[:,bth] * np.exp(K[:,bth])*gamma + Ff[bth, :] = Fd[bth, :] * np.exp(K[bth, :])*gamma + Ff[:, bth] = Fd[:, bth] * np.exp(K[:, bth])*gamma Ff = Ff * np.logical_not(A) @@ -233,30 +226,22 @@ def deg_gen(A, K, D, m, eta, gamma, model_var, s_fun): if mv1 in ('powerlaw', 'power_law'): Fd = D**eta elif mv1 in ('exponential',): - Fd = np.exp(eta*D) + Fd = np.exp(eta*D) if mv2 in ('powerlaw', 'power_law'): Fk = K**gamma elif mv2 in ('exponential',): - Fk = np.exp(gamma*K) + Fk = np.exp(gamma*K) P = Fd * Fk * np.logical_not(A) - u,v = np.where(np.triu(np.ones((n,n)), 1)) + u, v = np.where(np.triu(np.ones((n, n)), 1)) b = np.zeros((m,), dtype=int) -# print(mseed) -# print(np.shape(u),np.shape(v)) -# print(np.shape(b)) -# print(np.shape(A[u,v])) -# print(np.shape(np.where(A[u,v])), 'sqishy') -# print(np.shape(P), 'squnnaq') + b[:mseed] = np.squeeze(np.where(A[u, v])) - #b[:mseed] = np.where(A[np.ix_(u,v)]) - b[:mseed] = np.squeeze(np.where(A[u,v])) - #print(mseed, m) for i in range(mseed, m): - C = np.append(0, np.cumsum(P[u,v])) + C = np.append(0, np.cumsum(P[u, v])) r = np.sum(np.random.random()*C[-1] >= C) uu = u[r] vv = v[r] @@ -264,38 +249,17 @@ def deg_gen(A, K, D, m, eta, gamma, model_var, s_fun): k[vv] += 1 if mv2 in ('powerlaw', 'power_law'): - Fk[:,uu] = Fk[uu,:] = s_fun(k, k[uu]) ** gamma - Fk[:,vv] = Fk[vv,:] = s_fun(k, k[vv]) ** gamma + Fk[:, uu] = Fk[uu, :] = s_fun(k, k[uu]) ** gamma + Fk[:, vv] = Fk[vv, :] = s_fun(k, k[vv]) ** gamma elif mv2 in ('exponential',): - Fk[:,uu] = Fk[uu,:] = np.exp(s_fun(k, k[uu]) * gamma) - Fk[:,vv] = Fk[vv,:] = np.exp(s_fun(k, k[vv]) * gamma) + Fk[:, uu] = Fk[uu, :] = np.exp(s_fun(k, k[uu]) * gamma) + Fk[:, vv] = Fk[vv, :] = np.exp(s_fun(k, k[vv]) * gamma) P = Fd * Fk - b[i] = r P[u[b[:i]], v[b[:i]]] = P[v[b[:i]], u[b[:i]]] = 0 - A[u[r], v[r]] = A[v[r], u[r]] = 1 - #P[b[u[:i]], b[v[:i]]] = P[b[v[:i]], b[u[:i]]] = 0 - - #A[uu,vv] = A[vv,uu] = 1 - - -# indx = v*n + u -# indx[b] -# -# nH = np.zeros((n,n)) -# nH.ravel()[indx[b]]=1 -# -# nG = np.zeros((n,n)) -# nG[ u[b], v[b] ]=1 -# nG = nG + nG.T -# -# print(np.shape(np.where(A != nG))) -# -# import pdb -# pdb.set_trace() return A @@ -312,63 +276,67 @@ def matching_gen(A, K, D, m, eta, gamma, model_var): if mv1 in ('powerlaw', 'power_law'): Fd = D**eta elif mv1 in ('exponential',): - Fd = np.exp(eta*D) + Fd = np.exp(eta*D) if mv2 in ('powerlaw', 'power_law'): Fk = K**gamma elif mv2 in ('exponential',): - Fk = np.exp(gamma*K) + Fk = np.exp(gamma*K) Ff = Fd * Fk * np.logical_not(A) - u,v = np.where(np.triu(np.ones((n,n)), 1)) - + u, v = np.where(np.triu(np.ones((n, n)), 1)) + for ii in range(mseed, m): - C = np.append(0, np.cumsum(Ff[u,v])) + C = np.append(0, np.cumsum(Ff[u, v])) r = np.sum(np.random.random()*C[-1] >= C) uu = u[r] vv = v[r] - A[uu,vv] = A[vv,uu] = 1 + A[uu, vv] = A[vv, uu] = 1 - updateuu, = np.where(np.inner(A, A[:,uu])) + updateuu, = np.where(np.inner(A, A[:, uu])) np.delete(updateuu, np.where(updateuu == uu)) np.delete(updateuu, np.where(updateuu == vv)) - c1 = np.append(A[:,uu], A[uu,:]) + c1 = np.append(A[:, uu], A[uu, :]) for i in range(len(updateuu)): j = updateuu[i] - c2 = np.append(A[:,j], A[j,:]) - + c2 = np.append(A[:, j], A[j, :]) + use = np.logical_or(c1, c2) use[uu] = use[uu+n] = use[j] = use[j+n] = 0 ncon = np.sum(c1[use]) + np.sum(c2[use]) if ncon == 0: K[uu, j] = K[j, uu] = epsilon else: - K[uu, j] = K[j, uu] = (2 / ncon * - np.sum(np.logical_and(c1[use], c2[use])) + epsilon) + K[uu, j] = K[j, uu] = ( + 2 / ncon * + np.sum(np.logical_and(c1[use], c2[use])) + + epsilon) - updatevv, = np.where(np.inner(A, A[:,vv])) + updatevv, = np.where(np.inner(A, A[:, vv])) np.delete(updatevv, np.where(updatevv == uu)) np.delete(updatevv, np.where(updatevv == vv)) - - c1 = np.append(A[:,vv], A[vv,:]) + + c1 = np.append(A[:, vv], A[vv, :]) for i in range(len(updatevv)): j = updatevv[i] - c2 = np.append(A[:,j], A[j,:]) - + c2 = np.append(A[:, j], A[j, :]) + use = np.logical_or(c1, c2) use[vv] = use[vv+n] = use[j] = use[j+n] = 0 ncon = np.sum(c1[use]) + np.sum(c2[use]) if ncon == 0: K[vv, j] = K[j, vv] = epsilon else: - K[vv, j] = K[j, vv] = (2 / ncon * - np.sum(np.logical_and(c1[use], c2[use])) + epsilon) + K[vv, j] = K[j, vv] = ( + 2 / ncon * + np.sum(np.logical_and(c1[use], c2[use])) + + epsilon) Ff = Fd * Fk * np.logical_not(A) return A - + def neighbors_gen(A, K, D, m, eta, gamma, model_var): K += epsilon @@ -382,18 +350,18 @@ def neighbors_gen(A, K, D, m, eta, gamma, model_var): if mv1 in ('powerlaw', 'power_law'): Fd = D**eta elif mv1 in ('exponential',): - Fd = np.exp(eta*D) + Fd = np.exp(eta*D) if mv2 in ('powerlaw', 'power_law'): Fk = K**gamma elif mv2 in ('exponential',): - Fk = np.exp(gamma*K) + Fk = np.exp(gamma*K) Ff = Fd * Fk * np.logical_not(A) - u,v = np.where(np.triu(np.ones((n,n)), 1)) - + u, v = np.where(np.triu(np.ones((n, n)), 1)) + for ii in range(mseed, m): - C = np.append(0, np.cumsum(Ff[u,v])) + C = np.append(0, np.cumsum(Ff[u, v])) r = np.sum(np.random.random()*C[-1] >= C) uu = u[r] vv = v[r] @@ -401,7 +369,7 @@ def neighbors_gen(A, K, D, m, eta, gamma, model_var): x = A[uu, :].astype(int) y = A[:, vv].astype(int) - + K[uu, y] += 1 K[y, uu] += 1 K[vv, x] += 1 @@ -410,7 +378,7 @@ def neighbors_gen(A, K, D, m, eta, gamma, model_var): if mv2 in ('powerlaw', 'power_law'): Fk = K**gamma elif mv2 in ('exponential',): - Fk = np.exp(gamma*K) + Fk = np.exp(gamma*K) if mv2 in ('powerlaw', 'power_law'): Ff[uu, y] = Ff[y, uu] = Fd[uu, y] * (K[uu, y] ** gamma) @@ -439,7 +407,7 @@ def euclidean_gen(A, D, m, eta, model_var): elif mv1 in ('exponential',): Fd = np.exp(eta ** D) - u,v = np.where(np.triu(np.ones((n,n)), 1)) + u, v = np.where(np.triu(np.ones((n, n)), 1)) P = Fd * np.logical_not(A) b = np.zeros((m,), dtype=int) @@ -458,95 +426,95 @@ def euclidean_gen(A, D, m, eta, model_var): if model_type in ('clu-avg', 'clu_avg'): Kseed = k_avg(clustering_coef_bu(A)) for j, (ep, gp) in enumerate(zip(eta, gamma)): - B[:,:,j] = clu_gen(A, Kseed, D, m, ep, gp, model_var, x_avg) + B[:, :, j] = clu_gen(A, Kseed, D, m, ep, gp, model_var, x_avg) elif model_type in ('clu-diff', 'clu_diff'): Kseed = k_diff(clustering_coef_bu(A)) for j, (ep, gp) in enumerate(zip(eta, gamma)): - B[:,:,j] = clu_gen(A, Kseed, D, m, ep, gp, model_var, x_diff) + B[:, :, j] = clu_gen(A, Kseed, D, m, ep, gp, model_var, x_diff) elif model_type in ('clu-max', 'clu_max'): Kseed = k_max(clustering_coef_bu(A)) for j, (ep, gp) in enumerate(zip(eta, gamma)): - B[:,:,j] = clu_gen(A, Kseed, D, m, ep, gp, model_var, x_max) + B[:, :, j] = clu_gen(A, Kseed, D, m, ep, gp, model_var, x_max) elif model_type in ('clu-min', 'clu_min'): Kseed = k_min(clustering_coef_bu(A)) for j, (ep, gp) in enumerate(zip(eta, gamma)): - B[:,:,j] = clu_gen(A, Kseed, D, m, ep, gp, model_var, x_min) + B[:, :, j] = clu_gen(A, Kseed, D, m, ep, gp, model_var, x_min) elif model_type in ('clu-prod', 'clu_prod'): Kseed = k_prod(clustering_coef_bu(A)) for j, (ep, gp) in enumerate(zip(eta, gamma)): - B[:,:,j] = clu_gen(A, Kseed, D, m, ep, gp, model_var, x_prod) + B[:, :, j] = clu_gen(A, Kseed, D, m, ep, gp, model_var, x_prod) elif model_type in ('deg-avg', 'deg_avg'): Kseed = k_avg(np.sum(A, axis=1)) for j, (ep, gp) in enumerate(zip(eta, gamma)): - B[:,:,j] = deg_gen(A, Kseed, D, m, ep, gp, model_var, s_avg) + B[:, :, j] = deg_gen(A, Kseed, D, m, ep, gp, model_var, s_avg) elif model_type in ('deg-diff', 'deg_diff'): Kseed = k_diff(np.sum(A, axis=1)) for j, (ep, gp) in enumerate(zip(eta, gamma)): - B[:,:,j] = deg_gen(A, Kseed, D, m, ep, gp, model_var, s_diff) - + B[:, :, j] = deg_gen(A, Kseed, D, m, ep, gp, model_var, s_diff) + elif model_type in ('deg-max', 'deg_max'): Kseed = k_max(np.sum(A, axis=1)) for j, (ep, gp) in enumerate(zip(eta, gamma)): - B[:,:,j] = deg_gen(A, Kseed, D, m, ep, gp, model_var, s_max) + B[:, :, j] = deg_gen(A, Kseed, D, m, ep, gp, model_var, s_max) elif model_type in ('deg-min', 'deg_min'): Kseed = k_min(np.sum(A, axis=1)) for j, (ep, gp) in enumerate(zip(eta, gamma)): - B[:,:,j] = deg_gen(A, Kseed, D, m, ep, gp, model_var, s_min) + B[:, :, j] = deg_gen(A, Kseed, D, m, ep, gp, model_var, s_min) elif model_type in ('deg-prod', 'deg_prod'): Kseed = k_prod(np.sum(A, axis=1)) for j, (ep, gp) in enumerate(zip(eta, gamma)): - B[:,:,j] = deg_gen(A, Kseed, D, m, ep, gp, model_var, s_prod) + B[:, :, j] = deg_gen(A, Kseed, D, m, ep, gp, model_var, s_prod) elif model_type in ('neighbors',): Kseed = np.inner(A, A) np.fill_diagonal(Kseed, 0) for j, (ep, gp) in enumerate(zip(eta, gamma)): - B[:,:,j] = neighbors_gen(A, Kseed, D, m, ep, gp, model_var) + B[:, :, j] = neighbors_gen(A, Kseed, D, m, ep, gp, model_var) elif model_type in ('matching', 'matching-ind', 'matching_ind'): mi, _, _ = matching_ind(A) Kseed = mi + mi.T for j, (ep, gp) in enumerate(zip(eta, gamma)): - B[:,:,j] = matching_gen(A, Kseed, D, m, ep, gp, model_var) + B[:, :, j] = matching_gen(A, Kseed, D, m, ep, gp, model_var) elif model_type in ('spatial', 'geometric', 'euclidean'): for j, ep in enumerate(eta): - B[:,:,j] = euclidean_gen(A, D, m, ep, model_var) + B[:, :, j] = euclidean_gen(A, D, m, ep, model_var) return np.squeeze(B) -def evaluate_generative_model(A, Atgt, D, eta, gamma=None, - model_type='matching', model_var='powerlaw', epsilon=1e-6): + +def evaluate_generative_model(A, Atgt, D, eta, gamma=None, + model_type='matching', model_var='powerlaw', + epsilon=1e-6): ''' Generates synthetic networks with parameters provided and evaluates their energy function. The energy function is defined as in Betzel et al. 2016. Basically it takes the Kolmogorov-Smirnov statistics of 4 network measures; comparing the degree distributions, clustering coefficients, betweenness centrality, and Euclidean distances between connected regions. - + The energy is globally low if the synthetic network matches the target. Energy is defined as the maximum difference across the four statistics. ''' m = np.size(np.where(Atgt.flat))//2 - n = len(Atgt) xk = np.sum(Atgt, axis=1) xc = clustering_coef_bu(Atgt) xb = betweenness_bin(Atgt) xe = D[np.triu(Atgt, 1) > 0] - B = generative_model(A, D, m, eta, gamma, model_type=model_type, + B = generative_model(A, D, m, eta, gamma, model_type=model_type, model_var=model_var, epsilon=epsilon, copy=True) - #if eta != gamma then an error is thrown within generative model - + # if eta != gamma then an error is thrown within generative model nB = len(eta) if nB == 1: @@ -556,13 +524,11 @@ def evaluate_generative_model(A, Atgt, D, eta, gamma=None, def kstats(x, y): bin_edges = np.concatenate([[-np.inf], - np.sort(np.concatenate((x, y))), + np.sort(np.concatenate((x, y))), [np.inf]]) - bin_x,_ = np.histogram(x, bin_edges) - bin_y,_ = np.histogram(y, bin_edges) - - #print(np.shape(bin_x)) + bin_x, _ = np.histogram(x, bin_edges) + bin_y, _ = np.histogram(y, bin_edges) sum_x = np.cumsum(bin_x) / np.sum(bin_x) sum_y = np.cumsum(bin_y) / np.sum(bin_y) @@ -573,13 +539,12 @@ def kstats(x, y): delta_cdf = np.abs(cdfsamp_x - cdfsamp_y) print(np.shape(delta_cdf)) - #print(delta_cdf) print(np.argmax(delta_cdf), np.max(delta_cdf)) return np.max(delta_cdf) for ib in range(nB): - Bc = B[:,:,ib] + Bc = B[:, :, ib] yk = np.sum(Bc, axis=1) yc = clustering_coef_bu(Bc) yb = betweenness_bin(Bc) diff --git a/bct/algorithms/modularity.py b/bct/algorithms/modularity.py index 8970577..37ab61b 100644 --- a/bct/algorithms/modularity.py +++ b/bct/algorithms/modularity.py @@ -87,7 +87,7 @@ def community_louvain(W, gamma=1, ci=None, B='modularity', seed=None): initial community affiliation vector. default value=None B : str | NxN np.arraylike string describing objective function type, or provides a custom - NxN objective-function matrix. builtin values + NxN objective-function matrix. builtin values 'modularity' uses Q-metric as objective function 'potts' uses Potts model Hamiltonian. 'negative_sym' symmetric treatment of negative weights @@ -128,14 +128,14 @@ def community_louvain(W, gamma=1, ci=None, B='modularity', seed=None): s1 = np.sum(W1) if s1: B1 = (W1 - gamma * np.outer(np.sum(W1, axis=1), np.sum(W1, axis=0)) - / s1) + / s1) else: B1 = 0 elif np.min(W) < -1e-10: - raise BCTParamError("Input connection matrix contains negative " - 'weights but objective function dealing with negative weights ' - 'was not selected') + raise BCTParamError('Input connection matrix contains negative ' + 'weights but objective function dealing with ' + 'negative weights was not selected') if B == 'potts' and np.any(np.logical_not(np.logical_or(W == 0, W == 1))): raise BCTParamError('Potts hamiltonian requires binary input matrix') @@ -158,8 +158,8 @@ def community_louvain(W, gamma=1, ci=None, B='modularity', seed=None): raise BCTParamError('objective function matrix does not match ' 'size of adjacency matrix') if not np.allclose(B, B.T): - print ('Warning: objective function matrix not symmetric, ' - 'symmetrizing') + print('Warning: objective function matrix not symmetric, ' + 'symmetrizing') B = (B + B.T) / 2 Hnm = np.zeros((n, n)) @@ -333,32 +333,23 @@ def link_communities(W, type_clustering='single'): U = np.arange(m) # initial community assignments C[0, :] = np.arange(m) - import time - for i in range(m - 1): print('hierarchy %i' % i) - #time1 = time.time() - - for j in range(len(U)): # loop over communities + for j in range(len(U)): # loop over communities ixes = C[i, :] == U[j] # get link indices - links = np.sort(Lw[ixes]) - #nodes = np.sort(Ln[ixes,:].flat) nodes = np.sort(np.reshape( Ln[ixes, :], 2 * np.size(np.where(ixes)))) # get unique nodes nodulo = np.append(nodes[0], (nodes[1:])[nodes[1:] != nodes[:-1]]) - #nodulo = ((nodes[1:])[nodes[1:] != nodes[:-1]]) - nc = len(nodulo) - #nc = len(nodulo)+1 mc = np.sum(links) - min_mc = np.sum(links[:nc - 1]) # minimal weight + min_mc = np.sum(links[:nc - 1]) # minimal weight dc = (mc - min_mc) / (nc * (nc - 1) / - 2 - min_mc) # community density + 2 - min_mc) # community density if np.array(dc).shape is not (): print(dc) @@ -368,14 +359,7 @@ def link_communities(W, type_clustering='single'): Mc[i, j] = mc Dc[i, j] = dc if not np.isnan(dc) else 0 - #time2 = time.time() - #print('compute densities time', time2-time1) - - C[i + 1, :] = C[i, :] # copy current partition - - #if i in (2693,): - # import pdb - # pdb.set_trace() + C[i + 1, :] = C[i, :] # copy current partition # Profiling and debugging show that this line, finding # the max values in this matrix, take about 3x longer than the @@ -393,20 +377,14 @@ def link_communities(W, type_clustering='single'): u1 = uc u2 = ud - #time25 = time.time() - #print('copy and max time', time25-time2) - # get unique links (implementation of matlab sortrows) - #ugl = np.array((u1,u2)) ugl = np.sort((u1, u2), axis=1) ug_rows = ugl[np.argsort(ugl, axis=0)[:, 0]] + # implementation of matlab unique(A, 'rows') unq_rows = np.vstack({tuple(row) for row in ug_rows}) V = U[unq_rows] - #time3 = time.time() - #print('sortrows time', time3-time25) - for j in range(len(V)): if type_clustering == 'single': x = np.max(ES[V[j, :], :], axis=0) @@ -427,23 +405,11 @@ def link_communities(W, type_clustering='single'): C[i + 1, C[i + 1, :] == V[j, 1]] = V[j, 0] V[V == V[j, 1]] = V[j, 0] - #time4 = time.time() - #print('get linkages time', time4-time3) - U = np.unique(C[i + 1, :]) if len(U) == 1: break + # ENDT HAIERARKIKL CLUSTRRINNG - #time5 = time.time() - #print('get unique communities time', time5-time4) - - #ENDT HAIERARKIKL CLUSTRRINNG - #ENDT HAIERARKIKL CLUSTRRINNG - #ENDT HAIERARKIKL CLUSTRRINNG - #ENDT HAIERARKIKL CLUSTRRINNG - #ENDT HAIERARKIKL CLUSTRRINNG - - #Dc[ np.where(np.isnan(Dc)) ]=0 i = np.argmax(np.sum(Dc * Mc, axis=1)) U = np.unique(C[i, :]) @@ -526,10 +492,7 @@ def recur(module): (np.dot(modmat, mod_asgn_iter)) qmax = np.max(q_iter * it) imax = np.argmax(q_iter * it) - #imax, = np.where(q_iter == qmax) - #if len(imax) > 0: - # imax = imax[0] - # print(imax) + # does switching increase modularity? mod_asgn_iter[imax] *= -1 it[imax] = np.ma.masked @@ -696,7 +659,6 @@ def modularity_finetune_und(W, ci=None, gamma=1, seed=None): ''' np.random.seed(seed) - #import time n = len(W) # number of nodes if ci is None: ci = np.arange(n) + 1 @@ -956,8 +918,9 @@ def modularity_louvain_dir(W, gamma=1, hierarchy=False, seed=None): while flag: it += 1 if it > 1000: - raise BCTParamError('Modularity Infinite Loop Style F. Please ' - 'contact the developer with this error.') + raise BCTParamError('Modularity Infinite Loop Style F. ' + 'Please contact the developer with ' + 'this error.') flag = False # loop over nodes in random order @@ -1067,10 +1030,6 @@ def modularity_louvain_und(W, gamma=1, hierarchy=False, seed=None): q.append(-1) # hierarchical modularity values n0 = n - #knm = np.zeros((n,n)) - # for j in np.xrange(n0+1): - # knm[:,j] = np.sum(w[;, - while True: if h > 300: raise BCTParamError('Modularity Infinite Loop Style B. Please ' @@ -1086,8 +1045,9 @@ def modularity_louvain_und(W, gamma=1, hierarchy=False, seed=None): while flag: it += 1 if it > 1000: - raise BCTParamError('Modularity Infinite Loop Style C. Please ' - 'contact the developer with this error.') + raise BCTParamError('Modularity Infinite Loop Style C. ' + 'Please contact the developer with ' + 'this error.') flag = False # loop over nodes in random order @@ -1246,7 +1206,8 @@ def modularity_louvain_und_sign(W, gamma=1, qtype='sta', seed=None): it += 1 if it > 1000: raise BCTParamError('Infinite Loop was detected and stopped. ' - 'This was probably caused by passing in a directed matrix.') + 'This was probably caused by passing in a ' + 'directed matrix.') flag = False # loop over nodes in random order for u in np.random.permutation(nh): @@ -1515,9 +1476,7 @@ def recur(module): (np.dot(modmat, mod_asgn_iter)) qmax = np.max(q_iter * it) imax = np.argmax(q_iter * it) - #imax, = np.where(q_iter == qmax) - #if len(imax) > 1: - # imax = imax[0] + # does switching increase modularity? mod_asgn_iter[imax] *= -1 it[imax] = np.ma.masked @@ -1591,8 +1550,6 @@ def modularity_und_sign(W, ci, qtype='sta'): Kn0 = np.sum(Knm0, axis=1) # positive node degree Kn1 = np.sum(Knm1, axis=1) # negative node degree - Km0 = np.sum(Knm0, axis=0) # positive module degree - Km1 = np.sum(Knm1, axis=0) # negaitve module degree if qtype == 'smp': d0 = 1 / s0 diff --git a/bct/algorithms/motifs.py b/bct/algorithms/motifs.py index 8c734b4..181f3ff 100644 --- a/bct/algorithms/motifs.py +++ b/bct/algorithms/motifs.py @@ -724,10 +724,9 @@ def motif4struct_bin(A): np.append(np.zeros((vz,)), As[u, vz + 1:n + 1]), V3) for v3 in np.where(V3)[0]: - a = np.array((A[v1, u], A[v2, u], A[v3, u], A[u, v1], A[v2, v1], - A[v3, v1], A[u, v2], A[v1, v2], A[ - v3, v2], A[u, v3], A[v1, v3], - A[v2, v3])) + a = np.array((A[v1, u], A[v2, u], A[v3, u], A[u, v1], + A[v2, v1], A[v3, v1], A[u, v2], A[v1, v2], + A[v3, v2], A[u, v3], A[v1, v3], A[v2, v3])) s = np.uint64( np.sum(np.power(10, np.arange(11, -1, -1)) * a)) @@ -804,19 +803,17 @@ def motif4struct_wei(W): V3 = np.logical_or( np.append(np.zeros((vz,)), As[u, vz + 1:n + 1]), V3) for v3 in np.where(V3)[0]: - a = np.array((A[v1, u], A[v2, u], A[v3, u], A[u, v1], A[v2, v1], - A[v3, v1], A[u, v2], A[v1, v2], A[ - v3, v2], A[u, v3], A[v1, v3], - A[v2, v3])) + a = np.array((A[v1, u], A[v2, u], A[v3, u], A[u, v1], + A[v2, v1], A[v3, v1], A[u, v2], A[v1, v2], + A[v3, v2], A[u, v3], A[v1, v3], A[v2, v3])) s = np.uint64( np.sum(np.power(10, np.arange(11, -1, -1)) * a)) # print np.shape(s),np.shape(m4n) ix = np.squeeze(s == m4n) - w = np.array((W[v1, u], W[v2, u], W[v3, u], W[u, v1], W[v2, v1], - W[v3, v1], W[u, v2], W[v1, v2], W[ - v3, v2], W[u, v3], W[v1, v3], - W[v2, v3])) + w = np.array((W[v1, u], W[v2, u], W[v3, u], W[u, v1], + W[v2, v1], W[v3, v1], W[u, v2], W[v1, v2], + W[v3, v2], W[u, v3], W[v1, v3], W[v2, v3])) M = w * m4[ix, :] id = id4[ix] - 1 diff --git a/bct/algorithms/reference.py b/bct/algorithms/reference.py index c230fed..602ad66 100644 --- a/bct/algorithms/reference.py +++ b/bct/algorithms/reference.py @@ -82,7 +82,9 @@ def latmio_dir_connected(R, itr, D=None): # rewiring condition if not (R[a, d] or R[c, b]): # lattice condition - if (D[a, b] * R[a, b] + D[c, d] * R[c, d] >= D[a, d] * R[a, b] + D[c, b] * R[c, d]): + if (D[a, b] * R[a, b] + D[c, d] * R[c, d] >= + D[a, d] * R[a, b] + D[c, b] * R[c, d]): + # connectedness condition if not (np.any((R[a, c], R[d, b], R[d, c])) and np.any((R[c, a], R[b, d], R[b, a]))): @@ -197,7 +199,9 @@ def latmio_dir(R, itr, D=None): # rewiring condition if not (R[a, d] or R[c, b]): # lattice condition - if (D[a, b] * R[a, b] + D[c, d] * R[c, d] >= D[a, d] * R[a, b] + D[c, b] * R[c, d]): + if (D[a, b] * R[a, b] + D[c, d] * R[c, d] >= + D[a, d] * R[a, b] + D[c, b] * R[c, d]): + R[a, d] = R[a, b] R[a, b] = 0 R[c, b] = R[c, d] @@ -305,7 +309,9 @@ def latmio_und_connected(R, itr, D=None): # rewiring condition if not (R[a, d] or R[c, b]): # lattice condition - if (D[a, b] * R[a, b] + D[c, d] * R[c, d] >= D[a, d] * R[a, b] + D[c, b] * R[c, d]): + if (D[a, b] * R[a, b] + D[c, d] * R[c, d] >= + D[a, d] * R[a, b] + D[c, b] * R[c, d]): + # connectedness condition if not (R[a, c] or R[b, d]): P = R[(a, d), :].copy() @@ -427,7 +433,9 @@ def latmio_und(R, itr, D=None): # rewiring condition if not (R[a, d] or R[c, b]): # lattice condition - if (D[a, b] * R[a, b] + D[c, d] * R[c, d] >= D[a, d] * R[a, b] + D[c, b] * R[c, d]): + if (D[a, b] * R[a, b] + D[c, d] * R[c, d] >= + D[a, d] * R[a, b] + D[c, b] * R[c, d]): + R[a, d] = R[a, b] R[a, b] = 0 R[d, a] = R[b, a] @@ -800,8 +808,8 @@ def maketoeplitzCIJ(n, k, s): itr += 1 if itr > 10000: raise BCTParamError('Infinite loop was caught generating toeplitz ' - 'matrix. This means the matrix could not be resolved with the ' - 'specified parameters.') + 'matrix. This means the matrix could not be ' + 'resolved with the specified parameters.') return CIJ @@ -852,7 +860,8 @@ def null_model_dir_sign(W, bin_swaps=5, wei_freq=.1): W = W.copy() n = len(W) np.fill_diagonal(W, 0) # clear diagonal - Ap = (W > 0) # positive adjmat + Ap = (W > 0) # positive adjmat + An = (W < 0) # negative adjmat if np.size(np.where(Ap.flat)) < (n * (n - 1)): W_r = randmio_und_signed(W, bin_swaps) @@ -873,9 +882,9 @@ def null_model_dir_sign(W, bin_swaps=5, wei_freq=.1): Si = np.sum(W * Acur, axis=0) # positive in-strength So = np.sum(W * Acur, axis=1) # positive out-strength - Wv = np.sort(W[Acur].flat) # sorted weights vector + Wv = np.sort(W[Acur].flat) # sorted weights vector i, j = np.where(A_rcur) - Lij, = np.where(A_rcur.flat) # weights indices + Lij, = np.where(A_rcur.flat) # weights indices P = np.outer(So, Si) @@ -973,7 +982,8 @@ def null_model_und_sign(W, bin_swaps=5, wei_freq=.1): W = W.copy() n = len(W) np.fill_diagonal(W, 0) # clear diagonal - Ap = (W > 0) # positive adjmat + Ap = (W > 0) # positive adjmat + An = (W > 0) # positive adjmat if np.size(np.where(Ap.flat)) < (n * (n - 1)): W_r = randmio_und_signed(W, bin_swaps) @@ -1210,8 +1220,8 @@ def randmio_und_connected(R, itr): to reach every other node in the network. The input network for this function must be connected. - NOTE the changes to the BCT matlab function of the same name - made in the Jan 2016 release + NOTE the changes to the BCT matlab function of the same name + made in the Jan 2016 release have not been propagated to this function because of substantially decreased time efficiency in the implementation. Expect these changes to be merged eventually. @@ -1342,36 +1352,26 @@ def randmio_dir_signed(R, itr): itr *= n * (n - 1) - #maximal number of rewiring attempts per iter + # maximal number of rewiring attempts per iter max_attempts = n - #actual number of successful rewirings + # actual number of successful rewirings eff = 0 - #print(itr) - for it in range(int(itr)): - #print(it) att = 0 while att <= max_attempts: - #select four distinct vertices - + # select four distinct vertices a, b, c, d = pick_four_unique_nodes_quickly(n) - #a, b, c, d = np.random.choice(n, 4) - #a, b, c, d = np.random.permutation(4) - r0_ab = R[a, b] r0_cd = R[c, d] r0_ad = R[a, d] r0_cb = R[c, b] - #print(np.sign(r0_ab), np.sign(r0_ad)) - - #rewiring condition - if ( np.sign(r0_ab) == np.sign(r0_cd) and - np.sign(r0_ad) == np.sign(r0_cb) and - np.sign(r0_ab) != np.sign(r0_ad)): - + # rewiring condition + if (np.sign(r0_ab) == np.sign(r0_cd) and + np.sign(r0_ad) == np.sign(r0_cb) and + np.sign(r0_ab) != np.sign(r0_ad)): R[a, d] = r0_ab R[a, b] = r0_ad @@ -1383,10 +1383,9 @@ def randmio_dir_signed(R, itr): att += 1 - #print(eff) - return R, eff + def randmio_und(R, itr): ''' This function randomizes an undirected network, while preserving the @@ -1486,7 +1485,7 @@ def randmio_und_signed(R, itr): R = R.copy() n = len(R) - itr *= int(n * (n -1) / 2) + itr *= int(n * (n - 1) / 2) max_attempts = int(np.round(n / 2)) eff = 0 @@ -1502,11 +1501,11 @@ def randmio_und_signed(R, itr): r0_ad = R[a, d] r0_cb = R[c, b] - #rewiring condition - if ( np.sign(r0_ab) == np.sign(r0_cd) and - np.sign(r0_ad) == np.sign(r0_cb) and - np.sign(r0_ab) != np.sign(r0_ad)): - + # rewiring condition + if (np.sign(r0_ab) == np.sign(r0_cd) and + np.sign(r0_ad) == np.sign(r0_cb) and + np.sign(r0_ab) != np.sign(r0_ad)): + R[a, d] = R[d, a] = r0_ab R[a, b] = R[b, a] = r0_ad @@ -1520,6 +1519,7 @@ def randmio_und_signed(R, itr): return R, eff + def randomize_graph_partial_und(A, B, maxswap): ''' A = RANDOMIZE_GRAPH_PARTIAL_UND(A,B,MAXSWAP) takes adjacency matrices A diff --git a/bct/nbs.py b/bct/nbs.py index a2a52f1..d75b5c7 100644 --- a/bct/nbs.py +++ b/bct/nbs.py @@ -24,7 +24,7 @@ def nbs_bct(x, y, thresh, k=1000, tail='both', paired=False, verbose=False): thresh : float minimum t-value used as threshold k : int - number of permutations used to estimate the empirical null + number of permutations used to estimate the empirical null distribution tail : {'left', 'right', 'both'} enables specification of particular alternative hypothesis @@ -49,42 +49,42 @@ def nbs_bct(x, y, thresh, k=1000, tail='both', paired=False, verbose=False): an adjacency matrix identifying the edges comprising each component. edges are assigned indexed values. null : Kx1 np.ndarray - A vector of K sampled from the null distribution of maximal component + A vector of K sampled from the null distribution of maximal component size. Notes ----- - ALGORITHM DESCRIPTION - The NBS is a nonparametric statistical test used to isolate the - components of an N x N undirected connectivity matrix that differ - significantly between two distinct populations. Each element of the - connectivity matrix stores a connectivity value and each member of - the two populations possesses a distinct connectivity matrix. A - component of a connectivity matrix is defined as a set of - interconnected edges. - - The NBS is essentially a procedure to control the family-wise error - rate, in the weak sense, when the null hypothesis is tested + ALGORITHM DESCRIPTION + The NBS is a nonparametric statistical test used to isolate the + components of an N x N undirected connectivity matrix that differ + significantly between two distinct populations. Each element of the + connectivity matrix stores a connectivity value and each member of + the two populations possesses a distinct connectivity matrix. A + component of a connectivity matrix is defined as a set of + interconnected edges. + + The NBS is essentially a procedure to control the family-wise error + rate, in the weak sense, when the null hypothesis is tested independently at each of the N(N-1)/2 edges comprising the undirected - connectivity matrix. The NBS can provide greater statistical power - than conventional procedures for controlling the family-wise error + connectivity matrix. The NBS can provide greater statistical power + than conventional procedures for controlling the family-wise error rate, such as the false discovery rate, if the set of edges at which the null hypothesis is rejected constitues a large component or components. The NBS comprises fours steps: 1. Perform a two-sample T-test at each edge indepedently to test the hypothesis that the value of connectivity between the two - populations come from distributions with equal means. + populations come from distributions with equal means. 2. Threshold the T-statistic available at each edge to form a set of - suprathreshold edges. + suprathreshold edges. 3. Identify any components in the adjacency matrix defined by the set - of suprathreshold edges. These are referred to as observed - components. Compute the size of each observed component - identified; that is, the number of edges it comprises. + of suprathreshold edges. These are referred to as observed + components. Compute the size of each observed component + identified; that is, the number of edges it comprises. 4. Repeat K times steps 1-3, each time randomly permuting members of - the two populations and storing the size of the largest component + the two populations and storing the size of the largest component identified for each permuation. This yields an empirical estimate - of the null distribution of maximal component size. A corrected + of the null distribution of maximal component size. A corrected p-value for each observed component is then calculated using this null distribution. @@ -110,7 +110,6 @@ def ttest2_stat_only(x, y, tail): def ttest_paired_stat_only(A, B, tail): n = len(A - B) - df = n - 1 sample_ss = np.sum((A - B)**2) - np.sum(A - B)**2 / n unbiased_std = np.sqrt(sample_ss / (n - 1)) z = np.mean(A - B) / unbiased_std @@ -242,12 +241,12 @@ def ttest_paired_stat_only(A, B, tail): hit += 1 if verbose: - print(('permutation %i of %i. Permutation max is %s. Observed max' - ' is %s. P-val estimate is %.3f') % ( + print(('permutation %i of %i. Permutation max is %s. ' + 'Observed max is %s. P-val estimate is %.3f') % ( u, k, null[u], max_sz, hit / (u + 1))) elif (u % (k / 10) == 0 or u == k - 1): - print('permutation %i of %i. p-value so far is %.3f' % (u, k, - hit / (u + 1))) + print('permutation %i of %i. p-value so far is %.3f' % ( + u, k, hit / (u + 1))) pvals = np.zeros((nr_components,)) # calculate p-vals diff --git a/bct/utils/other.py b/bct/utils/other.py index 1e611e3..5f39e35 100644 --- a/bct/utils/other.py +++ b/bct/utils/other.py @@ -83,26 +83,25 @@ def threshold_proportional(W, p, copy=True): raise BCTParamError('Threshold must be in range [0,1]') if copy: W = W.copy() - n = len(W) # number of nodes - np.fill_diagonal(W, 0) # clear diagonal + n = len(W) # number of nodes + np.fill_diagonal(W, 0) # clear diagonal - if np.allclose(W, W.T): # if symmetric matrix - W[np.tril_indices(n)] = 0 # ensure symmetry is preserved - ud = 2 # halve number of removed links + if np.allclose(W, W.T): # if symmetric matrix + W[np.tril_indices(n)] = 0 # ensure symmetry is preserved + ud = 2 # halve number of removed links else: ud = 1 - ind = np.where(W) # find all links + ind = np.where(W) # find all links - I = np.argsort(W[ind])[::-1] # sort indices by magnitude + I = np.argsort(W[ind])[::-1] # sort indices by magnitude - en = int(round((n * n - n) * p / ud)) # number of links to be preserved + en = int(round((n * n - n) * p / ud)) # number of links to be preserved W[(ind[0][I][en:], ind[1][I][en:])] = 0 # apply threshold - #W[np.ix_(ind[0][I][en:], ind[1][I][en:])]=0 - if ud == 2: # if symmetric matrix - W[:, :] = W + W.T # reconstruct symmetry + if ud == 2: # if symmetric matrix + W[:, :] = W + W.T # reconstruct symmetry return W diff --git a/bct/utils/visualization.py b/bct/utils/visualization.py index 7ea029b..50bc1cf 100644 --- a/bct/utils/visualization.py +++ b/bct/utils/visualization.py @@ -57,22 +57,9 @@ def adjacency_plot_und(A, coor, tube=False): from mayavi import mlab n = len(A) - nr_edges = (n * n - 1) // 2 - - #starts = np.zeros((nr_edges,3)) - #vecs = np.zeros((nr_edges,3)) - #adjdat = np.zeros((nr_edges,)) ixes, = np.where(np.triu(np.ones((n, n)), 1).flat) - # i=0 - # for r2 in xrange(n): - # for r1 in xrange(r2): - # starts[i,:] = coor[r1,:] - # vecs[i,:] = coor[r2,:] - coor[r1,:] - # adjdat[i,:] - # i+=1 - adjdat = A.flat[ixes] A_r = np.tile(coor, (n, 1, 1)) @@ -200,7 +187,8 @@ def align_matrices(m1, m2, dfun='sqrdiff', verbose=False, H=1e6, Texp=1, elif dfun == 'cosang': maxcost = np.pi / 2 lowcost = np.arccos(np.dot(m1.flat, m2.flat) / - np.sqrt(np.dot(m1.flat, m1.flat) * np.dot(m2.flat, m2.flat))) / maxcost + np.sqrt(np.dot(m1.flat, m1.flat) * + np.dot(m2.flat, m2.flat))) / maxcost else: raise BCTParamError('dfun must be absdiff or sqrdiff or cosang') @@ -299,7 +287,8 @@ def backbone_wu(CIJ, avgdeg): n = len(CIJ) if not np.all(CIJ == CIJ.T): raise BCTParamError('backbone_wu can only be computed for undirected ' - 'matrices. If your matrix is has noise, correct it with np.around') + 'matrices. If your matrix is has noise, correct ' + 'it with np.around') CIJtree = np.zeros((n, n)) # find strongest edge (if multiple edges are tied, use only first one) @@ -716,8 +705,8 @@ def reorder_mod(A, ci): # reverse mod_imp to sort by the first column first and so on # print ksmi # for i,sin in enumerate(signs): - # if sin==-1: - # ksmi[i,:]=ksmi[i,:][::-1] + # if sin==-1: + # ksmi[i,:]=ksmi[i,:][::-1] # print ksmi # print np.shape(ksmi) diff --git a/test/basic_tests.py b/test/basic_tests.py index 001724a..7b2aed4 100644 --- a/test/basic_tests.py +++ b/test/basic_tests.py @@ -1,4 +1,4 @@ -from load_samples import * +from load_samples import load_sample, load_directed_sample import bct import numpy as np @@ -30,7 +30,6 @@ def test_threshold_absolute(): def test_strengths_und(): x = load_sample() - s = bct.strengths_und(x) assert np.allclose(np.sum(x), 38967.38702018) diff --git a/test/clustering_tests.py b/test/clustering_tests.py index aa0b73d..0e0cfe0 100644 --- a/test/clustering_tests.py +++ b/test/clustering_tests.py @@ -1,4 +1,6 @@ -from load_samples import * +from load_samples import load_sample, load_signed_sample, \ + load_sparse_sample, load_directed_low_modularity_sample, \ + load_binary_directed_low_modularity_sample import numpy as np import bct @@ -37,11 +39,9 @@ def test_transitivity_signed(): def test_component(): - from scipy import stats x = load_sparse_sample() c1, cs1 = bct.get_components(x) - print(np.max(c1), 19) assert np.max(c1) == 19 diff --git a/test/core_tests.py b/test/core_tests.py index a4b9952..9e00f9a 100644 --- a/test/core_tests.py +++ b/test/core_tests.py @@ -1,4 +1,4 @@ -from load_samples import * +from load_samples import load_sample import bct import numpy as np diff --git a/test/distance_tests.py b/test/distance_tests.py index 15ce99a..235860a 100644 --- a/test/distance_tests.py +++ b/test/distance_tests.py @@ -1,4 +1,4 @@ -from load_samples import * +from load_samples import load_sample import numpy as np import bct diff --git a/test/load_samples.py b/test/load_samples.py index 3d79fa7..bd6546f 100644 --- a/test/load_samples.py +++ b/test/load_samples.py @@ -3,7 +3,10 @@ import os MAT_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'mats') -mat_path = lambda fname: os.path.join(MAT_DIR, fname) + + +def mat_path(fname): + return os.path.join(MAT_DIR, fname) def load_sample(thres=1): @@ -92,5 +95,5 @@ def compose(*functions): return functools.reduce(lambda f, g: lambda x: f(g(x)), functions) thresh_fun = functools.partial(bct.threshold_proportional, p=.5) return np.transpose(list(map(compose(bct.normalize, thresh_fun), - (f[:, :, i] for i in range(f.shape[2])))), - (1, 2, 0)) + (f[:, :, i] for i in range(f.shape[2])))), + (1, 2, 0)) diff --git a/test/modularity_derived_metrics_test.py b/test/modularity_derived_metrics_test.py index 95057e1..7118c5d 100644 --- a/test/modularity_derived_metrics_test.py +++ b/test/modularity_derived_metrics_test.py @@ -1,4 +1,4 @@ -from load_samples import * +from load_samples import mat_path, load_sample import numpy as np import bct @@ -56,7 +56,7 @@ def gateway_test(): print(np.sum(g_pos_bet), 43.4026) assert np.allclose(np.sum(g_pos_bet), 43.4026, atol=.001) - + def test_zi(): x = load_sample(thres=.4) diff --git a/test/modularity_tests.py b/test/modularity_tests.py index 91cfaff..a8c9a7e 100644 --- a/test/modularity_tests.py +++ b/test/modularity_tests.py @@ -1,4 +1,5 @@ -from load_samples import * +from load_samples import load_sample, load_signed_sample, \ + load_directed_low_modularity_sample, load_directed_sample import numpy as np import bct @@ -80,7 +81,7 @@ def test_modularity_finetune_und(): # also the matlab and python versions of modularity_und return slightly # different modular structure, but the instability is present despite this - #(i.e. it is unstable both when the modular structure is identical and not) + # (ie. it is unstable both when the modular structure is identical and not) def test_modularity_louvain_und_sign_seed(): @@ -152,12 +153,12 @@ def test_modularity_louvain_dir_low_modularity(): assert np.allclose(q, .06934894) # def test_modularity_finetune_dir_low_modularity(): -# x = load_directed_low_modularity_sample(thres=.67) -# seed = 39602351 -# ci,oq = bct.modularity_louvain_dir(x, seed=seed) -# _,q = bct.modularity_finetune_dir(x, ci=ci, seed=seed) -# print q,oq -# assert q >= oq +# x = load_directed_low_modularity_sample(thres=.67) +# seed = 39602351 +# ci,oq = bct.modularity_louvain_dir(x, seed=seed) +# _,q = bct.modularity_finetune_dir(x, ci=ci, seed=seed) +# print q,oq +# assert q >= oq # this does not pass. the matlab code appears to have no idea what to do # with # the low modularity directed modules. this may be someone else's fault. @@ -177,13 +178,13 @@ def test_modularity_louvain_dir(): assert np.allclose(q, .32697921) # def test_modularity_finetune_dir(): -# x = load_directed_sample() -# seed = 26080 -# ci,oq = bct.modularity_louvain_dir(x, seed=seed) -# for i in xrange(100): -# _,q = bct.modularity_finetune_dir(x, ci=ci) -# print q,oq -# assert q >= oq +# x = load_directed_sample() +# seed = 26080 +# ci,oq = bct.modularity_louvain_dir(x, seed=seed) +# for i in xrange(100): +# _,q = bct.modularity_finetune_dir(x, ci=ci) +# print q,oq +# assert q >= oq # this does not pass with similar behavior to low modularity. # the code occasionally returns lower modularity (but very very similar, # order .001) partitions despite returning diff --git a/test/nbs_tests.py b/test/nbs_tests.py index 98ee72d..679d133 100644 --- a/test/nbs_tests.py +++ b/test/nbs_tests.py @@ -1,4 +1,5 @@ -from load_samples import * +from load_samples import load_sample_group_qball, \ + load_sample_group_dsi, load_sample_group_fmri import numpy as np import bct @@ -26,8 +27,6 @@ def test_nbs_paired_dsi_fmri(): def _nbs_helper(x, y, expected_pval, atol=.05, thresh=.1, ntrials=25, paired=False): - # comment - pval, _, _ = bct.nbs_bct(x, y, thresh, k=ntrials, paired=paired) print(pval, expected_pval) assert np.allclose(pval, expected_pval, atol=atol) diff --git a/test/nodals_tests.py b/test/nodals_tests.py index f7c97a0..8e7da72 100644 --- a/test/nodals_tests.py +++ b/test/nodals_tests.py @@ -1,4 +1,4 @@ -from load_samples import * +from load_samples import load_sample import numpy as np import bct diff --git a/test/partition_distance_test.py b/test/partition_distance_test.py index f712454..5f96e0d 100644 --- a/test/partition_distance_test.py +++ b/test/partition_distance_test.py @@ -1,4 +1,4 @@ -from load_samples import * +from load_samples import load_sample_group_qball, load_sample_group_dsi import numpy as np import bct diff --git a/test/very_long_tests.py b/test/very_long_tests.py index 0b52777..dc7eb9d 100644 --- a/test/very_long_tests.py +++ b/test/very_long_tests.py @@ -1,4 +1,4 @@ -from load_samples import * +from load_samples import load_sample import numpy as np import bct