基于MapReduce实现PageRank算法

一、什么是PageRank

PageRank,又称网页排名、谷歌左侧排名,是一种由搜索引擎根据网页之间相互的超链接计算的技术,而作为网页排名的要素之一,以Google公司创办人拉里·佩奇(Larry Page)之姓来命名。Google用它来体现网页的相关性和重要性,在搜索引擎优化操作中是经常被用来评估网页优化的成效因素之一。

二、实现公式

对于一个页面A,那么它的PR值为:
在这里插入图片描述

  • R(A) 是页面A的PR值

  • PR(Ti)是页面Ti的PR值,在这里,页面Ti是指向A的所有页面中的某个页面

  • C(Ti)是页面Ti的出度,也就是Ti指向其他页面的边的个数

  • d 为阻尼系数,其意义是,在任意时刻,用户到达某页面后并继续向后浏览的概率,该数值是根据上网者使用浏览器书签的平均频率估算而得,通常d=0.85

  • N为页面总数

三、项目梳理

1.项目流程

分为两个流程:Mapper和Reducer
Mapper:将文件输出成 网页+pr值 的形式
Reducer:将Mapper端输入的文件输出成 网页+pr值+出链 的形式
通过迭代的方式使pr值收敛。

2.项目难点

Mapper端:
1、 如何将数据切分
2.、切分后如何求值
3、求值后如何输出
Reduce端:
1、如何取出数据值
2、取出数据后如何求值
3、求值后如何输出
Driver时:
通过哪种方法进行迭代,可以使数据在迭代时被正确处理

3.解决方法

Mapper端:
1、如何将数据切分

数据的形式为:
1 0.25 2 4
2 0.25 3
3 0.25 1 2
4 0.25 2 3
其中网页与pr值中是用\t分隔的pr值与出链网页中均由空格分隔

此时考虑采用split()将value分隔,用split("\t")分隔成两段,后将第二段用split(" “)将pr值和出链网页分隔开。因为迭代时key与value中间会有一个”\t",所以考虑将文件输入设置为以上形式。

2、切分后如何求值与如何输出

Mapper端目标输出文件形式:
1 0.125
4 0.125
3 0.25

另外的,考虑到迭代问题,一个Mapper还应输出
1 _2 4
代表网页1的出链网页有2、4

所以在切分后,先将切分出的出链网页和对应的pr值(PR(Ti)/C(Ti))作为key-value写出,再将行数据的第一部分(元网页)与所有出链网页作为key-value写出。

Reducer端:
1、如何取数据

此时Mapper端输出的数据形式为:
1 0.125
4 0.125
1 _2 4

此时,输入数据中含有两种数据,一种是用于下一步进行PR值计算的数据,一种是写明网页及其出链网页的数据。此时考虑用contains()方法,通过判断是否含有"_“来判断是将此行数据取出计算PR值还是将此行作为value的后半部分。
2、如何计算数据
通过迭代器取出相同网页收到的PR值,再通过(1 - d) / n + d * pr_sum 计算出每个网页的pr值。或取出出链网页,将其存入变量用于下一步输出。
3、求值后如何输出
key:从Mapper端输入的key就是求出PR值对应的网页,所以不用更改直接继续输出
value:value分为两部分(pr值+” "+出链网页)
Driver时
使用for循环进行迭代,通过path+i的方式对每次迭代的文件进行命名。设置完输入输出路径后,将输出路径赋值给出入路径,作为下次调用时的输入路径。通过此方法递归读取数据,直至数据收敛。
注意:job提交后不能使用System.exit()来查看是否执行完毕,否则MR只会运行一次

四、项目Demo

Mapper端:

package com.wuyik.pagerank;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

import java.io.IOException;

//将传入的文件输出为  网页+pr值  的形式
public class PageRankMapper extends Mapper<Object, Text, Text, Text> {

    private Text urlKey = new Text();  //输出的key值  网页url

    @Override
    protected void map(Object key, Text value, Context context) throws IOException, InterruptedException {
        String line = value.toString();

        //判断文件是否为part-r-00000
        if (line.substring(0, 1).matches("[0-9]{1}")) {

            boolean flag = false;

            //开始递归调用时(第一次文件中不含有代表url的“_”)
            if (line.contains("_")){
                line = line.replace("_","");
                flag = true;
            }

            String[] values = line.split("\t");
            Text text = new Text(values[0]);
            String[] urlValue = values[1].split(" ");

            String url = "_";
            double pr = 0; //PR(Ti)
            int num = 0;  //C(Ti)
            int i = 0;  //遍历数组urlValue时使用

            if (flag){ //递归时
                i = 2;
                pr = Double.parseDouble(urlValue[1]);
                num = urlValue.length - 2;
            } else {  //首次进入Mapper时
                i = 1;
                pr = Double.parseDouble(urlValue[0]);
                num = urlValue.length - 1;
            }

            for(; i< urlValue.length; i++){
                url += urlValue[i] + " ";
                urlKey.set(urlValue[i]);
                Text urlResult = new Text(String.valueOf(pr / num));
                context.write(urlKey,urlResult);
            }

            //再次将网页和对应的出链输出
            context.write(text,new Text(url));

        }
    }
}


Reducer端:

package com.wuyik.pagerank;

import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;

//将Map端输入的数据输出为  网页+pr值+出链网页  的形式
public class PageRankReducer extends Reducer<Text, Text, Text, Text> {

    private Text result = new Text();

    @Override
    protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
        double sum = 0;
        String url = "";

        for (Text value : values) {
            //如果结果中含有"_"说明此行记录为  网页+外链  不参与计算
            if (value.toString().contains("_")) {
                url = value.toString();
            } else {
                sum += Double.parseDouble(value.toString());
            }
        }

        //设置阻尼值d=0.85
        double d = 0.85;
        //求pr
        double pr = (1 - d) / 4 + d * sum;
        String str = String.format("%.6f",pr);
        result.set(new Text(str + " " + url));
        context.write(key,result);

    }
}

Driver端:

package com.wuyik.pagerank;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import java.io.IOException;

public class PageRankDriver {

    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {

        String paths = "E:\\PageRank\\out\\00";
        String path1 = paths;
        String path2 = "";

        for (int i = 1; i <= 20; i++) {

            path2 = paths + i;

            Job job = Job.getInstance(new Configuration());

            job.setJarByClass(PageRankDriver.class);

            job.setMapperClass(PageRankMapper.class);
            job.setCombinerClass(PageRankReducer.class);
            job.setReducerClass(PageRankReducer.class);

            job.setMapOutputKeyClass(Text.class);
            job.setMapOutputValueClass(Text.class);
            job.setOutputKeyClass(Text.class);
            job.setOutputValueClass(Text.class);

            FileInputFormat.setInputPaths(job, new Path(path1));
            FileOutputFormat.setOutputPath(job, new Path(path2));
            path1 = path2;

            boolean b = job.waitForCompletion(true);

        }

    }

}

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值