西电数据挖掘实验3——复杂网络社团检测

一、实验内容

  复杂网络是描述复杂系统的有力工具,其中每个实体定义成一个节点,实体间的交互关系定义为边。复杂网络社团结构定义为内紧外松的拓扑结构,即一组节点的集合,集合内的节点交互紧密,与外界节点交互松散。复杂网络社团结构检测广泛的应用于信息推荐系统、致癌基因识别、数据挖掘等领域。

  本实验利用两类数据:模拟数据与真实数据。模拟数据有著名复杂网络学者Mark Newmann所提出,该网络包括128个节点,每个节点的度为16,网络包含4个社团结构,每个社团包含32个节点,每个节点与社团内部节点有 k 1 k_1 k1个节点相互链接,与社团外部有 k 2 k_2 k2个节点相互链接 ( k 1 + k 2 = 16 k_1+k_2=16 k1+k2=16)。通过调节参数 k 2 k_2 k2 k 2 = 1 , 2 , . . . , 8 k_2=1,2,...,8 k2=1,2,...,8)增加社团构建检测难度。

  真实数据集:跆拳道俱乐部数据由34个节点组成,由于管理上的分歧,俱乐部分解成两个社团。

二、分析及设计

  Step1:导入网络数据

  利用邻接矩阵 A A A来存储网络,其中 A i j A_{ij} Aij表示第 i i i个节点与第 j j j个节点的是否有边相互链接,1表示有,0表示没有。

  在本次实验中,我利用Python中的networkx包处理网络数据。networkx能够有效地组织与管理图数据结构,并且其中封装了很多与图操作相关的函数调用,能够提高本次实验程序的编写效率。

  Step2:根据网络结构特征给出节点相似性度量指标

  给定节点 i i i, 其邻居节点定义为与该节点相链接的所有节点组成的集合,即 N ( i ) = { j ∣ A i j = 1 , j = 1 , 2 , . . . , n } N(i)=\{j|A_{ij}=1,j=1,2,...,n\} N(i)={jAij=1,j=1,2,...,n}。给定一对节点 ( i , j ) (i,j) (i,j),其相似性定义如下:
S i j = ∣ N ( i ) ∩ N ( j ) ∣ ∣ N ( i ) ∪ N ( j ) ∣ S_{ij} = \frac{|N(i) \cap N(j)|}{|N(i) \cup N(j)|} Sij=N(i)N(j)N(i)N(j)
其中 ∣ N ( i ) ∩ N ( j ) ∣ |N(i) \cap N(j)| N(i)N(j)表示集合 N ( i ) ∩ N ( j ) N(i) \cap N(j) N(i)N(j)中元素的个数。

  Step3:采用贪婪算法提取模块

  随机选择一个未聚类的节点作为当前社团C,提取出社团C所有未聚类的邻居节点 N ( c ) N(c) N(c)。选择使得社团密度降低最小的那个节点 v v v添加到社团 C C C,更新当前社团为 C = C ∪ v C = C \cup v C=Cv(若某节点已经找不到其未聚类的邻居节点,则认为该节点自成一个社团),持续该过程直到当前社团的密度小于某个阈值。当一个社团提取完成后,将其加入存放总社团的集合中,即 C l u b s = C l u b s ∪ C Clubs = Clubs \cup C Clubs=ClubsC。此后,再从剩余的未被分类的节点中任选一个出来作为新社团的初始节点,重复进行上述操作,直到所有节点均被归类到某一社团中,算法结束。计算过程中我使用社团中所有节点对的相似度之和除以节点对总数(组合数)再除以2来定义的社团密度,具体函数表达式如下:
