MapReduce实现PageRank算法(稀疏图法)

本文介绍使用Python结合Hadoop Streaming实现PageRank算法的过程。该算法通过MapReduce框架处理网页链接数据,迭代计算网页的重要性评分。具体包括Map阶段计算出链概率、Reduce阶段汇总计算PageRank值等步骤。

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

前言

本文用Python编写代码,并通过hadoop streaming框架运行。

 

算法思想

下图是一个网络:

 

考虑转移矩阵是一个很多的稀疏矩阵,我们可以用稀疏矩阵的形式表示,我们把web图中的每一个网页及其链出的网页作为一行,即用如下方式表示:

1 A    B    C    D
2 B    A    D
3 C    C
4 D    B    C

Map阶段

在Map阶段,Map操作的每一行,对所有出链发射当前网页概率值的1/k,k是当前网页的出链数,比如对第一行输出<B,1/3*1/4>,<C,1/3*1/4>,<D,1/3*1/4>。

 

Reduce阶段

Reduce操作收集同一网页的值,累加并按权重计算,即$P_{i} = \alpha \cdot (b_{1}+b_{2}+\cdots +b_{m})+\frac{(1-\alpha )}{N}$,其中$m$是指向网页$j$的网页数,$N$为所有网页数。

实践的时候,怎样在Map阶段知道当前行网页的概率值,需要一个单独的文件专门保存上一轮的概率分布值,先进行一次排序,让出链行与概率值按网页id出现在同一Mapper里面,整个流程如下:

 

算法实现

sortMapper.py 和 sortReducer.py的功能就是将图和概率矩阵读入并进行排序。

sortMapper.py

1 #!/usr/bin/env python3
2 import sys
3 
4 
5 for line in sys.stdin:
6     print(line.strip())

sortReducer.py

1 #!/usr/bin/env python3
2 import sys
3 
4 
5 for line in sys.stdin:
6     print(line.strip())

pageRankMapper.py

 1 #!/usr/bin/env python3
 2 import sys
 3 
 4 node1 = node2 = None
 5 count1 = count2 = 0
 6 page_rank = 0.0
 7 
 8 
 9 for line in sys.stdin:
10     if line.count('\n') == len(line):
11         continue         # 除去空行
12     data = line.strip().split('\t')
13     if data[1] == 'a':   # 该行数据是PageRank
14         count1 += 1
15         if count1 > 1:   # 避免某个结点的PageRank丢失,因为有可能连着两行数据是PageRank
16             print('%s\t%s' % (node1, 0.0))
17         node1 = data[0]       # 记录出发结点
18         page_rank = float(data[2])   # 记录出发结点的PageRank
19     else:                # 该行是链
20         node2 = data[0]
21         reach_node_list = data[1:]
22 
23     if node1 == node2 and node1:  # 注意除去None的情况
24         node_number = len(reach_node_list)   # 出链数
25         for reach_node in reach_node_list:
26             print('%s\t%s' % (reach_node, page_rank*1.0/node_number))
27         print('%s\t%s' % (node1, 0.0))
28         node1 = node2 = None
29         count1 = 0

pageRankReducer.py

 1 #!/usr/bin/env python3
 2 import sys
 3 
 4 alpha = 0.8
 5 node = None             # 记录当前结点
 6 page_rank_sum = 0.0   # 记录当前结点的PageRank总和
 7 N = 4
 8 
 9 for line in sys.stdin:
10     if line.count('\n') == len(line): continue
11     data = line.strip().split('\t')
12     if node is None or node != data[0]:
13         if(node): print('%s\ta\t%s' % (node, alpha*page_rank_sum + (1.0-alpha)/N))
14         node = data[0]
15         page_rank_sum = 0.0
16     page_rank_sum += float(data[1])
17 
18 print('%s\ta\t%s' % (node, alpha*page_rank_sum + (1.0-alpha)/N))

 

算法运行

 由于该算法需要迭代运行,所以编写shell脚本来运行。

我也不是很会写shell脚本,所以写了一个感觉比较傻的。

run.sh

 1 #!/bin/bash
 2 
 3 max=50
 4 
 5 for i in `seq 1 $max`
 6 do
 7     /usr/local/hadoop/bin/hadoop jar /usr/local/hadoop/hadoop-streaming-2.9.2.jar \
 8     -mapper /usr/local/hadoop/sortMapper.py \
 9     -file /usr/local/hadoop/sortMapper.py \
10     -reducer /usr/local/hadoop/sortReducer.py \
11     -file /usr/local/hadoop/sortReducer.py \
12     -input links.txt pageRank.txt \
13     -output out1
14 
15 
16     /usr/local/hadoop/bin/hadoop jar /usr/local/hadoop/hadoop-streaming-2.9.2.jar \
17     -mapper /usr/local/hadoop/pageRankMapper.py \
18     -file /usr/local/hadoop/pageRankMapper.py \
19     -reducer /usr/local/hadoop/pageRankReducer.py \
20     -file /usr/local/hadoop/pageRankReducer.py \
21     -input out1/part-00000 \
22     -output out2
23 
24     /usr/local/hadoop/bin/hadoop fs -rm pageRank.txt
25     /usr/local/hadoop/bin/hadoop fs -cp out2/part-00000 pageRank.txt
26     /usr/local/hadoop/bin/hadoop fs -rm -r -f out1
27     /usr/local/hadoop/bin/hadoop fs -rm -r -f out2
28 
29     
30     rm -r ~/Desktop/tmp.txt
31     /usr/local/hadoop/bin/hadoop fs -get pageRank.txt ~/Desktop/tmp.txt
32     echo $i | ~/Desktop/slove.py
33 done

 解释一下该脚本,首先每次需要执行sortMapper.py,sortReducer.py,pageRankMapper.py,pageRankReducer.py四段代码,执行完之后会产生新的结点pageRank值,即保存在part-00000中。因为下一次运行需要用到这新的数据,所以此时把旧的pageRank.txt文件删除,再把新产生的数据复制过去。

为了记录每次迭代的结果,我还额外编写了一段代码来将记录每次结果。代码如下:
slove.py

 1 #!/usr/bin/env python3
 2 import sys
 3 
 4 
 5 number = sys.stdin.readline().strip()
 6 
 7 f_src = open("tmp.txt","r")
 8 f_dst = open("result.txt", "a")
 9 
10 mat = "{:^30}\t"
11 f_dst.write('\n' + number)
12 
13 lines = f_src.readlines()
14 for line in lines:
15     if line.count('\n') == len(line):
16         continue
17     data = line.strip().split('\t')
18     f_dst.write(mat.format(data[2]))

 

最后的运行结果为:

 

后记

上面实现的稀疏图的算法,后来我又实现了矩阵的算法。有兴趣的可以转至:传送门 

 

 

参考:

[1] PageRank算法简介及Map-Reduce实现

转载于:https://www.cnblogs.com/zyb993963526/p/10582150.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值