spark-scala版的PageRank

本文介绍PageRank算法原理及其实现过程,通过迭代计算网页的重要性,并利用Spark RDD的分区特性提高计算效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

PageRank是一种从RDD分区中获益的算法,刚学完RDD分区,便以它为例吧。

PageRank用来根据外部文档指向一个文档的链接,对集合中每个文档的重要程度赋一个度量值。该算法可以用于对网页进行排序,也可以用于排序科技文章或社交网站中有影响的用户。

PageRank是执行多次连接的一个迭代算法,算法维护两个数据集:一个由(pageID,linkList)的元素组成,包含每个页面的相邻页面的列表;另一个由(pageID,rank)元素组成,包含每个页面的当前排序值。

计算步骤:

(1)将每个页面的排序值初始化为1.0

(2)在每次迭代中,对页面p,向其每个相邻页面(有直接链接的页面)发送一个值为rank(p)/numNeighbors(p)的贡献值

(3)将每个页面的排序值设置为0.15+0.85*contributionsReceived

最后两步会重复几个循环,在此过程中,算法会逐渐收敛于每个页面的实际PageRank值,在实际操作中,收敛通常需要大约10轮迭代。

scala版代码:

初始数据:假设原始数据为:100页面中有两个直接链接指向页面101,102;102页面中两个直接链接指向页面100,105。。。。。

scala> val data=Seq(("100",Seq("101","102")),("102",Seq("100","105")),("103",Seq("104","101")),("105",Seq("102","107")))
data: Seq[(String, Seq[String])] = List((100,List(101, 102)), (102,List(100, 105)), (103,List(104, 101)), (105,List(102, 107)))
 创建rdd并使用哈希分区,分区后缓存在内存中:

scala> import org.apache.spark._//导包
import org.apache.spark._

scala> val links=sc.parallelize(data).partitionBy(new HashPartitioner(100)).persist()
links: org.apache.spark.rdd.RDD[(String, Seq[String])] = ShuffledRDD[29] at partitionBy at <console>:41

这里有一个坑,如果用makeRDD去创建,RDD不是键值对RDD,以后还是使用parallelize方法吧。。。 makeRDD和parallelize的区别   makeRDD和parallelize的区别

scala> val links=sc.makeRDD(data)
links: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[26] at makeRDD at <console>:32
将每个页面的排序值初始化为1.0,;由于使用mapValues(没有改变键)生成的RDD,所以和links的分区方式一样

scala> var ranks=links.mapValues(v=>1.0)
ranks: org.apache.spark.rdd.RDD[(String, Double)] = MapPartitionsRDD[32] at mapValues at <console>:43
 运行10轮PageRank迭代

scala> for(i<-0 until 10){
     |  val contributions=links.join(rank).flatMap{//注意map和flatMap的区别  只要是元素中是集合,要变成单个元素就用flatMap()
     | case(pageId,(links,rank))=>links.map(dest=>(dest,rank/links.size))//每个页面收到的贡献值
     | }
     | ranks =contributions.reduceByKey((x,y)=>x+y).mapValues(v=>0.15+0.85*v)//reduceByKey()结果已经是哈希分区,这样下次循环中映射操作的结果再次与links进行连接操作就会更高效
     | }
 遍历ranks得到最终排名

scala> ranks foreach println
(100,0.575)
(101,1.0)
(102,1.0)
(104,0.575)
(105,0.575)
(107,0.575)

 map和flatMap的区别:

flatMap:

scala> val contributions=links.join(rank).flatMap{ case(pageId,(links,rank))=>links }
contributions: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[173] at flatMap at <console>:45
scala> contributions foreach println
101
102
100
105
104
101
102
107
 map:

scala> val contributions=links.join(rank).map{ case(pageId,(links,rank))=>links }
contributions: org.apache.spark.rdd.RDD[Seq[String]] = MapPartitionsRDD[181] at map at <console>:45

scala> contributions foreach println
List(101, 102)
List(100, 105)
List(104, 101)
List(102, 107)

map返回一个大集合,大集合中每个元素是一个小集合,而flatMap返回一个大集合,大集合中每个元素是String元素,因为 flatMap会将集合拍扁成单个元素,所以要想将集合变成单个元素就用flatMap



这是关于rdd分区的一个例子,为了最大化分区相关优化的潜在作用,应该在无需改变元素的键时尽量使用mapValues()或flatMapValues() 而不是map()、flatMap()





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值