D e n s i t y = ∑ i , j s i j C l e n ( c ) 2 / 2 ∈ ( 0 , 2 ) Density = \frac{\sum_{i,j} s_{ij}}{C_{len(c)}^2 / 2} \in(0,2) Density=Clen(c)2/2i,jsij(0,2)
其中 l e n ( c ) len(c) len(c)是社团 c c c中的节点个数, C l e n ( c ) 2 C_{len(c)}^2 Clen(c)2是从社团 c c c的节点中任选2个节点的组合数, i , j i,j i,j是从 c c c中任取2个节点的组合对应的节点标号。

  Step4:采用Cytoscape工具,可视化聚类结果

  由于Cytoscape对于被导入的文件中的数据格式有一定的要求,所以我先用Python对karate.gml中的数据进行了处理,输出了符合Cytoscape导入数据规范的边数据,然后再将边数据导入txt文件中,最后导入Cytoscape。导入Cytoscape后,根据Python计算出的社团分类结果,将这34个节点分别着色,每个社团中的节点着同色,最终完成可视化操作。

三、详细实现

  由于本次实验需要处理图数据,对图中节点进行相关操作,而Python中有很强大的networkx包便于我们构建和操作复杂的图结构,故我选择用Python编写本次实验的程序。具体代码实现如下(所有重要语句均已给出相应的注释)

1.先导入本实验需要用到的包:

import random
import copy
import numpy as np
import networkx as nx
import matplotlib.pyplot as plt

2.定义函数show_info(),显示一下社团网络的相关信息:

# 显示图G的相关信息
def show_info(G):
    # 输出所有节点和所有边
    print("nodes:", G.nodes(), '\n')
    print("edges:", G.edges(), '\n')
    # 输出节点总数和边总数
    nodes_num = G.number_of_nodes()
    edges_num = G.number_of_edges()
    print("number of nodes:", nodes_num)
    print("number of edges:", edges_num)

3.定义函数clac_s(),利用题目中给出的相似性计算公式计算相似性矩阵:

# 计算相似性矩阵
def calc_s(G):
    # 初始化s
    nodes_num = G.number_of_nodes()
    sim = np.zeros((nodes_num,nodes_num))
    # 开始计算s
    for i in range(1, nodes_num + 1):
        for j in range(1, nodes_num + 1):
            # 根据定义计算i,j的相似性
            sim[i-1][j-1] = len(G.adj[i].keys() & G.adj[j].keys()) / len(G.adj[i].keys() | G.adj[j].keys())
    print('图G的相似性矩阵为:')
    print(sim)
    return sim

4.定义函数calc_density()计算社团密度:

# 计算社团密度(采用基于相似度度量的密度)
def calc_density(c, s):
    # 求出社团中的点数和边数
    v = c.number_of_nodes()
    e = c.number_of_edges()
    # 若社团中只有一个点,则密度最大,为1
    if len(c) == 1:
        return 1.0
    # 初始化总相似度
    sum_sim = 0.0
    for node_i in list(c.nodes()):
        for node_j in list(c.nodes()):
            # 不计算自身
            if (node_i != node_j) & (node_j.__index__() > node_i.__index__()):
                sum_sim = sum_sim + s[node_i-1][node_j-1]
    # 最后除以社团c中node_i,node_j的组合数再除以系数
    density_2 = sum_sim / ( ( v * (v - 1) ) / 4 )
    # 最后返回社团密度
    return density_2

5.定义函数find_nbrs()求出社团所有未聚类的邻居节点:

# 求社团未聚类的邻居节点
def find_nbrs(G, G_copy, c):
    # 初始化
    nbrs = []
    for node in list(c.nodes()):
        # node的邻居应该在G中找
        node_nbrs = list(G.adj[node].keys())
        nbrs = list(set(nbrs) | (set(node_nbrs)))
    # 未聚类的邻居节点应该在G_copy中找,最后以数值形式存放在列表里
    final_nbrs = list(set(nbrs) & (set(list(G_copy.nodes()))))
    return final_nbrs

6.定义核心函数club_julei()利用贪心算法求解所有社团,算法思想在上文中已经阐述,算法细节见注释:

