使用networkx绘制网络图及模块使用

使用networkx绘制网络图及模块使用

主要练习了包和模块的生成,并且学习了各种可视化的操作方法,包括networkx库(对于网络的生成)、使用matplotlib、seaborn画柱形图,折线图等及对其的保存。同时,还学习了Gephi工具。

包的设计

在这里插入图片描述

包作为一个文件夹,需要在文件夹目录中添加一个__ init __.py文件,表示这是一个模块

一些在测试中使用的文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fC7DQzyz-1664261383990)(C:\Users\kerrla\AppData\Roaming\Typora\typora-user-images\image-20220927113232793.png)]

模块节点操作

import csv
import networkx as nx

def init_node(path_node):#生成字典,每个id对应一个字典,字典里包含所有属性
    f=open(path_node,"r",encoding="utf-8")
    file=csv.reader(f)
    file = list(file)
    f.close()
    dict_user = {}  # 保存每个用户/节点的信息
    index = [0, 1, 2, 3, 4, 6, 7, 8]  # 表示对应每一个id的属性
    for item in file[1:]:
        info = {'views': "NAN", 'mature': "NAN", 'life_time': "NAN",
                'created_at': "NAN", 'updated_at': "NAN", 'dead_account': "NAN", 'language': "NAN", 'affiliate': "NAN"}
        for i in index:
            info[file[0][i]] = item[i]
        dict_user[item[5]] = info
    return dict_user
#list(dict_user.keys())返回键

def update_dict_user(dict_user,id,attribute,attribute_value):#之后要构建图之类的,可能需要对节点更新信息,之后发现貌似不需要
    dict_user[id][attribute] = attribute_value
    return dict_user

def get_attribute(id,G,attribute):#输入节点的id,返回需要的属性值
    if attribute=="degree":
        ans=G.degree(id)
    else:
        ans=G.nodes[id][attribute]
    return ans

def print_node(G,id):
    print("id:{0:},{1:},'degree':{2:}".format(id, G.nodes[id],G.degree[id]))

改进方法:

#字典初始化
#为了达到该函数的普适性,我不希望在函数中出现具体属性的名字,而是通过索引的方式,当然id的位置可能没办法,但是当作为字典再去调用id的方式或者用列表索引的方式找id也是可以的,此处不再赘述
info=dict.fromkeys(file[0],"NAN")#前面表示key值,后面表示赋值

G的一些关于节点的函数

G.nodes()返回G的所有节点,如果要打印最好转为list形式

G.nodes[i] 返回节点的所有属性,且返回字典形式

G.degree[id]返回节点的度

对于打印所有的属性的函数,可以采用**dic[id],**调用可变长度的字典,或者*调用可变长度的元组

构建网络

import csv

def init_edge(path_edge):
    f = open(path_edge, "r", encoding="utf-8")
    file = csv.reader(f)
    file = list(file)
    edge_lis=file[1:]#排除第一行的列名
    f.close()
    return edge_lis

将边转化为构建图像使用的样式

import networkx as nx
import pickle as pick

def init_graph(dict_user,edge_lis):#对于传入的参数为id列表,因此需要对字典输出做一个键的列表化
    G=nx.Graph()#生成一个空白图
    for id in dict_user:
        for key in dict_user[id]:
            G.add_node(id)#插入点
            G.nodes[id][key]=dict_user[id][key]#添加属性
    G.add_edges_from(edge_lis)#连接节点
    return G


def save_graph(G,path):
    f=open(path,"wb")#注意应该是二进制,因为序列化需要
    pick.dump(G,f)
    f.close()

def load_graph(path):
    #反序列化需要将文件以二进制读取"rb",且load参数应为数据流
    f=open(path,"rb")
    result =pick.load(f)
    f.close()
    return result

说明:在init_graph函数里采用了两重循环,其实是比较费时的,但是这样可以保证输出的属性结果比较好看。

之后发现使用可变长度变量能够非常好的解决这个问题,最后也输出了同样的结果

for id in dict_user:
    G.add_node(id,**(dict_user[id]))#插入点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4qaCmKji-1664261383992)(C:\Users\kerrla\AppData\Roaming\Typora\typora-user-images\image-20220927111748803.png)]

