Spark+Scala实现PageRank

PageRank算法实现的三个步骤:

 (1) 将每个页面的PR值初始化为1.0。

 (2) 在每次迭代中,对页面p,向其“引用”的每个页面(即出链)发送一个值为rank / numNeighbors 的贡献值。

 (3) 将每个页面的排序值设为0.15 + 0.85 * contributionsReceived(0.85为阻尼因子,如果不清楚可以不管)。

 经过迭代计算四个页面的pr值,最终趋于收敛

Scala实现PageRank代码:

import org.apache.spark.HashPartitioner

//val links = // load RDD of (pageId, links) pairs
//var ranks = // load RDD of (pageId, rank) pairs
val links = sc.parallelize(
    List(("A",List("B","C")),
         ("B",List("A","C")),
         ("C",List("A","B","D")),
         ("D",List("C")))).partitionBy(new HashPartitioner(100)).persist()

var ranks=links.mapValues(v=>1.0)   //每个pageId的value值即pr值初始化为1

for (i <- 0 until 10) {
    //flat将map得到的(links,pr)再拆开成一个个(link,pr),再组合成一个序列
    val contributions=links.join(ranks).flatMap {
        case (pageId,(links,rank)) => 
        links.map(dest=>(dest,rank/links.size))  //遍历节点的每条出链的pr值为rank/links.size
    }
    ranks=contributions.reduceByKey((x,y)=>x+y).mapValues(v=>0.15+0.85*v)
}

ranks.sortByKey().collect()
  • spark需要维护两个RDD:一个由(pageID, linkList) 的元素组成,包含每个页面所“引用”页面的列表;另一个由(pageID, rank) 元素组成,包含每个页面的当前PR值。
  • flapMap里面的语句:将两个RDD数据集内连接以后,会得到以pageId(url)为key的(pageId, (links,
    rank))形式的四个键值对(假设只有四个节点),该语句里,对每个键值对进行case判断(这里省略了遍历对象对case后的键值对的match语句),即如果属于这个形式,便给遍历对象赋值 => 符号后生成的对象( => 符号是一个赋值操作),该操作是对此次键值对中的pageId的links(该pageId所引用的page列表)进行一个map操作,将rank/links.size的值赋给每个引用的page,这样完成了一个对pageId所引用的所有page进行赋值的操作,然后将生成的lists对象赋给遍历对象,同样,再继续对其它三个遍历对象完成同样的过程。最后我们还需要将得到赋值以后的对象(得到的可以看成是lists对象)进行一个flat操作,将各个(link, rank / links.size)组合,形成contributions的子对象,得到最终的contributions。
  • reduceByKey((x, y) => x + y)根据key对其所有的value求和,mapValues(v => 0.15 + 0.85*v)只对key的value进行操作。最后将得到的结果给ranks,进行更新。
  • spark实现PageRank的关建是,要想到利用键值对来进行rank值的更新,所以要想清楚设计出怎样的键值对来实现最后的叠加,一个好的线索是,reduce操作往往是对相同的key进行value值的求和,所以应该设置为<page, on_of_rank>键值对,page是其中的一个节点。

Scala运行Scala程序:

  1. 开启spark以后,进入到/home/spark-1.6/bin/目录,运行spark-shell:
    spark-shell

  2. 编写PageRank代码,输入到scala shell中,查看运行结果:
    PageRank output

最后补充一些对flatMap函数的理解:

  1. 调用flatMap方法返回的是一个可迭代集合,如:var words = Set(“hive”, “hbase”, “redis”)
    val result = words.flatMap(x => x.toUpperCase),输出结果是:Set(E, A, I, V, B, H, R, D, S)。符号=>左边的值x是遍历的对象(这也要求word必须是个可迭代的集合对象),右边是对x赋值或者是执行相关操作( => 符号一般是一个赋值操作);flatMap函数首先进行了一个map操作,map的结果应该变为Set[“HIVE”, “HBASE”, “REDIS”];然后对于map后的每个元素中的每个元素进行flatten,"HBASE"中的每个元素依次为: ‘H’ ‘B’ ‘A’ ‘S’ ‘E’ 因为最终是返回一个set,也就是将这些元素全部放入set中。这也要求words对象为一个含两层的集合(第一次遍历可以得到每个字符串,第二层遍历可以得到每个字符串所对应的字符)
  2. 如果是map函数,那它不会包括后面的flatten操作,也就不会再拆分map后的每个元素对象
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值