# 这里是初始化全局变量picked_node
picked_node = 0
# 利用贪心算法找到所有的社团——核心函数
def club_julei(G, s, t):
    # 初始化列表存放所有社团列表
    clubs = []
    # 深拷贝G,否则对G_copy操作也会同时影响G
    G_copy = copy.deepcopy(G)
    # 当G_copy的节点列表不为空时循环寻找社团,G_copy节点列表为空时说明所有的社团均已被找到
    while(G_copy.nodes()):
        # 注意列表下标从0开始
        c = nx.Graph()
        idx = random.randint(0, len(G_copy)-1)
        # 随机选择一个G_copy中的节点初始化社团c(G_copy中节点都是未被聚类的)
        randpick_node = list(G_copy.nodes())[idx]
        c.add_node(randpick_node)
        # 从G_copy中删去该节点
        G_copy.remove_node(randpick_node)
        # 死循环构造社团直到社团密度小于阈值
        while(1):
            # 计算原始社团密度
            density_old = calc_density(c, s)
            # 求当前社团未聚类的邻居节点
            candinodes = find_nbrs(G, G_copy, c)
            if len(candinodes) == 0:
                clubs.append(list(c.nodes()))
                break
            minval = 1.0
            global picked_node # 全局变量picked_node 
            # 寻找使社团密度降低最小的那个节点
            for node in candinodes:
                c.add_node(node)
                # 计算加入该节点后的社团密度
                density_new = calc_density(c, s)
                # 计算社团密度减小量
                dec = density_old - density_new
                # 若dec<minval,更新数据
                if dec < minval:
                    minval = dec
                    picked_node = node
                # 测试完当前结点后删除该节点,再循环测下一个
                c.remove_node(node)
            # 循环结束后picked_node已找到,将该节点加入社团c
            # 这里判断是否为0是为了应对picked_node未被赋值的情况
            if picked_node == 0:
                continue
            else:
                c.add_node(picked_node)
            # 同时在G中删除该点,因为该点已被分类
            if picked_node in list(G_copy.nodes()):
                G_copy.remove_node(picked_node)
            # 若当前社团的密度小于阈值,则将其加入到clubs列表中
            if calc_density(c, s) < t:
                #print(calc_density(c, s))
                clubs.append(list(c.nodes()))
                break
    # 返回所有社团
    return clubs

四、实验结果

 社团聚类结果随着社团密度阈值选取的不同(0.2, 0.4, 0.5)而变化的情况如下图所示:
在这里插入图片描述
 我用Cytoscape绘制了阈值t分别取0.2,0.4,0.5时的社团聚类结果图(其余阈值下画法类似,不再展示结果),效果如下:

 t = 0.2时的社团聚类结果:

 t = 0.4时的社团聚类结果:

 t = 0.5时的社团聚类结果:

  分析上图可知,当阈值t = 0.4左右时,社团聚类的效果最好。当密度阈值设定为一个比较小的数时(如小于0.1),所有节点被分到同一个社团中;当密度阈值稍微增大后(如0.2-0.5),将会出现更多的社团,当密度阈值取到比较大的值后(如大于0.6),基本上一个社团中只存在两三个节点。当然,即使在同一阈值下,每次运行程序得到的社团聚类结果也是不同的,这是因为每次随机选取的初始节点不同,最终合成的社团也会不同。

  在实验中,我尝试过用基本的图密度定义: d e n s i t y = 2 ∣ E ∣ ∣ V ∣ ⋅ ∣ V − 1 ∣ density = \frac{2|E|}{|V|·|V-1|} density=VV1∣2∣E去计算社团的密度,也尝试过用图密度+基于相似性的密度去计算社团密度,但是后来发现这两种方法划分社团的效果并不理想,具体表现为当阈值还不是很大(如0.4)时就已经划分出很多小社团了,不符合我们的预期。