在使用序列化和反序列化的操作中,要注意两个点:1、解码和写入都用的是二进制方法。2、pickle.dump(序列化对象,文件指针),pickle.load(文件指针),而不是直接输入文件路径

构建完序列化的文件在之后可以直接拿来用,就不需要打开各种数据文件,重新生成一遍,会快很多,当我发现这个问题时,已经来不及了。。。

图像分析

import networkx as nx

def get_node_degree(id,G):#返回某一个节点的度
    ans=G.degree(id)
    return ans

def get_node_number(G):
    number=nx.number_of_nodes(G)
    return number

def get_edge_number(G):
    number=nx.number_of_edges(G)
    return number

def cal_average_degree(G):
    node=G.nodes()#返回degree列表
    sum=0
    for i in node:
        sum+=G.degree(i)
    ans=sum/len(node)
    return ans

def cal_degree_distribution(G):#返回度的分布序列
    degree_dis=nx.degree_histogram(G)#统计从0到最大度的频次
    lis = [z / float(sum(degree_dis)) for z in degree_dis]#生成密度列表
    return lis

def caL_view_distribution(G):
    view_dic={}
    for id in G.nodes():#生成字典
        view_dic[G.nodes[id]["views"]]=view_dic.get(G.nodes[id]["views"],0)+1
    sum=0
    for key in view_dic.keys():
        sum+=view_dic[key]
    for key in view_dic.keys():
        view_dic[key]=view_dic[key]/sum
    return view_dic

一些简单的计算和函数调用。

需要说明的就是几个函数:

nx.number_of_nodes(G)统计节点数,或者直接对节点列表取len也一样;nx.number_of_edges(G)取边的数量,或者直接使用G.edges()取列表长度也是一样的

nx.degree_histogram(G)统计的是图中所有的度,从0到最大度,如果为0则标0

词频统计的一个方法dict[key]=dict.get(key,0)+1;dict.keys() and dict.values() can return two lists.

可视化

可视化的方式有很多,当时考虑了一些处理数据的方式,毕竟数据量实在过于庞大,对于可视化的效果并不好

节点视图可视化

import networkx as nx
import matplotlib.pyplot as plt

def plot_ego_2(G,node):##由于有600多万条边和16万多点,这种画法如果在考虑度的角度,点就过多,考虑点的角度,度就过大
    plt.figure(figsize=(16,16))
    nx.draw_networkx_nodes(G,pos=nx.spring_layout(G),nodelist=node,node_color='r', label=True)  # 画节点
    nx.draw_networkx_edges(G,pos=nx.spring_layout(G))  # 画边
    plt.axis('off')  # 去掉坐标刻度
    plt.savefig("D:\\经管大三\\现代程序设计\\week4\\ego_2.png")
    plt.show()

def plot_ego(G,node):
    res=G.subgraph(node)
    nx.draw_networkx(res,pos=nx.spring_layout(res))
    plt.savefig("D:\\经管大三\\现代程序设计\\week4\\ego.png")
    plt.show()

def plot_ego_3(G,node):
    res = G.subgraph(node)
    nx.draw(res,
        pos = nx.circular_layout(G),
        node_color = 'b',
        edge_color = 'r',
        with_labels = True,
        font_size =10,
        node_size =20)
    plt.savefig("D:\\经管大三\\现代程序设计\\week4\\ego_3.png")
    plt.show()

def plotdegree_distribution(degree_dis):
    x=[i for i in range(len(degree_dis))]#生成横坐标
    y=degree_dis
    x2=[i for i in range(100)]#因为通过可视化发现100的分布较多,而由于度上限很大,使得该部分分布密集
    y2=degree_dis[0:100]
    plt.subplot(1,2,1)
    plt.plot(x,y,color="red",linewidth=3.0)
    plt.title("degree distribution")
    plt.xlabel("degree")
    plt.ylabel("scale")
    plt.subplot(1,2,2)
    plt.plot(x2,y2,color="blue",linewidth=3.0)
    plt.title("degree distribution")
    plt.xlabel("degree")
    plt.ylabel("scale")
    plt.show()
for i in G.nodes():
    if G.degree(i)==60:
        id=i
        break
