「这是我参与11月更文挑战的第25天,活动详情查看:2021最后一次更文挑战」
使用python-networkx实践社交网络可视化
01 使用networkx制作图网络模型
- 使用networkx创建图,添加结点和边
- 查看邻接矩阵/邻接表/图热力图
- 图网络赋权
- 图网络统计量(出度和入度、最短路径)
导包
import networkx as nx
import matplotlib.pyplot as plt
import collections
import random
import numpy as np
from pylab import rcParams
import warnings
warnings.filterwarnings("ignore")
# 支持中文
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
复制代码
添加节点
# 添加结点
G = nx.Graph() # 创建一个空的无向图 创建有向图使用nx.DiGraph()
G.add_node(1) # 创建单个节点
G.add_nodes_from(range(10)) # 创建一组节点
nx.draw(G,node_color="darkblue")
复制代码
添加边
G.add_edge(1,2)
e = [(2,3),(9,3),(8,4),(3,5),(8,6),(0,8),(1,9)]
G.add_edges_from(e)
nx.draw(G,with_labels=True,node_color="darkblue",font_color="white")
复制代码
打印邻接矩阵和邻接表
# 打印邻接矩阵
print("邻接矩阵")
print(nx.adjacency_matrix(G))
print("="*50)
print("邻接表")
for i in G.adj.items():
print(i)
复制代码
热力图
# 邻接矩阵的热力图:反应稀疏程度
print(nx.to_numpy_matrix(G)) #转换邻接矩阵为numpy格式
plt.imshow(nx.to_numpy_matrix(G)) #创建2维热力图
cbar = plt.colorbar() #--> 设置colorbar热力图题注
cbar.set_ticks([0,1]) #--> 设置colorbar刻度范围
cbar.ax.set_yticklabels(['Zero','One'],) #--设置colorbar刻度轴首尾坐标名
cbar.set_label('link', rotation=0) #--设置colorbar坐标名
plt.xlabel('node idx') #--设置 x 轴
plt.ylabel('node idx') #--设置 y 轴
复制代码
赋权
# 赋权
for e in G.edges():
G[e[0]][e[1]]['weight'] = random.uniform(0, 1) # 使用uniform分布给已存在的关系重新赋权
print(nx.to_numpy_matrix(G))
plt.imshow(nx.to_numpy_matrix(G))
cbar = plt.colorbar() #--> 设置colorbar热力图题注
cbar.set_ticks([0,1]) #--> 设置colorbar刻度范围
cbar.ax.set_yticklabels(['Zero','One'],) #--设置colorbar刻度轴首尾坐标名
cbar.set_label('link', rotation=0) #--设置colorbar坐标名
plt.xlabel('node idx') #--设置 x 轴
plt.ylabel('node idx') #--设置 y 轴
复制代码
网络统计量
##### 网络统计量
# 度
G =nx.karate_club_graph() # 创建一个空手道成员俱乐部图 :这是一个非常有名的社交网络图
degree_sequence = sorted([d for n, d in G.degree()], reverse=True) # 保存每个结点的度,并按降序排列
print("度 排列", degree_sequence)
print("= "*50)
degreeCount = collections.Counter(degree_sequence) # 计算每种度的数目
print("度 频率", degreeCount)
print("= "*50)
deg, cnt = zip(*degreeCount.items()) # 创建一个迭代器
rcParams['figure.figsize'] = 12, 8 # 设置画板全局大小
fig, ax = plt.subplots() # 创建子图
plt.bar(deg, cnt, width=0.80, color='darkblue')
plt.title("度 直方图")
plt.ylabel("频率")
plt.xlabel("度")
plt.axes([0.4, 0.4, 0.5, 0.5])
plt.axis('off')
ax.set_xticks([d + 0.4 for d in deg])
ax.set_xticklabels(deg)
pos = nx.spring_layout(G) #-- 设置网络布局
nx.draw_networkx_nodes(G, pos, node_color= 'darkblue',node_size=20) # 画图
nx.draw_networkx_edges(G, pos, alpha=0.5) # 画边
plt.show()
print('无向图的度均值:', np.mean(G.degree()))
复制代码
无向图的度均值: 10.544117647058824
最短路径
# 最短路径
R = nx.karate_club_graph()
source=14 # 选一个点做起点
target=16 # 选一个点做终点
# 最短路径作图函数
def short_path_plot(G,source,target):
pos = nx.spring_layout(G) #-- 设置网络布局
nx.draw(G,pos,node_color='k', with_labels=True, font_color='white') #-- 画图结构
path = nx.shortest_path(G,source=14,target=16) #-- 调用shortest path计算最短路径
print("最短路径:",path)
path_edges = list(zip(path,path[1:])) #-- 创建最短路径边
nx.draw_networkx_nodes(G,pos,nodelist=path,node_color='r', label=True) #-- 画结点
nx.draw_networkx_edges(G,pos,edgelist=path_edges,edge_color='r',width=10) #--画边
plt.axis('equal')
plt.show()
return
#调用函数
short_path_plot(R,source,target)
复制代码
02 网络结构
- 随机图
- 小世界模型
- 无标度网络
# 下载 幂律分布 包
# !pip install powerlaw
import re
import powerlaw
import seaborn as sns
from operator import itemgetter
# 直方图绘制
def draw_hist_network(G):
degree_sequence = sorted([d for n, d in G.degree()], reverse=True)
degreeCount = collections.Counter(degree_sequence)
deg, cnt = zip(*degreeCount.items())
rcParams['figure.figsize'] = 10, 5
fig, ax = plt.subplots()
plt.bar(deg, cnt, width=0.80, color='darkblue')
plt.title("度 直方图")
plt.ylabel("频率")
plt.xlabel("度")
ax.set_xticks([d + 0.4 for d in deg])
ax.set_xticklabels(deg)
plt.axes([0.4, 0.4, 0.5, 0.5])
Gcc = G.subgraph(sorted(nx.connected_components(G), key=len, reverse=True)[0])
pos = nx.spring_layout(G)
plt.axis('off')
nx.draw_networkx_nodes(G, pos, node_color= 'darkblue',node_size=20)
nx.draw_networkx_edges(G, pos, alpha=0.4)
plt.show()
pass
# 网络绘制
def draw_network(G,name):
plt.title(name)
pos = nx.circular_layout(G)
nx.draw_networkx_nodes(G, pos, node_color= 'darkblue',node_size=20)
nx.draw_networkx_edges(G, pos, alpha=0.4)
plt.axis('off')
plt.show()
pass
复制代码
随机图
# 生成随机图
nodes_n=50
degree=20
simulation_number=10
rcParams['figure.figsize'] = 5, 5
#连通率 = 0.01:
#------------------------------
G = nx.random_regular_graph(degree,nodes_n) #<-- Generate the Small World Network
draw_network(G,name="随机正太分布图")
复制代码
小世界网络
# 小世界网络
nodes_n=1000
degree=20
rcParams['figure.figsize'] = 5, 5
#简单的小世界模型------------------------------
G = nx.watts_strogatz_graph(20,5,p=0.01) #<-- 生成小世界模型
draw_network(G,name="小世界模型")
#连通率 = 0.01:
#------------------------------
G = nx.watts_strogatz_graph(nodes_n,degree, p = 0.01) #<-- 生成小世界模型
draw_hist_network(G)
#连通率 = 0.05:
#------------------------------
G = nx.watts_strogatz_graph(nodes_n,degree, p = 0.05) #<-- 生成小世界模型
draw_hist_network(G)
#连通率 = 0.1:
#------------------------------
G = nx.watts_strogatz_graph(nodes_n,degree, p = 0.1) #<-- 生成小世界模型
draw_hist_network(G)
复制代码
无标度网络
无标度网络呈现幂率分布
# 无标度网络
node_n = 100000 #-- 节点数
m=3 #-- 从新节点附加到现有节点的边缘数
G = nx.barabasi_albert_graph(node_n, m)
# draw_network(G,name="无标度网络") # 100000结点绘图很慢很占内存
degree_freq = nx.degree_histogram(G)
degrees = range(len(degree_freq))
plt.figure(figsize=(12, 8))
plt.loglog(degrees[m:], degree_freq[m:],'go-')
plt.xlabel('度', fontsize = 'x-large')
plt.ylabel('频率', fontsize = 'x-large')
plt.show()
复制代码
03 结点测量 - 中心度
什么是中心度?
中心性是描述图表中各个节点的重要性的术语。以便回答“那个结点是图表中最重要的节点(顶点)?”这一问题,为此进行了大量实验,实验列表如下所示:
- 点度中心度 Degree Centrality
- 中介中心度 Betweenness Centrality
- 接近中心度 Closeness Centrality
- 特征向量中心度 Eigenvector Centrality
- 卡茨中心度 Katz Centrality
点度中心度/中介中心度/接近中心度都使用绝对中心度
import pandas as pd
import matplotlib.colors as mcolors
from pylab import rcParams
复制代码
Zachary 空手道俱乐部是一个被广泛使用的社交网络,其中的节点代表空手道俱乐部的成员,边代表成员之间的相互关系。
# Zachary 空手道俱乐部
G = nx.karate_club_graph()
pos = nx.circular_layout(G) #-- 圆盘布局
nodes = nx.draw_networkx_nodes(G, pos, node_size=400,node_color='White',edgecolors='b')
nodes.set_norm(mcolors.SymLogNorm(linthresh=0.03, linscale=2))
labels = {i:i for i in G.nodes()}
labels = nx.draw_networkx_labels(G, pos, labels, font_size=12)
edges = nx.draw_networkx_edges(G, pos, edge_color = 'grey')
plt.axis('off')
plt.show()
def draw(G, pos, measures, measure_name):
nodes = nx.draw_networkx_nodes(G, pos, node_size=400, cmap=plt.cm.plasma,
node_color=list(measures.values()),
nodelist=list(measures.keys()))
nodes.set_norm(mcolors.SymLogNorm(linthresh=0.03, linscale=1))
labels = nx.draw_networkx_labels(G, pos, font_color='white')
edges = nx.draw_networkx_edges(G, pos)
plt.title(measure_name,size=18)
cbar = plt.colorbar(nodes)
cbar.set_label('中心度权重', rotation=0)
plt.axis('off')
plt.show()
pass
复制代码
点度中心度
# 点度中心度 Degree Centrality
draw(G, pos,nx.degree_centrality(G),'点度中心度')
复制代码
中介中心度
# 中介中心度 Betweenness Centrality
draw(G, pos, nx.betweenness_centrality(G), '中介中心度')
复制代码
接近中心度
# 接近中心度 Closeness Centrality
draw(G, pos, nx.closeness_centrality(G), '接近中心度')
复制代码
特征向量中心度
# 特征向量中心度 Eigenvector Centrality
draw(G, pos, nx.eigenvector_centrality(G), '特征向量中心度')
复制代码
卡茨中心度
# 卡茨中心度 Katz Centrality
draw(G, pos, nx.katz_centrality(G, alpha=0.1, beta=1.0), '卡茨中心度')
复制代码
#创建一个pandas表比较各种中心度:
dataset1=[]
for k in nx.degree_centrality(G).keys():
dataset1.append([k,nx.degree_centrality(G)[k],nx.katz_centrality(G)[k],nx.eigenvector_centrality(G)[k],
nx.closeness_centrality(G)[k],nx.betweenness_centrality(G)[k]])
rcParams['figure.figsize'] = 5, 5
sns.set(style="ticks")
#创建dataframe:
df=pd.DataFrame(dataset1,columns=['id',
'Degree centrality','Katz centrality','Eigenvector centrality',
'Clossness centrality','Betweenness centrality'])
#输出 变量两两之间的关系:
sns.pairplot(df)
复制代码
04.整体测量
- 密度
- 中心势
# 密度
G = nx.karate_club_graph()
print('karate_club_graph图 密度:', nx.density(G))
G = nx.watts_strogatz_graph(21,7,p=0.05) #<-- 生成小世界模型
print('生成的小世界模型 密度:', nx.density(G))
复制代码
karate_club_graph图 密度: 0.13903743315508021 生成的小世界模型 密度: 0.3
密度
1.密度
无向图的密度是
有向图的密度是
其中n
是G中结点的数目 m
是G中边的数量。
无边图的密度为0,完整图的密度为1,多重图的密度可以大于1。
中心势
2.中心势:中心势用来测量网络的整体中心性
- 首先找到网络中的最大中心度值MAX;
- 然后分别计算该MAX值与其他点的中心度的“差值”;
- 再计算这些"差值”的总和;
- 最后用这个总和除以理论上各个差值总和的最大可能值。
绝对中心势是
相对中心势是
# 点度中心势
def Degree_Centralization(G,mode="AD"):
n = nx.number_of_nodes(G) #-- n节点数目
centrality_list = np.array(list(nx.degree_centrality(G).values())) # 中心度列表
max_centrality = max(centrality_list) # 中心度 最大值
if mode=='AD':
degree_centralization = sum(max_centrality - centrality_list) / (n-1)*(n-2)
if mode=='RD':
degree_centralization = sum(max_centrality - centrality_list) / (n-2)
return degree_centralization
复制代码
G = nx.karate_club_graph()
print('karate_club_graph图 绝对中心势:', Degree_Centralization(G,mode='AD'))
print('karate_club_graph图 相对中心势:', Degree_Centralization(G,mode='RD'))
draw_network(G,name="")
G = nx.watts_strogatz_graph(21,7,p=0.05) #<-- 生成小世界模型
print('生成的小世界模型 绝对中心势:', Degree_Centralization(G,mode='AD'))
print('生成的小世界模型 相对中心势:', Degree_Centralization(G,mode='RD'))
draw_network(G,name="")
复制代码
05.凝聚子群
- K-核 凝聚子群
G = nx.karate_club_graph()
nx.core_number(G)
# 输出
{0: 4,
1: 4,
2: 4,
3: 4,
4: 3,
5: 3,
6: 3,
7: 4,
8: 4,
9: 2,
10: 3,
11: 1,
12: 2,
13: 4,
14: 2,
15: 2,
16: 2,
17: 2,
18: 2,
19: 3,
20: 2,
21: 2,
22: 2,
23: 3,
24: 3,
25: 3,
26: 2,
27: 3,
28: 3,
29: 3,
30: 4,
31: 3,
32: 4,
33: 4}
复制代码
def draw_clu(G, pos, measures, measure_name):
clusters=np.array(list(set(measures.values())))
plt.figure()
nodes = nx.draw_networkx_nodes(G, pos, node_size=250, cmap=mcolors.ListedColormap(plt.cm.Set3(clusters)),
node_color=np.array(list(measures.values()))-1,
nodelist=list(measures.keys()))
print(measures.values())
print(measures.keys())
labels = nx.draw_networkx_labels(G, pos)
edges = nx.draw_networkx_edges(G, pos)
plt.title(measure_name)
rcParams['figure.figsize'] = 12, 8
rcParams['font.sans-serif'] = ['SimHei']
cb = plt.colorbar(nodes,ticks=range(0,len(clusters)), label='子群标签')
cb.ax.tick_params(length=0)
cb.set_ticklabels(list(set(measures.values())))
nodes.set_clim(-0.5, len(clusters)-0.5)
plt.axis('off')
plt.show()
G = nx.karate_club_graph()
pos = nx.spring_layout(G)
draw_clu(G, pos, nx.core_number(G),'k-Core')
复制代码
draw(G, pos, nx.core_number(G), 'k-Core')
复制代码
06.链接预测
import networkx as nx
import numpy as np
import urllib.request
urllib.request.urlretrieve("http://snap.stanford.edu/data/ca-GrQc.txt.gz", "ca-GrQc.txt.gz")
graph = nx.read_edgelist('ca-GrQc.txt.gz')
graph.order()
draw_network(graph,name="")
复制代码
degrees = dict(graph.degree())
author = sorted(degrees.items(),key=lambda x: x[1],reverse=True)[500][0]
print('学者 %s 的"度"为 %d' % (author, graph.degree()[author]))
# 获取子图
def get_subgraph(graph, nodes, n=100):
neighbors = set()
for ni in nodes:
neighbors |= set(graph.neighbors(ni))
# plot at least the target node and his neighbors.
result = set(nodes) | neighbors
# add "friends of friends" up to n total nodes.
for x in neighbors:
# how many more nodes can we add?
maxsize = n - len(result)
toadd = set(graph.neighbors(x)) - result
result.update(list(toadd)[:maxsize])
if len(result) > n:
break
return graph.subgraph(result)
subgraph = get_subgraph(graph, [author], n=30)
print('子图有个 %d 结点' % len(subgraph.nodes()))
复制代码
学者 13813 的"度"为 13
子图有个 30 结点
# 绘制子图
def plot_subgraph(subgraph, target_nodes):
nodes = list(subgraph.nodes())
colors = ['c'] * len(nodes)
for n in target_nodes:
idx = nodes.index(n)
colors[idx] = 'r'
sizes = [800] * len(nodes)
sizes[idx] = 1000
plt.figure(figsize=(8,8))
plt.axis('off')
nx.draw_networkx(subgraph, nodelist=nodes, with_labels=True,
width=.5, node_color=colors,
node_size=sizes, alpha=.5)
plot_subgraph(subgraph, [author])
复制代码
应该推荐作者13813和谁合作呢?
连接预测方法
- 把链接预测任务看做信息检索中的排序问题
- 计算每个边的新分数 .
- 按降序排列所有可能的分数边 .
- 选择最高的分数边 .
1.) 最短路径: 从 到 最短路径的长度.
# 最短路径算法
def rank_by_shortest_path(graph, node):
paths = nx.shortest_path_length(graph, node)
return sorted(paths.items(), key=lambda x: x[1])
shortest_paths = rank_by_shortest_path(graph, author)
print('最短路径 top-20 :')
shortest_paths[:20]
print("以作者13813为起始点,在网络中最短路径为2的合作者有%s位" % len([s for s in shortest_paths if s[1] == 2]))
print('链接预测 top-10 :')
[s for s in shortest_paths if s[1] == 2][:10]
复制代码
以作者13813为起始点,在网络中最短路径为2的合作者有57位
可视化,推荐效果
pair = set([author, '5227','5543','10931','639','18736'])
plot_subgraph(get_subgraph(graph, pair, n=30), pair)
复制代码
另一种方法:如果 和 有许多共同撰写的共同作者,那么 和 更有可能共同作者
2.) jaccard:
def rank_by_jaccard(graph, node):
neighbors = set(graph.neighbors(node))
scores = []
for n in graph.nodes():
neighbors2 = set(graph.neighbors(n))
scores.append((n, len(neighbors & neighbors2) / len(neighbors | neighbors2)))
return sorted(scores, key=lambda x: x[1], reverse=True)
common_jaccard = rank_by_jaccard(graph, author)
common_jaccard[:20]
plt.plot(sorted([x[1] for x in common_jaccard if x[1] > 0.]))
plt.show()
复制代码
def dataframe_scores(scores_list, names, n):
ids = set()
for scores in scores_list:
ids.update([x[0] for x in scores[:n]])
print('jaccard算法链接预测的 top %d' % (n))
results = []
for id_ in ids:
result = {'id': id_}
for scores, name in zip(scores_list, names):
for rank, (id2, score) in enumerate(scores):
if id2 == id_:
result[name + '_rank'] = rank
result[name + '_score'] = score
break
results.append(result)
headers = ['id']
for name in names:
headers.append(name + '_rank')
headers.append(name + '_score')
return pd.DataFrame(results, columns=headers)
df = dataframe_scores([common_jaccard],['jaccard'],20)
df.sort_values('jaccard_rank').head(10)
pair = set([author, '23204','19204','17559','409','6746'])
plot_subgraph(get_subgraph(graph, pair, n=30), pair)
复制代码