图的表示:如何存储微博、微信等社交网络中的好友关系?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图的表示:如何存储微博、微信等社交网络中的好友关系?相关的知识,希望对你有一定的参考价值。

参考技术A x博中,两个人可以互相关注,互加好友,那如何存储这些社交网络的好友关系呢?

这就要用到:图。

和树比起来,这是一种更加复杂的非线性表结构。

树的元素称为节点,图中元素叫作顶点(vertex)。图中的一个顶点可以与任意其他顶点建立连接关系,这种建立的关系叫作边(edge)。

社交网络就是典型的图结构。

把每个用户看作一个顶点。如果两个用户之间互加好友,就在两者之间建立一条边。
所以,整个微信的好友关系就可用一张图表示。
每个用户有多少个好友,对应到图中就叫作顶点的度(degree),即跟顶点相连接的边的条数。

不过微博的社交关系跟微信还有点不同,更复杂一点。微博允许单向关注,即用户A关注用户B,但B可不关注A。

这就引入边的“方向”。

A关注B,就在图中画一条从A到B的带箭头的边,表示边的方向。A、B互关,就画一条从A指向B的边,再画一条从B指向A的边,这种边有方向的图叫作“有向图”。边没有方向的图也就叫“无向图”。

无向图中有“度”:一个顶点有多少条边。
有向图中,把度分为:

QQ社交关系更复杂,不仅记录用户之间的好友关系,还记录了两个用户之间的亲密度,如何在图中记录这种好友关系亲密度呢?
这就要用到带权图(weighted graph),每条边都有个权重(weight),可以通过这个权重来表示QQ好友间的亲密度。

最直观的一种存储方法,邻接矩阵(Adjacency Matrix)。

依赖一个二维数组:

无向图,若A[i][j]==1,则A[j][i]==1。实际上,只需存储一个即可。即无向图的二维数组,如果将其用对角线划分为上下两部分,则只需利用上或下面这样一半空间就够了,另外一半其实完全浪费。
如果存储的是稀疏图(Sparse Matrix),即顶点很多,但每个顶点的边并不多,则更浪费空间。
如微信有好几亿用户,对应到图就是好几亿顶点。但每个用户好友并不很多,一般也就三五百个而已。如果我们用邻接矩阵来存储,那绝大部分的存储空间都被浪费了。

但这也并不是说,邻接矩阵的存储方法就完全没有优点。首先,邻接矩阵的存储方式简单、直接,因为基于数组,所以在获取两个顶点的关系时,就非常高效。其次,用邻接矩阵存储图的另外一个好处是方便计算。这是因为,用邻接矩阵的方式存储图,可以将很多图的运算转换成矩阵之间的运算。比如求解最短路径问题时会提到一个Floyd-Warshall算法,就是利用矩阵循环相乘若干次得到结果。

针对上面邻接矩阵比较浪费内存空间,另外一种图存储,邻接表(Adjacency List)。

有点像散列表?每个顶点对应一条链表,链表中存储的是与这个顶点相连接的其他顶点。图中画的是一个有向图的邻接表存储方式,每个顶点对应的链表里面,存储的是指向的顶点。对于无向图来说,也是类似的,不过,每个顶点的链表中存储的,是跟这个顶点有边相连的顶点,你可以自己画下。

如上图示例,若要确定是否存在一条从顶点2到顶点4的边,就要遍历顶点2的链表,看其中是否存在顶点4,而链表存储对缓存不友好。所以邻接表查询两个顶点之间的关系较为低效。

基于链表法解决冲突的散列表中,若链过长,为提高查找效率,可将链表换成其他更高效数据结构,如平衡二叉查找树。
邻接表长得很像散列。所以,也可将邻接表同散列表一样进行“优化”。

可将邻接表中的链表改成平衡二叉查找树。实际可选用红黑树。即可更快速查找两个顶点之间是否存在边。
这里的二叉查找树也可换成其他动态数据结构,如跳表、散列表。
还可将链表改成有序动态数组,通过二分查找快速定位两个顶点之间是否存在边。

虽然微博有向图,微信是无向图,但对该问题,二者思路类似,以微博为例。

数据结构服务于算法,选择哪种存储方法和需支持的操作有关。
对于微博用户关系,需支持如下操作:

因为社交网络是一张稀疏图,使用邻接矩阵存储比较浪费存储空间。所以,这里采用邻接表。

