题目:使用两次MR实现返回人和人两两间的共同好友
输入数据:
A:B,C,D,F,E,O
B:A,C,E,K
C:F,A,D,I
......
表示A有好友B,C,D,F,E,O. B有好友A,C,E,K. C有好友F,A,D,I
输出结果:
A,B: CE
A,C: DF
......
表示A,B的共同好友有:C和E;A,C的共同好友有:D和F ;......;若没有共同好友则不输出
完整的输入数据:
A:B,C,D,F,E,O
B:A,C,E,K
C:F,A,D,I
D:A,E,F,L
E:B,C,D,M,L
F:A,B,C,D,E,O,M
G:A,C,D,E,F
H:A,C,D,E,O
I:A,O
J:B,O
K:A,C,D
L:D,E,F
M:E,F,G
O:A,H,I,J
解题步骤:
第一步(第一次map):倒排。要知道两两间的共同好友,我们需要知道某一个人是哪些人的好友。例如:A是B的好友,A是C的好友,那么我们就可以知道B、C的共同好友有A。所以我们先一个个列出来
如题中:
A:B,C,D,F,E,O 需要转换为:
B:A
C:A
D:A
F:A
E:A
O:A
表示B是A的好友,C是A的好友,...,O是A的好友
B:A,C,E,K 需要转换为:
A:B
C:B
E:B
K:B
表示A是B的好友,C是B的好友,...,K是B的好友
...
依次进行转换,直到完毕!
代码实现:
#-*-encoding:utf-8-*-
#map01.py
import sys
for line in sys.stdin:
for item in line.strip()[2:].split(','):
print item+':'+line[0]
全部转换后得到下面结果:
[root@master C1]# cat data.txt | python map01.py
B:A
C:A
D:A
F:A
E:A
O:A
A:B
C:B
E:B
K:B
F:C
A:C
D:C
I:C
A:D
E:D
F:D
L:D
B:E
C:E
D:E
M:E
L:E
A:F
B:F
C:F
D:F
E:F
O:F
M:F
A:G
C:G
D:G
E:G
F:G
A:H
C:H
D:H
E:H
O:H
A:I
O:I
B:J
O:J
A:K
C:K
D:K
D:L
E:L
F:L
E:M
F:M
G:M
A:O
H:O
I:O
J:O
排序后结果:
[root@master C1]# cat data.txt | python map01.py | sort -k1
A:B
A:C
A:D
A:F
A:G
A:H
A:I
A:K
A:O
B:A
B:E
B:F
B:J
C:A
C:B
C:E
C:F
C:G
C:H
C:K
D:A
D:C
D:E
D:F
D:G
D:H
D:K
D:L
E:A
E:B
E:D
E:F
E:G
E:H
E:L
E:M
F:A
F:C
F:D
F:G
F:L
F:M
G:M
H:O
I:C
I:O
J:O
K:B
L:D
L:E
M:E
M:F
O:A
O:F
O:H
O:I
O:J
此时我们就已经能很清晰的看到共同好友关系了,例如拿A来说:
A:B
A:C
A:D
A:F
A:G
A:H
A:I
A:K
A:O
前面说过,这个表示A是B的好友,A是C的好友,...,A是O的好友。那么我们就可以知道,B、C有共同好友A,B、D有共同好友A,...,其实就是(B,C,D,F,G,H,I,K,O)的两两组合有共同好友A。
第二步(第一次reduce):组合列出两两间共同好友
A:B
A:C
A:D
A:F
A:G
A:H
A:I
A:K
A:O 转换为:
B-C:A
B-D:A
...
K-O:A
依次进行转换,直到完毕!
代码实现:
#-*-encoding:utf-8-*-
#red01.py
import sys
import itertools as it
current=[]
for line in sys.stdin:
name,friend=line.strip().split(':')
if current==[]:
current=[name,friend]
elif current[0]==name:
current.append(friend)
else:
friends=list(set(current[1:])) #去重
friends=list(it.combinations(friends,2)) #两两组合
for person1, person2 in friends:
if person1>person2: #这一步是把B-A变为A-B,方面后面计算
print person2+'-'+person1+':'+current[0]
else:
print person1+'-'+person2+':'+current[0]
current=[name,friend]
friends=list(it.combinations(list(set(current[1:])) ,2))
for person1, person2 in friends:
if person1>person2:
print person2+'-'+person1+':'+name
else:
print person1+'-'+person2+':'+name
输出结果为:
[root@master C1]# cat data.txt | python map01.py | sort -k1 | python red01.py
B-C:A
C-D:A
C-G:A
C-F:A
C-I:A
C-H:A
C-K:A
C-O:A
B-D:A
B-G:A
B-F:A
B-I:A
B-H:A
B-K:A
B-O:A
D-G:A
D-F:A
D-I:A
D-H:A
D-K:A
D-O:A
F-G:A
G-I:A
G-H:A
G-K:A
G-O:A
F-I:A
F-H:A
F-K:A
F-O:A
H-I:A
I-K:A
I-O:A
H-K:A
H-O:A
K-O:A
A-J:B
A-E:B
A-F:B
E-J:B
F-J:B
E-F:B
A-B:C
A-E:C
A-G:C
A-F:C
A-H:C
A-K:C
B-E:C
B-G:C
B-F:C
B-H:C
B-K:C
E-G:C
E-F:C
E-H:C
E-K:C
F-G:C
G-H:C
G-K:C
F-H:C
F-K:C
H-K:C
A-C:D
A-E:D
A-G:D
A-F:D
A-H:D
A-K:D
A-L:D
C-E:D
C-G:D
C-F:D
C-H:D
C-K:D
C-L:D
E-G:D
E-F:D
E-H:D
E-K:D
E-L:D
F-G:D
G-H:D
G-K:D
G-L:D
F-H:D
F-K:D
F-L:D
H-K:D
H-L:D
K-L:D
A-B:E
A-D:E
A-G:E
A-F:E
A-H:E
A-M:E
A-L:E
B-D:E
B-G:E
B-F:E
B-H:E
B-M:E
B-L:E
D-G:E
D-F:E
D-H:E
D-M:E
D-L:E
F-G:E
G-H:E
G-M:E
G-L:E
F-H:E
F-M:E
F-L:E
H-M:E
H-L:E
L-M:E
A-C:F
A-D:F
A-G:F
A-M:F
A-L:F
C-D:F
C-G:F
C-M:F
C-L:F
D-G:F
D-M:F
D-L:F
G-M:F
G-L:F
L-M:F
C-O:I
D-E:L
E-F:M
A-H:O
A-J:O
A-I:O
A-F:O
H-J:O
H-I:O
F-H:O
I-J:O
F-J:O
F-I:O
第三步(第二次map):现在我们只需要排序,不需要其它什么操作了所以直接输出即可
代码实现
#-*-encoding:utf-8-*-
#map02.py
import sys
for line in sys.stdin:
print line.strip()
排序后为:
[root@master C1]# cat data.txt | python map01.py | sort -k1 | python red01.py | python map02.py | sort -k1
A-B:C
A-B:E
A-C:D
A-C:F
A-D:E
A-D:F
A-E:B
A-E:C
A-E:D
A-F:B
A-F:C
A-F:D
A-F:E
A-F:O
A-G:C
A-G:D
A-G:E
A-G:F
A-H:C
A-H:D
A-H:E
A-H:O
A-I:O
A-J:B
A-J:O
A-K:C
A-K:D
A-L:D
A-L:E
A-L:F
A-M:E
A-M:F
B-C:A
B-D:A
B-D:E
B-E:C
B-F:A
B-F:C
B-F:E
B-G:A
B-G:C
B-G:E
B-H:A
B-H:C
B-H:E
B-I:A
B-K:A
B-K:C
B-L:E
B-M:E
B-O:A
C-D:A
C-D:F
C-E:D
C-F:A
C-F:D
C-G:A
C-G:D
C-G:F
C-H:A
C-H:D
C-I:A
C-K:A
C-K:D
C-L:D
C-L:F
C-M:F
C-O:A
C-O:I
D-E:L
D-F:A
D-F:E
D-G:A
D-G:E
D-G:F
D-H:A
D-H:E
D-I:A
D-K:A
D-L:E
D-L:F
D-M:E
D-M:F
D-O:A
E-F:B
E-F:C
E-F:D
E-F:M
E-G:C
E-G:D
E-H:C
E-H:D
E-J:B
E-K:C
E-K:D
E-L:D
F-G:A
F-G:C
F-G:D
F-G:E
F-H:A
F-H:C
F-H:D
F-H:E
F-H:O
F-I:A
F-I:O
F-J:B
F-J:O
F-K:A
F-K:C
F-K:D
F-L:D
F-L:E
F-M:E
F-O:A
G-H:A
G-H:C
G-H:D
G-H:E
G-I:A
G-K:A
G-K:C
G-K:D
G-L:D
G-L:E
G-L:F
G-M:E
G-M:F
G-O:A
H-I:A
H-I:O
H-J:O
H-K:A
H-K:C
H-K:D
H-L:D
H-L:E
H-M:E
H-O:A
I-J:O
I-K:A
I-O:A
K-L:D
K-O:A
L-M:E
L-M:F
到了这里最终结果慢慢已经浮现了,我们可以看看前面部分
A-B:C
A-B:E
A-C:D
A-C:F
...
这表示A、B有共同好友C,A、B有共同好友E,即最终结果就是A、B有共同好友C和E
第四步(第二次reduce):将所有共同好友整合在一起
A-B:C
A-B:E
转换为:
A-B:CE
...
依次进行转换,直到完毕!
代码实现:
#-*-encoding:utf-8-*-
#red02.py
import sys
current=[]
for line in sys.stdin:
person1, person2=line.strip().split(':')
if current==[]:
current=[person1, person2]
elif current[0]== person1:
current.append(person2)
else:
print str(current[0]+': '+''.join(current[1:]))
current=[person1,person2]
print str(current[0]+': '+''.join(current[1:]))
输出结果:
[root@master C1]# cat data.txt | python map01.py | sort -k1 | python red01.py | python map02.py | sort -k1 | python red02.py
A-B: CE
A-C: DF
A-D: EF
A-E: BCD
A-F: BCDEO
A-G: CDEF
A-H: CDEO
A-I: O
A-J: BO
A-K: CD
A-L: DEF
A-M: EF
B-C: A
B-D: AE
B-E: C
B-F: ACE
B-G: ACE
B-H: ACE
B-I: A
B-K: AC
B-L: E
B-M: E
B-O: A
C-D: AF
C-E: D
C-F: AD
C-G: ADF
C-H: AD
C-I: A
C-K: AD
C-L: DF
C-M: F
C-O: AI
D-E: L
D-F: AE
D-G: AEF
D-H: AE
D-I: A
D-K: A
D-L: EF
D-M: EF
D-O: A
E-F: BCDM
E-G: CD
E-H: CD
E-J: B
E-K: CD
E-L: D
F-G: ACDE
F-H: ACDEO
F-I: AO
F-J: BO
F-K: ACD
F-L: DE
F-M: E
F-O: A
G-H: ACDE
G-I: A
G-K: ACD
G-L: DEF
G-M: EF
G-O: A
H-I: AO
H-J: O
H-K: ACD
H-L: DE
H-M: E
H-O: A
I-J: O
I-K: A
I-O: A
K-L: D
K-O: A
L-M: EF
第五步:我们要再去hadoop上运行,还得要写个脚本文件run.sh
#run.sh
HADOOP_CMD="/usr/local/src/hadoop-2.6.5/bin/hadoop" #hadoop路径
#streaming jar包路径,因为我们是python开发,需要通过hadoop streaming的方式提交作业
STREAM_JAR_PATH="/usr/local/src/hadoop-2.6.5/share/hadoop/tools/lib/hadoop-streaming-2.6.5.jar"
INPUT_FILE_PATH_1="/HomeWork/C1/data.txt" #输入文件路径
OUTPUT_PATH_1="/HomeWork/C1/result01" #第一次mapreduce输出文件路径
$HADOOP_CMD fs -rm -r -skipTrash $OUTPUT_PATH_1 #如果输出目录存在则删除后新建
#Trash会把删除掉的文件保留一段时间,可在core-site.xml中增加配置,这里skip跳过
# Step 1.
$HADOOP_CMD jar $STREAM_JAR_PATH \
-input $INPUT_FILE_PATH_1 \
-output $OUTPUT_PATH_1 \
-mapper "python map01.py" \
-reducer "python red01.py" \
-file ./map01.py \
-file ./red01.py
OUTPUT_PATH_2="/HomeWork/C1/result02" #第二次mapreduce输出文件路径
$HADOOP_CMD fs -rm -r -skipTrash $OUTPUT_PATH_2
# Step 2.
$HADOOP_CMD jar $STREAM_JAR_PATH \
-input $OUTPUT_PATH_1 \
-output $OUTPUT_PATH_2 \
-mapper "python map02.py" \
-reducer "python red02.py" \
-file ./map02.py \
-file ./red02.py
执行bash run.sh
[root@master C1]# hadoop fs -ls /HomeWork/C1/result02/
Found 2 items
-rw-r--r-- 3 root supergroup 0 2019-07-14 20:34 /HomeWork/C1/result02/_SUCCESS
-rw-r--r-- 3 root supergroup 665 2019-07-14 20:34 /HomeWork/C1/result02/part-00000
[root@master C1]# hadoop fs -cat /HomeWork/C1/result02/part-00000
A-B: CE
A-C: DF
A-D: EF
A-E: BCD
A-F: BCDEO
A-G: CDEF
A-H: CDEO
A-I: O
A-J: BO
A-K: CD
A-L: DEF
A-M: EF
B-C: A
B-D: AE
B-E: C
B-F: ACE
B-G: ACE
B-H: ACE
B-I: A
B-K: AC
B-L: E
B-M: E
B-O: A
C-D: AF
C-E: D
C-F: AD
C-G: ADF
C-H: AD
C-I: A
C-K: AD
C-L: DF
C-M: F
C-O: AI
D-E: L
D-F: AE
D-G: AEF
D-H: AE
D-I: A
D-K: A
D-L: EF
D-M: EF
D-O: A
E-F: BCDM
E-G: CD
E-H: CD
E-J: B
E-K: CD
E-L: D
F-G: ACDE
F-H: ACDEO
F-I: AO
F-J: BO
F-K: ACD
F-L: DE
F-M: E
F-O: A
G-H: ACDE
G-I: A
G-K: ACD
G-L: DEF
G-M: EF
G-O: A
H-I: AO
H-J: O
H-K: ACD
H-L: DE
H-M: E
H-O: A
I-J: O
I-K: A
I-O: A
K-L: D
K-O: A
L-M: EF