零、写在前面
这篇博客总结了Brain Connectivity Toolbox中关于计算图的度
相关的方法。
包括:
- 有向图
- 有向图中节点的入度、出度和度数
- 有向图中节点的强度(出强度、入强度)
- 无向图
- 无向图中节点的度数
- 无向图中节点的强度(区分正权重和负权重的边)
- 联合度矩阵(网络中节点的度分布特性的矩阵)
一、有向图
1. 计算有向图中节点的入度、出度和度数
在有向图中,节点的度数为入度 + 出度。
代码实现
import numpy as np
def degrees_dir(CIJ):
'''
计算有向图中节点的入度、出度和度数。
参数:
----------
CIJ : NxN numpy.ndarray
有向图的邻接矩阵,可以是二进制或带权重的。
返回:
-------
id : Nx1 numpy.ndarray
节点的入度数组。
od : Nx1 numpy.ndarray
节点的出度数组。
deg : Nx1 numpy.ndarray
节点的度数数组(入度 + 出度)。
注意:
-----
输入的CIJ矩阵的列代表节点,权重信息将被忽略。
'''
# 确保CIJ矩阵是二进制的,如果CIJ不是二进制的,将进行转换
CIJ = binarize(CIJ, copy=True)
# 计算入度:CIJ矩阵的每列求和,得到节点的入度
id = np.sum(CIJ, axis=0)
# 计算出度:CIJ矩阵的每行求和,得到节点的出度
od = np.sum(CIJ, axis=1)
# 计算度数:入度加上出度
deg = id + od
return id, od, deg
# 使用实例:
# 假设我们有一个3x3的有向图邻接矩阵,其中1表示存在边,0表示不存在边
CIJ_example = np.array([
[0, 1, 0], # 节点0没有出边,节点1有一个入边
[0, 0, 1], # 节点1有一个出边到节点2
[1, 0, 0] # 节点2有一个入边从节点0
])
# 调用degrees_dir函数计算入度、出度和度数
in_degrees, out_degrees, degrees = degrees_dir(CIJ_example)
# 打印结果
print("In-degrees:", in_degrees) # 入度 array([1, 1, 1])
print("Out-degrees:", out_degrees) # 出度 array([1, 1, 1])
print("Degrees:", degrees) # 度数 array([2, 2, 2])
2. 计算有向图中节点的强度
入强度是指向该节点的边权重之和(axis=0)
出强度是从此节点出发的边权重之和(axis=1)
代码实现
import numpy as np
def strengths_dir(CIJ):
'''
计算有向图中节点的强度。节点的强度是与节点相连的边的权重之和。
入强度是指向该节点的边权重之和,出强度是从此节点出发的边权重之和。
参数:
----------
CIJ : NxN np.ndarray
有向图的加权邻接矩阵
返回:
-------
is : Nx1 np.ndarray
节点的入强度数组
os : Nx1 np.ndarray
节点的出强度数组
str : Nx1 np.ndarray
节点的强度数组(入强度 + 出强度)
注意:
-----
输入的CIJ矩阵的列代表节点。
'''
# 计算入强度:CIJ矩阵的每列求和,得到每个节点的入强度
istr = np.sum(CIJ, axis=0)
# 计算出强度:CIJ矩阵的每行求和,得到每个节点的出强度
ostr = np.sum(CIJ, axis=1)
# 计算节点的总强度:入强度加上出强度
return istr + ostr
# 使用实例:
# 假设我们有一个3x3的有向图邻接矩阵,权重表示边的权重
CIJ_example = np.array([
[0, 1, 0], # 节点0没有出边,但有1条边从节点1指向它,权重为1
[0, 0, 2], # 节点1没有出边,但有1条边从节点2指向它,权重为2
[3, 0, 0] # 节点2有1条出边到节点0,权重为3
])
# 调用strengths_dir函数计算节点的强度
strengths = strengths_dir(CIJ_example)
# 打印结果
print("In-strengths of each node:", strengths[0]) # 入强度
print("Out-strengths of each node:", strengths[1]) # 出强度
print("Total strengths of each node:", strengths[2]) # 总强度
二、无向图
1. 计算无向图中节点的度数
在无向图中,节点的度数是与节点相连的边的数量。
代码实现
import numpy as np
def degrees_und(CIJ):
'''
计算无向图中节点的度数。节点的度数是与节点相连的边的数量。
参数:
----------
CIJ : NxN np.ndarray
无向图的邻接矩阵,可以是二进制或带权重的。
返回:
-------
deg : Nx1 np.ndarray
节点的度数数组。
注意:
-----
权重信息将被忽略,只考虑边的存在。
'''
# 确保CIJ矩阵是二进制的,如果CIJ不是二进制的,将进行转换
CIJ = binarize(CIJ, copy=True)
# 计算度数:对于无向图,度数是CIJ矩阵每一行(或每一列,因为图是无向的)
# 元素的总和,这里使用axis=0来计算每列的和,也就是每个节点的度数
return np.sum(CIJ, axis=0)
# 使用实例:
# 假设我们有一个3x3的无向图邻接矩阵,其中1表示存在边,0表示不存在边
CIJ_example = np.array([
[0, 1, 1], # 节点0与节点1和节点2相连
[1, 0, 1], # 节点1与节点0和节点2相连
[1, 1, 0] # 节点2与节点0和节点1相连
])
# 调用degrees_und函数计算节点的度数
degrees = degrees_und(CIJ_example) # array([2, 2, 2])
# 打印结果
print("Degrees of each node:", degrees)
2. 计算无向图中节点的强度
在无向图中,节点的强度是与节点相连的所有边的权重之和。
代码实现
import numpy as np
def strengths_und(CIJ):
'''
计算无向图中节点的强度。节点的强度是与节点相连的所有边的权重之和。
参数:
----------
CIJ : NxN np.ndarray
无向图的加权邻接矩阵
返回:
-------
str : Nx1 np.ndarray
节点的强度数组
'''
# 计算节点的强度:对于无向图,节点的强度等于CIJ矩阵每一列(或行,因为图是无向的)
# 的元素权重之和。这里使用axis=0来计算每列的和,也就是每个节点的强度
return np.sum(CIJ, axis=0)
# 使用实例:
# 假设我们有一个3x3的无向图邻接矩阵,权重表示边的权重
CIJ_example = np.array([
[0, 2, 1], # 节点0与节点1相连的边权重为2,与节点2相连的边权重为1
[2, 0, 3], # 节点1与节点0相连的边权重为2,与节点2相连的边权重为3
[1, 3, 0] # 节点2与节点0相连的边权重为1,与节点1相连的边权重为3
])
# 调用strengths_und函数计算节点的强度
node_strengths = strengths_und(CIJ_example)
# 打印结果
print("Strengths of each node:", node_strengths)
3. 计算无向图中节点的强度,区分正权重和负权重的边
import numpy as np
def strengths_und_sign(W):
'''
计算无向图中节点的强度,区分正权重和负权重的边。
参数:
----------
W : NxN np.ndarray
无向连接矩阵,包含正权重和负权重的边
返回:
-------
Spos : Nx1 np.ndarray
正权重边的节点强度数组
Sneg : Nx1 np.ndarray
负权重边的节点强度数组
vpos : float
正权重边的总权重
vneg : float
负权重边的总权重
'''
W = W.copy() # 复制W矩阵,避免修改原始矩阵
n = len(W) # 矩阵的大小,即节点的数量
np.fill_diagonal(W, 0) # 将对角线元素设为0,因为自环没有权重
# 计算正权重边的节点强度:W中元素大于0的条件下的元素乘积后求和,对每列进行操作
Spos = np.sum(W * (W > 0), axis=0)
# 计算负权重边的节点强度:W中元素小于0的条件下的元素乘积后求和,对每列进行操作
Sneg = np.sum(W * (W < 0), axis=0)
# 计算总的正权重:W中所有大于0的元素的总和
vpos = np.sum(W[W > 0])
# 计算总的负权重:W中所有小于0的元素的总和
vneg = np.sum(W[W < 0])
return Spos, Sneg, vpos, vneg
# 使用实例:
# 假设我们有一个3x3的无向图加权邻接矩阵,权重可以是正数或负数
W_example = np.array([
[0, 2, -1], # 节点0与节点1相连的边权重为2,与节点2相连的边权重为-1
[2, 0, 3], # 节点1与节点0相连的边权重为2,与节点2相连的边权重为3
[-1, 3, 0] # 节点2与节点0相连的边权重为-1,与节点1相连的边权重为3
])
# 调用strengths_und_sign函数计算正负权重的节点强度和总权重
positive_strengths, negative_strengths, total_positive_weight, total_negative_weight = strengths_und_sign(W_example)
# 打印结果
print("Positive strengths of each node:", positive_strengths)
# array([2, 5, 3])
print("Negative strengths of each node:", negative_strengths)
# array([-1, 0, -1])
print("Total positive weight:", total_positive_weight)
# 10
print("Total negative weight:", total_negative_weight)
# -2
其他
1. 将输入的带权重的连接矩阵转换为二进制矩阵
def binarize(W, copy=True):
'''
将输入的带权重的连接矩阵转换为二进制矩阵。如果copy参数没有设置,这个函数将就地修改W。
参数:
----------
W : NxN np.ndarray
带权重的连接矩阵。
copy : bool
如果为True,则返回矩阵的副本。否则,将就地修改矩阵。默认值为True。
返回:
-------
W : NxN np.ndarray
二进制连接矩阵。
'''
if copy: # 如果copy参数为True,则复制W矩阵
W = W.copy() # 复制W矩阵,以避免就地修改原始数据
# 将W矩阵中所有非零元素设置为1,实现二值化
W[W != 0] = 1
return W # 返回修改后的二进制矩阵
2. 计算联合度矩阵
什么是联合度矩阵
联合度矩阵(Joint Degree Matrix)
是一个用来表示网络中节点的度(Degree)分布特性的矩阵,特别是在有向图中,它同时考虑了节点的入度(In-degree)和出度(Out-degree)。这个矩阵可以揭示网络的结构特性,比如节点的连接模式和网络的中心性分布。
如何计算联合度矩阵
可以按照以下步骤计算联合度矩阵:
-
计算每个节点的入度和出度:
- 入度是指有多少条边指向该节点。
- 出度是指有多少条边从该节点发出。
-
构建联合度矩阵:
- 联合度矩阵的元素 ( JDM(i, j) ) 表示出度为 ( i ) 且入度为 ( j ) 的节点对的数量。
代码实现
def jdegree(CIJ):
'''
该函数返回一个矩阵,其中每个元素 (u,v) 对应的值表示具有 u 个出边和 v 个入边的节点数量。
参数:
----------
CIJ : NxN np.ndarray
有向图的邻接矩阵,可以是二进制或带权重的。
返回:
-------
J : ZxZ np.ndarray
联合度分布矩阵
(由于matlab的一维索引,矩阵被偏移了一个位置)
J_od : int
具有 od>id 的顶点数量
J_id : int
具有 id>od 的顶点数量
J_bl : int
具有 id==od 的顶点数量
注意:
-----
权重信息将被忽略。
'''
# 确保CIJ矩阵是二进制的,如果CIJ不是二进制的,将进行转换
CIJ = binarize(CIJ, copy=True)
# 计算节点的入度和出度
n = len(CIJ) # 节点总数
id = np.sum(CIJ, axis=0) # 入度 = CIJ矩阵的列和
od = np.sum(CIJ, axis=1) # 出度 = CIJ矩阵的行和
# 创建联合度分布矩阵
# 注意:矩阵偏移了一个位置,以适应id和od为零的情况
# 矩阵的上三角部分有od>id的顶点
# 矩阵的下三角部分有id>od的顶点
# 对角线有id=od的顶点
szJ = np.max((id, od)) + 1 # 联合度矩阵的大小
J = np.zeros((szJ, szJ)) # 初始化联合度矩阵
# 填充联合度矩阵
for i in range(n):
J[id[i], od[i]] += 1 # 对每个节点,增加其对应的id和od的计数
# 计算不同区域的数量
J_od = np.sum(np.triu(J, 1)) # 上三角部分(不包括对角线)的和,od>id的顶点数
J_id = np.sum(np.tril(J, -1)) # 下三角部分(不包括对角线)的和,id>od的顶点数
J_bl = np.sum(np.diag(J)) # 对角线的和,id==od的顶点数
return J, J_od, J_id, J_bl
有向图联合度矩阵的计算实例
# 使用实例:
# 假设我们有一个3x3的有向图邻接矩阵,其中1表示存在边,0表示不存在边
CIJ_example = np.array([
[0, 1, 0], # 节点0有一个出边到节点1
[0, 0, 1], # 节点1有一个出边到节点2
[1, 0, 0] # 节点2有一个入边从节点0
])
# 调用jdegree函数计算联合度分布
J_matrix, J_od_count, J_id_count, J_bl_count = jdegree(CIJ_example)
# 打印结果
print("Joint degree distribution matrix:\n", J_matrix)
# array([[0., 0.],
# [0., 3.]])
print("Number of vertices with outdegree > indegree:", J_od_count) # 0.0
print("Number of vertices with indegree > outdegree:", J_id_count) # 0.0
print("Number of vertices with indegree == outdegree:", J_bl_count) # 3.0
有向图vs无向图的联合度矩阵计算
对于有向图来说,联合度矩阵的定义会稍微复杂一些,因为我们需要区分入度(in-degree)和出度(out-degree)。我们需要分别计算每个节点的入度和出度
,然后构建一个联合度矩阵,表示入度和出度之间的关系。