但一个邻接表存储这种有向图也是不够的。查找某用户关注了哪些用户很容易,但若想知道某用户都被哪些用户关注了,即粉丝列表就没法了。

因此,还需一个逆邻接表,存储用户的被关注关系:

基础的邻接表不适合快速判断两个用户是否为关注与被关注关系,所以进行优化,将邻接表的链表改为支持快速查找的动态数据结构。

因需按照用户名称首字母排序,分页获取用户的粉丝列表或关注列表,跳表最合适:插入、删除、查找都非常高效,时间复杂度 ,空间复杂度稍高,是 。
跳表存储数据先天有序,分页获取粉丝列表或关注列表,非常高效。

对小规模数据,如社交网络中只有几万、几十万个用户,可将整个社交关系存储在内存,该解决方案没问题。

可通过哈希算法等数据分片方案,将邻接表存储在不同机器:
如下图,在机器1上存储顶点1,2,3的邻接表,在机器2上,存储顶点4,5的邻接表。逆邻接表的处理方式也一样。当要查询顶点与顶点关系的时候,我们就利用同样的哈希算法,先定位顶点所在的机器,然后再在相应的机器上查找。

还能借助外部存储(比如硬盘),因为外部存储的存储空间比内存多很多:
如用下表存储这样一个图。为高效支持前面定义的操作,可建多个索引,比如第一列、第二列,给这两列都建立索引。

Python实现社交网络可视化,看看你的人脉影响力如何

我们平常都会使用很多的社交媒体,有微信、微博、抖音等等,例如在微博上面,我们会关注某些KOL,同时自己身边的亲朋好友等等也会来关注我们自己,成为我们自己的粉丝。而慢慢地随着粉丝的量不断累积,这层关系网络也会不断地壮大,很多信息也是通过这样的关系网络不断地向外传播,分析这些社交网络并且了解透彻它对于我们做出各项商业决策来说也是至关重要的,今天小编就用一些Python的第三方库来进行社交网络的可视化

数据来源

小编用的数据是来自领英当中的社交数据,由于小编之前也在美国读书,也尝试过在国外找实习、找工作等等,都是通过领英在进行职场上的社交,投递简历、结交职场精英等等,久而久之也逐渐地形成了自己的社交网络,我们将这部分的社交数据下载下来,然后用pandas模块读取

数据的读取和清洗

当然我们先导入需要用到的模块

import pandas as pd
import janitor
import datetime

from IPython.core.display import display, HTML
from pyvis import network as net
import networkx as nx

读取所需要用到的数据集

df_ori = pd.read_csv("Connections.csv", skiprows=3)
df_ori.head()

接下来我们进行数据的清洗,具体的思路就是将空值去除掉,并且数据集当中的“Connected on”这一列,内容是日期,但是数据类型却是字符串,因此我们也需要将其变成日期格式。

df = (
    df_ori
    .clean_names() # 去除掉字符串中的空格以及大写变成小写
    .drop(columns=['first_name', 'last_name', 'email_address']) # 去除掉这三列
    .dropna(subset=['company', 'position']) # 去除掉company和position这两列当中的空值
    .to_datetime('connected_on', format='%d %b %Y')
  )

output

                    company            position connected_on
0                xxxxxxxxxx  Talent Acquisition   2021-08-15
1               xxxxxxxxxxxx   Associate Partner   2021-08-14
2                      xxxxx                猎头顾问   2021-08-14
3  xxxxxxxxxxxxxxxxxxxxxxxxx          Consultant   2021-07-26
4    xxxxxxxxxxxxxxxxxxxxxx     Account Manager   2021-07-19

数据的分析与可视化

先来看一下小编认识的这些人脉中,分别都是在哪些公司工作的

df['company'].value_counts().head(10).plot(kind="barh").invert_yaxis()

output

从上图可以看到,排在比较前面的大公司都是亚马逊、谷歌、Facebook、微软以及JP Morgan等大公司,看来在小编的校友以及人脉当中也就属小编混的最差了

然后我们再来看一下小编所结交的人脉中,大多都是什么职业的

df['position'].value_counts().head(10).plot(kind="barh").invert_yaxis()

output

从上图可以看出,大多都是从事的是软件工程师相关的工作,排在第二的则是数据科学家以及高级软件工程师,看来程序员认识的果然大多也都是程序员。

然后我们来看一下社交网络的可视化图表的绘制,但是在这之前呢,小编需要先说明几个术语,每一个社交网络都包含:

  • 节点:社交网络当中的每个参与者
  • 边缘:代表着每一个参与者的关系以及关系的紧密程度