整体源码:数据挖掘实验3

  • 4
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
### 回答1: 西安电子科技大学编译原理实验SQL解释器是一个用于解析和执行SQL语句的工具。SQL是一种结构化查询语言,用于管理和操作关系型数据库中的数据。 该解释器的主要功能是将用户输入的SQL语句进行解析和执行。首先,它会将输入的SQL语句进行词法分析,将其划分为不同的单词和符号,并生成一个词法分析树。然后,语法分析器将根据语法规则对词法分析树进行语法分析,生成一个语法分析树。 接下来,解释器会对语法分析树进行语义分析,检查语句是否符合数据库的定义和限制,例如表的存在性、属性的类型和完整性约束等。如果存在错误,解释器会给出相应的错误信息。如果语句通过了语义分析,解释器将根据语义分析树生成对应的查询执行计划。 最后,解释器将执行查询执行计划,从数据库中获取所需的数据,并返回给用户。在执行过程中,解释器会处理各种SQL语句,例如查询语句、更新语句、插入语句等,以及各种子句和操作符,如SELECT、JOIN、WHERE等。 西安电子科技大学编译原理实验SQL解释器的开发需要掌握词法分析、语法分析、语义分析和查询执行等相关知识与技术。它在数据库管理系统中具有重要的应用价值,能够提供方便、高效和准确的数据操作和管理功能,为用户提供了便捷的数据库操作界面。 ### 回答2: 西安电子科技大学编译原理实验中,我们设计了一个SQL解释器。SQL(Structured Query Language)是一种用于管理和处理关系型数据库的编程语言。我们的目标是设计一个能够解析和执行SQL语句的程序。 在我们的实验中,我们首先学习了SQL语言的语法和语义规则,了解了其基本的结构和常用的操作。然后,我们使用编译原理中的词法分析和语法分析技术,对输入的SQL语句进行解析。词法分析阶段将输入的SQL语句拆分为一个个单词(例如关键字、标识符、运算符等),然后语法分析阶段通过对这些单词进行组合和判断,构建SQL语句的抽象语法树。 接下来,我们需要对抽象语法树进行语义分析。通过检查语法树中每个节点的类型和属性,我们可以判断SQL语句在语义上是否合法。例如,我们可以验证表是否存在、列是否匹配、约束是否满足等。如果发现语义错误,我们将在解释器中报告错误信息。 一旦经过语义分析,我们就可以执行SQL语句了。在解释器中,我们根据SQL语句的类型(例如查询、插入、删除、更新等),调用相应的数据库操作,如查询表、插入记录等。我们还可以通过解释器实现一些高级功能,比如支持条件查询、连接查询、聚合函数等。 在实验中,我们还考虑了性能优化。例如,我们可以对SQL语句进行查询优化,选择合适的查询计划来提高查询速度。我们还可以使用缓存来避免重复执行相同的SQL语句。 总的来说,通过实现SQL解释器,我们能够更好地理解和学习编译原理的知识,同时也能够提升对SQL语言的理解和应用能力。通过实验,我们可以更好地掌握SQL语句的解析和执行过程,为未来在数据库设计和开发中更好地使用SQL语言打下基础。 ### 回答3: 西安电子科技大学编译原理 实验中的SQL解释器是一款用于解释和执行SQL语句的程序。SQL(Structured Query Language)是一种用于管理和操作关系数据库的语言,而编译原理是研究如何将高级语言翻译成机器语言的学科。 该SQL解释器的主要功能是接受用户输入的SQL语句,对其进行词法分析、语法分析和语义分析,最终生成并执行相应的数据库操作。 在词法分析过程中,解释器会将输入的SQL语句分解为一个个单词或符号。然后,在语法分析阶段,解释器会根据SQL语法规则,将单词和符号组合成具有语法结构的语句树。接着,在语义分析过程中,解释器会验证语句的语义正确性,并进行必要的类型检查和作用域分析。 完成了上述步骤后,解释器将根据语句树生成相应的数据库操作。这些操作可以包括查询数据、插入记录、更新数据和删除数据等。解释器会调用数据库系统提供的接口,将生成的操作发送给数据库引擎执行,然后将结果返回给用户。 通过实现SQL解释器,我们可以更深入地理解编译原理的相关概念和技术,同时也能够提升对数据库管理系统的理解和应用能力。此外,SQL解释器还可以用于实际开发中,对于处理和管理大量数据的应用程序来说,具有重要的实际意义。 总之,西安电子科技大学编译原理实验中的SQL解释器是一个用于解释和执行SQL语句的程序,它通过词法分析、语法分析和语义分析等步骤,将SQL语句转化为数据库操作,并与数据库引擎进行交互,实现对数据库的操作和管理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Polaris_T

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值