lis1=[id]
neighbor=list(G.neighbors(id))
lis1=lis1+neighbor
H=G.subgraph(lis1)#返回结果不错
nx.draw(H,pos=nx.circular_layout(H),with_labels= True)
plt.show()

在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T31V86C0-1664261383994)(D:\经管大三\现代程序设计\week4\ego_3.png)]

单纯的从度的角度或者节点的角度出发结果并不是很好

问题最大的地方不在于如何画图,在于如何选取点,最后发现采用上述方法找度为60的点及其邻接点,在这种情况下将会产生61个点,利用subgraph绘制子图即可。下图中我们可以看出82是与其他节点连接最密集的节点,当然我们找的度为60的节点就是82!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EUGylnMv-1664261383995)(D:\经管大三\现代程序设计\week4\度为60的节点图.png)]

函数详解

plt.figure() 对画布窗口的设置

nx.draw_networkx_nodes(G,pos=nx.spring_layout(G),nodelist=node,node_color=‘r’, label=True)
nx.draw_networkx_edges(G,pos=nx.spring_layout(G))

G我认为更像一个网络图的画布,我们可以通过draw函数在上面任意加点和线,利用draw_networkx_nodes 和draw_networkx_edges可以自定义网络图,pos表示绘图的方式,有五种基本的绘图方式,nodelist表示绘制点的列表,在绘制边的函数参数中也可以如此添加

我从同学那又看到一个筛选节点的方法,就是直接在图上删除节点G.remove_node(i)

plt.axis()设置坐标轴格式;plt.show()展示绘制的视图;plt.savefig(file)可以将图片按照希望的格式保存,也可以保存为矢量图

下面是绘制度的分布图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4H8qWuIJ-1664261383995)(D:\经管大三\现代程序设计\week4\Figure_1.png)]

如果对所有的度进行统计会发现差别特别大,度大于某一个值之后分布变化及其不明显,因此我将0-100的度分出,做一个局部的分析判断。结果显示有大量的节点并没有邻接点,且随着度的增大有明显的下降趋势。

其中用了plt.subplot()函数用来实现子图处理;plt.plo()t画折线图

网络图可视化

import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import networkx as nx
#第一版的plot方法由于x轴太密
import matplotlib.ticker as ticker
import math

def plot_nodes_attribute(G,attribute):
    node=G.nodes()
    key=['views', 'mature', 'life_time', 'created_at', 'updated_at', 'numeric_id', 'dead_account', 'language', 'affiliate']
    if attribute in key:
        key_dic={}
        for id in node:
            key_dic[G.nodes[id][attribute]]=key_dic.get(G.nodes[id][attribute],0)+1
        #画图
        x=list(key_dic.keys())
        y=list(key_dic.values())
        length=math.floor(len(x)/7)
        ax=sns.barplot(x,y)
        ax.xaxis.set_major_locator(ticker.MultipleLocator(base=length))  # 解决X轴密集问题
        plt.title(f"{attribute:}")
        plt.savefig(f"D:\\经管大三\\现代程序设计\\week4\\{attribute:}分布.png")
    elif attribute == "degree":
        y=nx.degree_histogram(G)
        x=[i for i in range(len(y))]
        length = math.floor(len(x) / 7)
        ax=sns.barplot(x, y)
        ax.xaxis.set_major_locator(ticker.MultipleLocator(base=length))  # 解决X轴密集问题
        plt.title(f"{attribute:}")
        plt.savefig(f"D:\\经管大三\\现代程序设计\\week4\\{attribute:}分布.png")
    else:
        print("not find")

def plot_nodes_attribute_2(G,attribute):#画直方图和拟合曲线
    node=G.nodes()
    key=['views', 'mature', 'life_time', 'created_at', 'updated_at', 'numeric_id', 'dead_account', 'language', 'affiliate']
    if attribute in key:
        lis=[]
        for id in node:
            lis.append(G.nodes[id][attribute])
        #画图
        length = math.floor(len(lis) / 7)#由于间隔太密,因此提取部分x轴坐标
        ax=sns.distplot(lis,kde=True,rug=True)
        ax.xaxis.set_major_locator(ticker.MultipleLocator(base=length))  # 解决X轴密集问题
        plt.title(f"{attribute:}")
        plt.savefig(f"D:\\经管大三\\现代程序设计\\week4\\{attribute:}分布_2.png")
    elif attribute == "degree":
        lid=[]
        for id in node:
            lid.append(G.degree(id))
        sns.distplot(lid)
        plt.title(f"{attribute:}")
        plt.savefig(f"D:\\经管大三\\现代程序设计\\week4\\{attribute:}分布_2.png")
    else:
        print("not find")

