python-networkx实践社交网络可视化

「这是我参与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")
复制代码

image.png

添加边

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")
复制代码

image.png

打印邻接矩阵和邻接表

# 打印邻接矩阵
print("邻接矩阵")
print(nx.adjacency_matrix(G))
print("="*50)
print("邻接表")
for i in G.adj.items():
    print(i)
复制代码

image.png

热力图

# 邻接矩阵的热力图:反应稀疏程度
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 轴
复制代码

image.png

赋权

# 赋权
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 轴
复制代码

image.png

网络统计量

##### 网络统计量
# 度
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()))
复制代码

image.png

无向图的度均值: 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)
复制代码

image.png

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="随机正太分布图")
复制代码

image.png

小世界网络

# 小世界网络
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)
复制代码

image.png

image.png

无标度网络

无标度网络呈现幂率分布

# 无标度网络
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()
复制代码

image.png

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
复制代码

image.png

点度中心度

# 点度中心度 Degree Centrality
draw(G, pos,nx.degree_centrality(G),'点度中心度')
复制代码

image.png

中介中心度

# 中介中心度 Betweenness Centrality
draw(G, pos, nx.betweenness_centrality(G), '中介中心度')
复制代码

image.png

接近中心度

# 接近中心度 Closeness Centrality
draw(G, pos, nx.closeness_centrality(G), '接近中心度')
复制代码

image.png

特征向量中心度

# 特征向量中心度 Eigenvector Centrality
draw(G, pos, nx.eigenvector_centrality(G), '特征向量中心度')
复制代码

image.png

卡茨中心度

# 卡茨中心度 Katz Centrality
draw(G, pos, nx.katz_centrality(G, alpha=0.1, beta=1.0), '卡茨中心度')
复制代码

image.png

#创建一个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)
复制代码

image.png

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.密度
无向图的密度是 D = 2 m n ( n 1 ) D=\frac{2m}{n(n-1)} 有向图的密度是 D = m n ( n 1 ) D=\frac{m}{n(n-1)} 其中n是G中结点的数目 m是G中边的数量。
无边图的密度为0,完整图的密度为1,多重图的密度可以大于1。

中心势

2.中心势:中心势用来测量网络的整体中心性

  1. 首先找到网络中的最大中心度值MAX;
  2. 然后分别计算该MAX值与其他点的中心度的“差值”;
  3. 再计算这些"差值”的总和;
  4. 最后用这个总和除以理论上各个差值总和的最大可能值。

绝对中心势是 C A D = i = 1 n ( C A D m a x C A D i ) ( n 1 ) ( n 2 ) C_{AD}= \frac{\sum_{i=1}^{n}(C_{ADmax}-C_{ADi})}{(n-1)(n-2)}

相对中心势是 C R D = i = 1 n ( C R D m a x C R D i ) ( n 2 ) C_{RD}= \frac{\sum_{i=1}^{n}(C_{RDmax}-C_{RDi})}{(n-2)}

# 点度中心势
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="")
复制代码

image.png

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')
复制代码

image.png

draw(G, pos, nx.core_number(G), 'k-Core')
复制代码

image.png

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="")
复制代码

image.png

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])
复制代码

image.png

应该推荐作者13813和谁合作呢?

连接预测方法

  • 把链接预测任务看做信息检索中的排序问题
  • 计算每个边的新分数 s ( X , Y ) s(X,Y) .
  • 按降序排列所有可能的分数边 s ( X , Y ) s(X,Y) .
  • 选择最高的分数边 s ( X , Y ) s(X,Y) \rightarrow .

1.) 最短路径: s ( X , Y ) = s(X,Y) = X X Y Y 最短路径的长度.

# 最短路径算法
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]
复制代码

image.png

以作者13813为起始点,在网络中最短路径为2的合作者有57位

image.png

可视化,推荐效果

pair = set([author, '5227','5543','10931','639','18736'])
plot_subgraph(get_subgraph(graph, pair, n=30), pair)

复制代码

image.png

另一种方法:如果 X X y y 有许多共同撰写的共同作者,那么 x x y y 更有可能共同作者

2.) jaccard: S = N ( X ) N ( Y ) N ( X ) N ( Y ) S = \frac{|N(X) \cap N(Y)|}{|N(X) \cup N(Y)|}

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()
复制代码

image.png

image.png

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)
复制代码

image.png

猜你喜欢

转载自juejin.im/post/7034418192973824030
今日推荐