• 社会科学问题研究的计算实践——2、同质性与社会网络的演化(计算实践:同质性作用下的网络演化过程)


    学习资源来自,一个哲学学生的计算机作业 (karenlyu21.github.io)


    1、背景问题

    上一节的内容主要是对社会网络的一种静态考察,但现实社会网络总是处在演变之中。一个最为重要的演化现象是,一些原本互不认识的节点可能会在某一时间点结识,对应在网络中建立了一条新的边。

    两个人的结识有一定规律可循,它受着“机会”、“信任”和“动机”的影响。当两个陌生人之间有中介(例如共同朋友)时,这一中介就会大大增加他们俩相遇的机会,也让他们更容易彼此信任,有时也会有让他们相互结识的动机。

    两个节点经由共同朋友的中介结识,称为“三元闭包”(triadic closure)。课上,李老师给我们介绍了一个基于大数据的研究,它借助一所大学里的邮件往来信息,验证了三元闭包原理:两个互不认识的人,共同朋友越多,一段时间后他们结识的概率越高

    课上还提到,三元闭包原理的一个细化版本是“强三元闭包原理”。对于一个社会网络,我们把其中的边标识为两类,一类指示强关系,另一类指示弱关系。强三元闭包原理说的是,如果节点i与节点j有强关系,节点i与节点k也有强关系,那么,节点j与节点k之间至少应该有弱关系

    这里的分析还可以引入“捷径”(local bridge)概念,亦即其两端节点没有“共同朋友” 的边。基于“强三元闭包原理”,我们可以推导出“捷径→弱关系”定理:在标识了强弱关系的网络中,如果一个节点符合强三元闭包原理且有两个强关系朋友(设为C和D),则它与任何捷径相连的朋友(设为A和B)之间一定是弱关系。否则,C或D一定是A和B的共同朋友,A和B之间的边就不再是捷径。

    社会网络演化的一个结果就是“同质性”:朋友(相近的人们)之间具有某种特征相似性,例如来自同一个地方。我们用“加入了某个‘社团’或‘俱乐部’”或“从事某件‘事’”,来刻画一个人的特征。当我们把“社团”概念考虑进社会网络以后,社会网络的演化不仅有“三元闭包”原理这一个规律,而且还有另外两个规律:

    • “社团闭包”(social focal closure)原理:两个人经由共同社团结识,称为“社团闭包”。这种闭包里,“物以类聚,人以群分”,社会网络的同质性得以加强。
    • “会员闭包”(membership closure)原理:社团闭包和三元闭包都讨论的是两个人之间新友谊的产生。除此以外,一个人在什么情况下会加入一个社团呢?与前两种闭包类似,当这个人已经有几个朋友参与了某一社团,ta有更多机会知晓、信任这一社团,更有动机加入它,总之是就更容易加入这个社团。这就是“会员闭包”。这种闭包里,“近朱者赤,近墨者黑”,社会网络的同质性得以加强。

    2、计算实践:同质性作用下的网络演化过程

    2.1、作业描述与算法思路

    本次作业的任务是,基于三个闭包原理,用编程来模拟社会网络的演化。

    为了操作的方便,我们把三个闭包原理简化为三个门槛假设:

    • 三元闭包:如果两个不相识的人有了s个或更多共同朋友,则他们下一时点前会成为朋友
    • 社团闭包:如果两个不相识的人参加了f个或更多相同的俱乐部,则他们在下一时点前会成为朋友
    • 会员闭包:如果某人有m个或更多朋友参加了某个俱乐部,则他在下一时点前参加该俱乐部

    正如我们可以用邻接矩阵来表示一个社会网络中的朋友关系,我们还可以用一个归属矩阵来表示人对社交聚点(social focal)的归属关系。

    image.png

    上面这个社会网络可以用下面两个矩阵表示:

    • 邻接矩阵:表示人与人之间的朋友关系;

    image.png

    • 归属矩阵:表示人与社团之间的归属关系。例如,矩阵的第一行(1 0)表示,人1加入了社团1,但没有加入社团2。

    image.png

    基于社会网络的矩阵化,三个闭包原理可以表示成如下的数学关系:

    • 三元闭包:节点i与节点j建立一条边,当A[i]⋅A[j] ≥ s;
    • 社团闭包:节点i与节点j建立一条边,当B[i]⋅B[j] ≥ f;
    • 会员闭包:节点i与社团c建立一条边,当A[i]⋅B[ ] [c] ≥ m。

    给定一个社会网络,我们考察它是否满足了上面三个临界条件,加上该加的边,生成下一轮的社会网络,我们再做同样的操作。如此循环,直到稳定,没有新边可加。

    2.2、编程实现与要点说明

    前提说明,这里邻接矩阵和归属矩阵采用上图社会网络矩阵。

    首先,我们还是需要调用读取数据文件的函数,把邻接矩阵和归属矩阵分别存储在numpy 2d-arrays里,前者是A,后者是Bsfm的值由用户指定。

    def arrayGen_A(filename):
        f = open(filename, 'r')
        r_list = f.readlines()  
        f.close()
        A = []
        for line in r_list:
            if line == '\n':
                continue
            line = line.strip('\n')
            line = line.strip() 
            row_list = line.split() 
            for k in range(len(row_list)):
                row_list[k] = row_list[k].strip()
                row_list[k] = int(row_list[k])
            A.append(row_list)
        An = len(A[0])
        A = np.array(A)
        return A, An
    
    def arrayGen_B(filename):
        f = open(filename, 'r')
        r_list = f.readlines() 
        f.close()
        B = []
        for line in r_list:
            if line == '\n':
                continue
            line = line.strip('\n')
            line = line.strip()  
            row_list = line.split()  
            for k in range(len(row_list)):
                row_list[k] = row_list[k].strip()
                row_list[k] = int(row_list[k])
            B.append(row_list)
        Bn = len(B[0])
        B = np.array(B)
        return B, Bn
    
    filename_A = input('请输入邻接矩阵文件名:')
    A, An = arrayGen_A(filename_A)
    filename_B = input('请输入归属矩阵文件名:')
    B, Bn = arrayGen_B(filename_B)
    
    s = int(input('请输入三元闭包的临界值(请输入整数):'))
    f = int(input('请输入社团闭包的临界值(请输入整数):'))
    m = int(input('请输入会员闭包的临界值(请输入整数):'))
    

    输入值的一个例子如下:

    >>> 输入邻接矩阵文件名:./a.txt
    >>> 输入归属矩阵文件名:./b.txt
    >>> 请输入三元闭包的临界值(请输入整数):3
    >>> 请输入社团闭包的临界值(请输入整数):2
    >>> 请输入会员闭包的临界值(请输入整数):2
    

    接着,我写了检验闭包条件的函数edgeAdder,便于重复调用:输入两个矩阵的数据ABsfm的值,输出满足闭包条件、加上新边后的两个矩阵。

    def edgeAdder(A, B, an, bn, s, f, m):
    

    三元闭包:一一检查网络中的边,当A[i]×A[j]≥s时,亦即np.dot(A[i], A[j]) >= s时,节点i与节点j建立一条边。这时我不应直接在邻接矩阵中作修改,否则会影响接下来对会员闭包的检验;我先把需要加的边存储在A_alters里。

    	A_alters = []
        B_alters = []
        for i in range(an):
            for j in range(i+1, an):
                if A[i][j] == 1:
                    continue
                if np.dot(A[i], A[j]) >= s:  # 返回两个数组的点积
                    A_alters.append([i, j])
                    print('三元闭包(人,人):\t%i %i' % (i, j))
    

    社团闭包:当np.dot(B[i], B[j]) >= f时,节点i与节点j建立一条边,存储在A_alters里。

        for i in range(an):
            for j in range(i+1, an):
                if A[i][j] == 1:
                    continue
                if np.dot(B[i], B[j]) >=f:
                    A_alters.append([i, j])
                    print('社团闭包(人,人):\t%i %i' % (i, j))
    

    会员闭包:当np.dot(A[i], B[:,c]) >= m时,节点i与社团c建立一条边,存储在B_alters里。

        for i in range(an):
            for c in range(bn):
                if B[i][c] == 1:
                    continue
                if np.dot(A[i], B[:,c]) >= m:
                    B_alters.append([i, c])
                    print('会员闭包(人,事):\t%i %i' % (i, c))
    

    根据A_altersB_alters的内容,统一对矩阵做出修改,加上该加的边。

        for item in A_alters:
            A[item[0]][item[1]] = 1
            A[item[1]][item[0]] = 1
        for item in B_alters:
            B[item[0]][item[1]] = 1
        return A, B, len(A_alters)+len(B_alters)
    

    主程序的内容,就是一轮接一轮地调用edgeAdder函数,加上该加的边,直到无边可加、演化结束。

    round = 1
    print('第 1 轮:')
    while True:
        A_new, B_new, alters = edgeAdder(A, B, An, Bn, s, f, m)
        if alters == 0:
            break
        else:
            A = A_new
            B = B_new
            round += 1
            print('第 %i 轮:' % round)
    print('演化结束!')
    
    第 1 轮:
    三元闭包(人,人):    5 6
    三元闭包(人,人):    7 9
    会员闭包(人,事):    0 1
    会员闭包(人,事):    3 0
    第 2 轮:
    社团闭包(人,人):    1 3
    会员闭包(人,事):    6 0
    第 3 轮:
    会员闭包(人,事):    7 0
    会员闭包(人,事):    9 0
    第 4 轮:
    会员闭包(人,事):    8 0
    第 5 轮:
    演化结束!
    

    3、完整代码

    附上可运行的完整代码,以供学习和交流!

    同质性作用下的网络演化过程

    import numpy as np
    
    
    def arrayGen(filename, prt=''):
        f = open(filename, 'r')
        r_list = f.readlines()
        f.close()
        array_nl = []
        for line in r_list:
            if line == '\n':
                continue
            line = line.strip('\n')
            line = line.strip()
            row_list = line.split(' ')
            for k in range(len(row_list)):
                row_list[k] = int(row_list[k])
            array_nl.append(row_list)
        n = len(array_nl[0])
        print('输入%s矩阵文件名:%s' % (prt, filename))
        array = np.array(array_nl)
        return array, n
    
    
    def edgeAdder(A, B, an, bn, s, f, m):
    # triadic closure adder
        A_alters = []
        B_alters = []
        for i in range(an):
            for j in range(i+1, an):
                if A[i][j] == 1:
                    continue
                if np.dot(A[i], A[j]) >= s:
                    A_alters.append([i, j])
                    print('三元闭包(人,人):\t%i %i' % (i, j))
    # social focal closure adder
        for i in range(an):
            for j in range(i+1, an):
                if A[i][j] == 1:
                    continue
                if np.dot(B[i], B[j]) >= f:
                    A_alters.append([i,j])
                    print('社团闭包(人,人):\t%i %i' % (i, j))
    # membership closure adder
        for i in range(an):
            for c in range(bn):
                if B[i][c] == 1:
                    continue
                if np.dot(A[i], B[:, c]) >= m:
                    B_alters.append([i, c])
                    print('会员闭包(人,事):\t%i %i' % (i, c))
        for item in A_alters:
            A[item[0]][item[1]] = 1
            A[item[1]][item[0]] = 1
        for item in B_alters:
            B[item[0]][item[1]] = 1
        return A, B, len(A_alters) + len(B_alters)
    
    
    fname_a = './a.txt'
    fname_b = './b.txt'
    A, An = arrayGen(fname_a, '邻接')
    B, Bn = arrayGen(fname_b, '归属')
    try:
        s = int(input('请输入三元闭包的临界值(请输入整数):'))  # triadic closure
        f = int(input('请输入社团闭包的临界值(请输入整数):'))  # social focal closure
        m = int(input('请输入会员闭包的临界值(请输入整数):'))  # membership closre
    except:
        print('输入错误,使用默认值s = 3, f = 2, m = 2。')
        s = 3
        f = 2
        m = 2
    
    
    round = 1
    print('第 1 轮:')
    while True:
        A_new, B_new, alters = edgeAdder(A, B, An, Bn, s, f, m)
        if alters == 0:
            break
        else:
            A = A_new
            B = B_new
            round += 1
            print('第 %i 轮:' % round)
    print('演化结束!')
    print(A)
    print(B)
    
  • 相关阅读:
    Linux系统网络文件配置
    Linux系统修改日期时间
    8、mysql索引
    7、mysql正则表达式、事务、alter命令
    6、mysql数据操作
    5、mysql数据类型
    4、mysql数据库操作
    3、mysql管理
    2、mysql安装
    1、mysql教程
  • 原文地址:https://www.cnblogs.com/wangzheming35/p/15865357.html
Copyright © 2020-2023  润新知