以上函数的编写部分我觉得简洁性欠佳,其实可以对之前显示节点所有属性的模块函数进行调用,这样就不需要重新提取属性了。最开始做的时候,发现没有注意坐标轴的问题,导致坐标轴相互交叠根本看不清楚。

length = math.floor(len(lis) / 7)#由于间隔太密,因此提取部分x轴坐标
ax=sns.distplot(lis,kde=True,rug=True)
ax.xaxis.set_major_locator(ticker.MultipleLocator(base=length))  # 解决X轴密集问题

因此我的处理方式就是将所有可能值的总长度分成7段,计算每段的长度,设置x轴的格式来进行简化

第一个函数用了sns.bar(x,y)绘制条形图,第二个函数用sns.distplot(lis,kde=True,rug=True)绘制条形图和拟合曲线,kde=True表示绘制核密度,rug=True表示用直线表示核密度

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cVewWWBy-1664261383996)(D:\经管大三\现代程序设计\week4\views分布.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i5B8XhVL-1664261383997)(D:\经管大三\现代程序设计\week4\life_time分布.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QvtzHdlq-1664261383997)(D:\经管大三\现代程序设计\week4\mature分布.png)]

main函数

import GraphStat.Networkbuilder.node as GNN
import GraphStat.Networkbuilder.edge as GNE
import GraphStat.Networkbuilder.graph as GNG
import GraphStat.Networkbuilder.stat as GNS
import GraphStat.Visualization.plotgraph as GVP
import GraphStat.Visualization.plotnodes as GVN

path="D:\\经管大三\\现代程序设计\\week4\\twitch_gamers\\large_twitch_features.csv"
path2="D:\\经管大三\\现代程序设计\\week4\\twitch_gamers\\large_twitch_edges.csv"
path3="D:\\经管大三\\现代程序设计\\week4\\G.txt"
dic=GNN.init_node(path)
edge=GNE.init_edge(path2)
G=GNG.init_graph(dic,edge)
#GNN.print_node(G,"8000")
#ans=GNN.get_attribute("8000",G,"language")
#print(ans)
#X=GNS.get_edge_number(G)
#Y=GNS.get_node_number(G)
#Z=GNS.caL_view_distribution(G)
#Q=GNS.cal_average_degree(G)
#W=GNS.cal_degree_distribution(G)
#E=GNS.get_node_degree("8000",G)
#node=[]
#for i in G.nodes():
 #   if G.degree(i)==120:#之前以度为标定
  #      node.append(i)
#GVP.plot_ego_3(G,node)
#GVP.plotdegree_distribution(W)
GVN.plot_nodes_attribute(G,"views")

Gephi工具的使用以及对图的处理

最开始提取了前50个id,想要返回他们子图的边的关系,但是发现子图中只有一条边,这显然是不符合要求的

path1="D:\\经管大三\\现代程序设计\\week4\\twitch_gamers\\large_twitch_features.csv"
#path2="D:\\经管大三\\现代程序设计\\week4\\twitch_gamers\\large_twitch_edges.csv"
path3="D:\\经管大三\\现代程序设计\\week4\\Gephi_node.csv"
path4="D:\\经管大三\\现代程序设计\\week4\\Gephi_edge.csv"
f1=open(path1,"r",encoding="utf-8")
#f2=open(path2,"r",encoding="utf-8")
lis1=list(csv.reader(f1))
lis1=[item[5] for item in lis1[1:51]]#惊奇的发现这个数据似乎是按照id的增序排列的,这么干好像又多此一举了
#lis2=list(csv.reader(f2))
G=GNG.load_graph("D:\\经管大三\\现代程序设计\\week4\\G.txt")
H=G.subgraph(lis1)
print(list(H.edges))
f1.close()
#f2.close()
#我们在使用Gephi时发现随机抽取边使得点的数量爆炸,哪怕只抽了100000条边,因此搜索一些边来进行操作
f1=open(path3,"w",encoding="utf-8",newline='')
f2=open(path4,"w",encoding="utf-8",newline='')#newline=''参数可以防止多余的换行
g1=csv.writer(f1)
g2=csv.writer(f2)
g1.writerow(lis1[0])
g1.writerows(lis1[1:201])
#g2.writerow(lis2[0])
#g2.writerows(lis2[1:100001])