我们先来简单的绘制一个社交网络,主要用到的是networkx模块以及pyvis模块,

g = nx.Graph()
g.add_node(0, label = "root") # intialize yourself as central node
g.add_node(1, label = "Company 1", size=10, title="info1")
g.add_node(2, label = "Company 2", size=40, title="info2")
g.add_node(3, label = "Company 3", size=60, title="info3")

我们先是建立了4个节点,也分别给他们命名,其中的参数size代表着节点的大小,然后我们将这些个节点相连接

g.add_edge(0, 1)
g.add_edge(0, 2)
g.add_edge(0, 3)

最后出来的样子如下图

我们先从小编的人脉中,他们所属的公司来进行网络的可视化,首先我们对所属的公司做一个统计排序

df_company = df['company'].value_counts().reset_index()
df_company.columns = ['company', 'count']
df_company = df_company.sort_values(by="count", ascending=False)
df_company.head(10)

output

                            company  count
0                            Amazon     xx
1                            Google     xx
2                          Facebook     xx
3   Stevens Institute of Technology     xx
4                         Microsoft     xx
5              JPMorgan Chase & Co.     xx
6         Amazon Web Services (AWS)     xx
9                             Apple      x
10                    Goldman Sachs      x
8                            Oracle      x

然后我们来绘制社交网络的图表

# 实例化网络
g = nx.Graph()
g.add_node('myself') # 将自己放置在网络的中心

# 遍历数据集当中的每一行
for _, row in df_company_reduced.iterrows():

    # 将公司名和统计结果赋值给新的变量
    company = row['company']
    count = row['count']

    title = f"<b>{company}</b> – {count}"
    positions = set([x for x in df[company == df['company']]['position']])
    positions = ''.join('<li>{}</li>'.format(x) for x in positions)

    position_list = f"<ul>{positions}</ul>"
    hover_info = title + position_list

    g.add_node(company, size=count*2, title=hover_info, color='#3449eb')
    g.add_edge('root', company, color='grey')

# 生成网络图表
nt = net.Network(height='700px', width='700px', bgcolor="black", font_color='white')
nt.from_nx(g)
nt.hrepulsion()

nt.show('company_graph.html')
display(HTML('company_graph.html'))

output

我们从上面也能看到小编与谷歌、Facebook以及亚马逊、微软等公司的联系较为密切,认识较多从这些大公司当中出来的员工,与此同时呢,我们来可视化一下小编人脉中各种岗位的分布,我们先做一个统计排序

df_position = df['position'].value_counts().reset_index()
df_position.columns = ['position', 'count']
df_position = df_position.sort_values(by="count", ascending=False)
df_position.head(10)

output

                           position  count
0                 Software Engineer     xx
1                    Data Scientist     xx
2          Senior Software Engineer     xx
3                      Data Analyst     xx
4             Senior Data Scientist     xx
5     Software Development Engineer     xx
6  Software Development Engineer II     xx
7                           Founder     xx
8                     Data Engineer     xx
9                  Business Analyst     xx

然后进行网络图的绘制

g = nx.Graph()
g.add_node('myself') # 将自己放置在网络的中心

for _, row in df_position_reduced.iterrows():

    # 将岗位名和统计结果赋值给新的变量
    position = row['position']
    count = row['count']

    title = f"<b>{position}</b> – {count}"
    positions = set([x for x in df[position == df['position']]['position']])
    positions = ''.join('<li>{}</li>'.format(x) for x in positions)

    position_list = f"<ul>{positions}</ul>"
    hover_info = title + position_list

    g.add_node(position, size=count*2, title=hover_info, color='#3449eb')
    g.add_edge('root', position, color='grey')

# 生成网络图表
nt = net.Network(height='700px', width='700px', bgcolor="black", font_color='white')
nt.from_nx(g)
nt.hrepulsion()

nt.show('position_graph.html')

output

可以看出认识的大多数人都是“软件工程师”这个职业,其次便是数据科学家以及高级软件工程师等职位。

以上是关于图的表示:如何存储微博、微信等社交网络中的好友关系?的主要内容,如果未能解决你的问题,请参考以下文章

Python实现社交网络可视化,看看你的人脉影响力如何

Python 实现社交网络可视化,看看你的人脉影响力如何

WordPress连接微博插件

微信微博好友数据结构

移动端小功能——置顶、主动排序

树的广度优先搜索(上)