图(Graph)概念
- 图是由顶点集合(vertex)及顶点间的关系集合(边edge)组成的一种网状数据结构
- 通常表示为二元组:Gragh=(V,E)
- 可以对事物之间的关系建模
- 应用场景
- 在地图应用中寻找最短路径
- 社交网络关系
- 网页间超链接关系
顶点(vertex)与边(Edge)
- 顶点与边的关系
Graph=(V,E)
集合V={v1,v2,v3}
集合E={(v1,v2),(v1,v3),(v2,v3)}
有向图
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)}
有环图
- 包含一系列顶点连接的回路(环路)
无环图
- DAG即为有向无环图
度:一个顶点所有边的数量
- 出度:指从当前顶点指向其他顶点的边的数量
- 入度:其他顶点指向当前顶点的边的数量
5:1个出度
2:1个入度
8:2个入度、1个出度
…
Spark GraphX 简介
- GraphX是Spark提供分布式图计算API
- GraphX特点
- 基于内存实现了数据的复用与快速读取
- 通过弹性分布式属性图(Property Graph)统一了图视图与表视图
- 与Spark Streaming、Spark SQL和Spark MLlib等无缝衔接
Graph的创建方式
创建Graph需要导包:import org.apache.spark.graphx._
如果是在idea等开发工具上开发需要先下载好依赖包,根据自己的使用的Spark版本来选择下载
maven上的下载地址:https://mvnrepository.com/artifact/org.apache.spark/spark-graphx
- 方式一:Graph(V,E)
//创建vertices顶点RDD
scala> val vertices = sc.makeRDD(Seq((1L,1),(2L,2),(3L,3)))
vertices: org.apache.spark.rdd.RDD[(Long, Int)] = ParallelCollectionRDD[92] at makeRDD at <console>:27
//创建edges边RDD
scala> val edges=sc.makeRDD(Seq(Edge(1L,2L,1),Edge(2L,3L,2)))
edges: org.apache.spark.rdd.RDD[org.apache.spark.graphx.Edge[Int]] = ParallelCollectionRDD[93] at makeRDD at <console>:27
//创建Graph对象
scala> val graph=Graph(vertices,edges)
graph: org.apache.spark.graphx.Graph[Int,Int] = org.apache.spark.graphx.impl.GraphImpl@26437bfa
//获取graph图对象的vertices信息
scala> graph.vertices.collect
res1: Array[(org.apache.spark.graphx.VertexId, Int)] = Array((1,1), (3,3), (2,2))
//获取graph图对象的edges信息
scala> graph.edges.collect
res2: Array[org.apache.spark.graphx.Edge[Int]] = Array(Edge(1,2,1), Edge(2,3,2))
//使用triplets
scala> graph.triplets.collect
res5: Array[org.apache.spark.graphx.EdgeTriplet[Int,Int]] = Array(((1,1),(2,2),1), ((2,2),(3,3),2))
- 方式二:通过加载文件创建Graph
//graph.txt文件数据
2 3
3 4
1 4
2 4
//通过文件加载创建graph
scala> val graphLod=GraphLoader.edgeListFile(sc,"file:///opt/kb09file/graph.txt")
//获取graph图对象的vertices信息
scala> graphLod.vertices.collect
res6: Array[(org.apache.spark.graphx.VertexId, Int)] = Array((4,1), (1,1), (3,1), (2,1))
//获取graph图对象的edges信息
scala> graphLod.edges.collect
res7: Array[org.apache.spark.graphx.Edge[Int]] = Array(Edge(1,4,1), Edge(2,3,1), Edge(2,4,1), Edge(3,4,1))
//使用triplets
scala> graphLod.triplets.collect
res8: Array[org.apache.spark.graphx.EdgeTriplet[Int,Int]] = Array(((1,1),(4,1),1), ((2,1),(3,1),1), ((2,1),(4,1),1), ((3,1),(4,1),1))
应用案例
-
构建用户社交网络关系
- 顶点:用户名、年龄
- 边:喜爱度
-
找出大于30岁的用户
-
假设喜爱度超过5,表示真爱。请找出他(她)们
实现代码
//创建verticesRDD
scala> val userRDD=sc.makeRDD(Array((1L,("Alice",28)),(2L,("Bob",27)),(3L,("Charlie",65)),(4L,("David",42)),(5L,("Ed",55)),(6L,("Fran",50))))
//创建edgeRDD
scala> val userCallRDD=sc.makeRDD(Array(Edge(2L,1L,7),Edge(2L,4L,2),Edge(3L,2L,4),Edge(3L,6L,3),Edge(4L,1L,1),Edge(5L,2L,2),Edge(5L,3L,8),Edge(5L,6L,3)))
//创建Graph对象
scala> val userGraph=Graph(userRDD,userCallRDD)
//过滤出年龄大于35岁的:方式一通过filter
scala> userGraph.vertices.filter(x=>x._2._2>35).collect.foreach(x=>{prinitlnn("name:"+x._2._1+"age:"+x._2._2)})
//过滤出年龄大于35岁的:方式二通过模式匹配
scala> userGraph.vertices.filter{case(id,(name,age))=> age>35}
//使用triplets
scala> userGraph.triplets.collwct.foreach(println)
((2,(Bob,27)),(1,(Alice,28)),7)
((2,(Bob,27)),(4,(David,42)),2)
((3,(Charlie,65)),(2,(Bob,27)),4)
((3,(Charlie,65)),(6,(Fran,50)),3)
((4,(David,42)),(1,(Alice,28)),1)
((5,(Ed,55)),(2,(Bob,27)),2)
((5,(Ed,55)),(3,(Charlie,65)),8)
((5,(Ed,55)),(6,(Fran,50)),3)
//喜爱度超过5的用户数据
//虽然在rdd与graph中数据都是以元组的形式存在,但调用triplets后就无法像元组一样通过._x的方式来获取指定位置的数据
//triplets后的数据格式:EdgeTriplet[(String, Int),Int]]
//可以通过srcAttr、dstAttr、attr的方式来._x获取数据
scala> userGraph.triplets.filter(x=>x.attr>5).collect.foreach(x=>println(x.srcAttr._1+"like"+x.dstAttr._1+":"+x.attr))
BoblikeAlice:7
EdlikeCharlie:8