基于Spark GraphX的图形数据分析
一、图(Graph)
- 为什么需要图计算
- 许多大数据以大规模图或网络的形式呈现
- 许多非图结构的大数据,常会被转换为图模型进行分析
- 图数据结构很好地表达了数据之间的关联性
1.1 图(Graph)的基本概念
- 图是由顶点集合(vertex)及顶点间的关系集合(边edge)组成的一种网状数据结构
- 通常表示为二元组:Gragh=(V,E)
- 可以对事物之间的关系建模
- 应用场景
- 在地图应用中寻找最短路径
- 社交网络关系
- 网页间超链接关系
1.2 图的术语
1.2.1 顶点和边
上面的图可以了解到图Graph包含三个顶点,即v1,v2,v3,每个顶点间各有一条互相关联的边,即下方所表述的意思。
Graph=(V,E)
集合V={v1,v2,v3}
集合E={(v1,v2),(v1,v3),(v2,v3)}
1.2.2 有向图和无向图
- 有向图
代码表述即:
G=(V,E)
V={A,B,C,D,E}
E={<A,B>,<B,C>,<B,D>,<C,E>,<D,A>,<E,D>}
- 无向图
代码表述即:
G=(V,E)
V={A,B,C,D,E}
E={(A,B),(A,D),(B,C),(B,D),(C,E),(D,E)}
1.2.3 有环图和无环图
- 有环图
- 包含一系列顶点连接的回路(环路)
- 包含一系列顶点连接的回路(环路)
- 无环图
- DAG即为有向无环图
- DAG即为有向无环图
1.2.4 度
- 出度: 指从当前顶点指向其他顶点的边的数量
- 入度: 其他顶点指向当前顶点的边的数量
- 以下图为例,我们可以看到,11号顶点的度是最多的。共有5个度,其中出度3条,出向顶点2,顶点9和顶点10,入度2个,分别来自顶点5和顶点7。
1.3 图的经典表示法
邻接矩阵
规则
- 1、对于每条边,矩阵中相应单元格值为1
- 2、对于每个循环,矩阵中相应单元格值为2,方便在行或列上求得顶点度数
由上方的规则,我们可以将下方的图转换成右边的二维矩阵。
二、Spark GraphX
2.1 简介
- GraphX是Spark提供分布式图计算API
- GraphX特点
- 基于内存实现了数据的复用与快速读取
- 通过弹性分布式属性图(Property Graph)统一了图视图与表视图
- 与Spark Streaming、Spark SQL和Spark MLlib等无缝衔接
2.2 GraphX核心抽象
- 弹性分布式属性图(Resilient Distributed Property Graph)
- 顶点和边都带属性的有向多重图
- 一份物理存储,两种视图
- 对Graph视图的所有操作,最终都会转换成其关联的Table视图的RDD操作来完成
三、GraphX API
3.1 创建Graph
3.1.1 我们以下面这张图作为案例来进行创建图
- 我们通过图可以看出,四个顶点Id分别为,3,7,5,2,他们的property都是一个包含name和job,而他们的关系也可以通过图的edge来获取到,所以我们可以得到下面的创建图标的代码。
val users: RDD[(Long, (String, String))] = sc.parallelize(
Array(
(3L, ("rxin", "student")),
(7L, ("jgonzal", "postdoc")),
(5L, ("franklin", "professor")),
(2L, ("istoica", "professor")))
)
val relationship: RDD[Edge[String]] = sc.parallelize(
Array(
Edge(3, 7, "Collaborator"),
Edge(5, 3, "Advisor"),
Edge(2, 5, "Colleague"),
Edge(5, 7, "PI"))
)
val userGraph = Graph(users,relationship)
3.1.2 通过加载文件的方式创建图
示例文件follwers.txt内容:
2 3
3 4
1 4
2 4
创建图代码如下:
import org.apache.spark.graphx.GraphLoader
val graphLoad = GraphLoader.edgeListFile(sc,"file:opt/kb09file/follwers.txt")
- 加载边列表文件创建图,文件每行描述一条边,格式:srcId dstId。顶点与边的属性均为1
3.2 查看图信息
3.2.1 查看图顶点、边和图数据信息
- 关键字:
- 顶点:vertices
- 边:edge
- 图:triplets
我们通过上面的操作已经得到了两个图,我们查看一下userGraph这张图的信息。
println("顶点数据------")
userGraph.vertices.collect.foreach(println)
println("边数据------")
userGraph.edges.collect.foreach(println)
println("图数据------")
userGraph.triplets.collect.foreach(println)
/**
顶点数据------
(2,(istoica,professor))
(3,(rxin,student))
(5,(franklin,professor))
(7,(jgonzal,postdoc))
边数据------
Edge(3,7,Collaborator)
Edge(5,3,Advisor)
Edge(2,5,Colleague)
Edge(5,7,PI)
图数据------
((3,(rxin,student)),(7,(jgonzal,postdoc)),Collaborator)
((5,(franklin,professor)),(3,(rxin,student)),Advisor)
((2,(istoica,professor)),(5,(franklin,professor)),Colleague)
((5,(franklin,professor)),(7,(jgonzal,postdoc)),PI)
*/
3.2.2 顶点数量、边数量、度、入度、出度
- 关键字:
- 顶点数量:numVertices
- 边数量:numEdges
- 度:degrees
- 入度:inDegrees
- 出度:outDegrees
继续以上面的userGraph为例。
println("定点数--------")
println(userGraph.numVertices)
println("边数--------")
println(userGraph.numEdges)
println("度--------")
userGraph.degrees.collect.foreach(println)
println("入度--------")
userGraph.inDegrees.collect.foreach(println)
println("出度--------")
userGraph.outDegrees.collect.foreach(println)
/**
定点数--------
4
边数--------
4
度--------
(2,1)
(3,2)
(5,3)
(7,2)
入度--------
(3,1)
(5,1)
(7,2)
出度--------
(2,1)
(3,1)
(5,2)
*/