下面三篇博客是用mapreduce计算PageRank, 记录有算法的分析。
hadoop2.5.2学习12-MR之PageRank
hadoop2.5.2学习12-MR之PageRank01
hadoop2.5.2学习12-MR之PageRank02
一、mapreduce复习
A B D ---->
A 1.0 B D
B 0.5
D 0.5
===> reduce
源: A 1.0 B D
入链
A 0.3
A 0.4
A 0.1
新的PR = prSum = 0.3 + 0.4 + 0.1
=========>
//计算出新的PR值
double q = 0.85;//阻尼系数
int N = 4;//总页面数
double newPR = (1-q)/N + q*prSum;
-->A newPR B D
计算收敛
//对比新旧PR差值,直到数据收敛
double d = newPR - sourceNode.getPageRank();
为了统计所有的分组都达到收敛
需要设置一个全局变量, 用于接收差值
context.getCounter(MyRunJob.MyCounter.countName).increment(j);
在下次迭代前进行收敛判断
/**
* 因为每次的reducer中按照key进行四次计算, 每次计算增加一次j
* reducer:
* context.getCounter(MyRunJob.MyCounter.countName).increment(j);
* 所以求取平均差值。要除以4*1000.0
* 在reducer中:int j = (int) (d*1000.0);
*/
double avgD = sum/4000.0;
if (avgD < d) {//达到了收敛
break;
}
二、Spark—PageRank
流程
源数据:
A B
B C
C A
D B
A D
A B
links 每个页面对应出链的集合
(B, C)
(A, (B,D))
ranks 设置初始rank值
(B,1.0)
(A,1.0)
(C,1.0)
(D,1.0)
join, 这个是为了下一步计算新的分页面rank
(A, ((B, D), 1.0))
重新计算rank值
mapValues() ===> ((B, D), 1.0)
计算每个页面的入链
rank/size
reduceByKey===>每个页面新的PR
新的ranks
2.1、处理源数据, 将边关系(A, B)
,(A, C)
合并成对应的映射集合(A, (B, C))
val links = lines.map( s =>{
val parts = s.split("\\s+")
(parts(0), parts(1))
}
).distinct()
.groupByKey() //分组, 生成连接表
.cache()
2.2、在pankrank算法中,设置每个页面的初始PR值
//设置初始值 (1,1.0) (2,1.0)..
var ranks = links.mapValues(v => 1.0)
2.3、迭代计算每个页面新的PR
将邻接表和ranks表join, 获取 values (有了出链集合, PR), 用于计算入链的贡献PR值。
//迭代计算PR值
for (i <- 1 to iters) {
// (A,((B,C,D), 1.0))
val contribs = links.join(ranks)
.values //((B,C,D), 1.0) 用于计算入链的贡献PR值。
.flatMap{ case (urls, rank) =>
val size = urls.size
//计算每个页面的入链值
urls.map(url => (url, rank / size))
}
//新的PR值
ranks = contribs.reduceByKey(_ + _).mapValues(0.15 + 0.85 * _)
}
scala完整代码
package com.chb.scala
import org.apache.spark.SparkContext._
import org.apache.spark.{SparkConf, SparkContext}
/**
* Computes the PageRank of URLs from an input file. Input file should
* be in format of:
* URL neighbor URL
* URL neighbor URL
* URL neighbor URL
* ...
* where URL and their neighbors are separated by space(s).
*/
object SparkPageRank {
def main(args: Array[String]) {
val sparkConf = new SparkConf().setAppName("PageRank").setMaster("local[1]")
val iters = 20; //迭代次数
val ctx = new SparkContext(sparkConf)
val lines = ctx.textFile("page.txt")
//根据边关系数据(A, B)(A, C)...生成 邻接表 如:(A,(B,C,C)) (B,(C,D))..
val links = lines.map( s =>{
val parts = s.split("\\s+")
(parts(0), parts(1))
}
).distinct()
.groupByKey() //分组, 生成连接表
.cache()
//设置初始值 (1,1.0) (2,1.0)..
var ranks = links.mapValues(v => 1.0)
//迭代计算PR值
for (i <- 1 to iters) {
// (A,((B,C,D), 1.0))
val contribs = links.join(ranks)
.values //((B,C,D), 1.0) 用于计算入链的贡献PR值。
.flatMap{ case (urls, rank) =>
val size = urls.size
//计算每个页面的入链值
urls.map(url => (url, rank / size))
}
//新的PR值
ranks = contribs.reduceByKey(_ + _).mapValues(0.15 + 0.85 * _)
}
ctx.stop()
}
}