于是我决定选择一个度为60的节点,然后反向寻找该节点的邻接点,生成一个新的子图

import csv
import GraphStat.Networkbuilder.graph as GNG
import networkx as nx

path3="D:\\经管大三\\现代程序设计\\week4\\Gephi_node.csv"#Gephi貌似只处理csv文件
path4="D:\\经管大三\\现代程序设计\\week4\\Gephi_edge.csv"#对于Gephi工具传入边的关系自动分析
G=GNG.load_graph("D:\\经管大三\\现代程序设计\\week4\\G.txt")
for i in G.nodes():
    if G.degree(i)==60:
        id=i
        break
lis1=[id]
neighbor=list(G.neighbors(id))
lis1=lis1+neighbor
H=G.subgraph(lis1)#返回结果不错
for it in range(len(lis1)):
    lis1[it]=[lis1[it]]#这一步是为了生成csv文件
H_edges=list(H.edges)
f1=open(path3,"w",encoding="utf-8",newline='')
f2=open(path4,"w",encoding="utf-8",newline='')#newline=''参数可以防止多余的换行
g1=csv.writer(f1)
g2=csv.writer(f2)
init_edge=["node1","node2"]
g1.writerow(["node"])
g1.writerows(lis1)
g2.writerow(init_edge)
g2.writerows(H_edges)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X9VKoTFn-1664261383998)(D:\经管大三\现代程序设计\week4\Gephi子图测试.png)]

id))
lis1=lis1+neighbor
H=G.subgraph(lis1)#返回结果不错
for it in range(len(lis1)):
lis1[it]=[lis1[it]]#这一步是为了生成csv文件
H_edges=list(H.edges)
f1=open(path3,“w”,encoding=“utf-8”,newline=‘’)
f2=open(path4,“w”,encoding=“utf-8”,newline=‘’)#newline=''参数可以防止多余的换行
g1=csv.writer(f1)
g2=csv.writer(f2)
init_edge=[“node1”,“node2”]
g1.writerow([“node”])
g1.writerows(lis1)
g2.writerow(init_edge)
g2.writerows(H_edges)

![在这里插入图片描述](https://img-blog.csdnimg.cn/34479c9022744efc8dbf7cc91fca890c.png#pic_center)
![在这里插入图片描述](https://img-blog.csdnimg.cn/d32fd66562da488e8d12fd148fd43026.png#pic_center)








  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Networkx 是一个用于创建、操作和研究复杂网络结构的 Python 库。虽然它的主要应用是网络分析和图可视化,但也可以用于实现深度学习计算图的绘制。 深度学习计算图是指由各层神经元和连接权重组成的图结构,用于描述神经网络的输入、输出以及隐藏层之间的关系。通过绘制这个计算图,我们可以更好地理解和分析深度学习模型。 要使用 Networkx 绘制深度学习计算图,首先需要安装 Networkx 库并导入所需的模块。然后,可以创建一个空的有向图,表示深度学习计算图。接下来,可以使用 Networkx 提供的各种方法来添加节点和边,以模拟神经网络中的神经元和连接权重。 例如,可以使用 `add_node` 方法添加输入节点、隐藏节点和输出节点,使用 `add_edge` 方法添加连接权重。可以根据需要设置节点的属性,如名称、类型和形状。还可以为边设置权重和其他属性。 绘制深度学习计算图时,可以使用 Networkx 提供的绘图功能,如 `draw` 方法。可以设置不同的布局算法,以使绘制出来的计算图更加清晰美观。可以设置节点和边的样式、颜色和大小,以增强可读性。 通过使用 Networkx 实现深度学习计算图的绘制,可以更好地理解和可视化深度学习模型的结构。这有助于我们更好地分析和优化模型,并加深对深度学习原理的理解。此外,Networkx 还提供了许多图论和网络分析的功能,可以进一步拓展我们对深度学习模型的研究和应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值