MapReduce综合练习题(2)社交粉丝数据分析

MapReduce综合练习题(2)社交粉丝数据分析

数据准备

以下是qq的好友列表数据,冒号前是一个用户,冒号后是该用户的所有好友(数据中的好友关系是单向的)

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

把数据保存到friend.txt

逻辑分析
求出哪些人两两之间有共同好友,及他俩的共同好友都有谁?
解题思路:
第一步
map
读一行 A:B,C,D,F,E,O
输出 <B,A><C,A><D,A><F,A><E,A><O,A>
在读一行 B:A,C,E,K
输出 <A,B><C,B><E,B><K,B>

REDUCE
拿到的数据比如<C,A><C,B><C,E><C,F><C,G>…
输出:
<A-B,C>
<A-E,C>
<A-F,C>
<A-G,C>
<B-E,C>
<B-F,C>…

第二步
map
读入一行<A-B,C>
直接输出<A-B,C>

reduce
读入数据 <A-B,C><A-B,F><A-B,G>…
输出: A-B C,F,G,…

Java代码

ComonsFriendsStepOne

package com.czxy.day20191122.Test01;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

import java.io.IOException;

public class ComonsFriendsStepOne  extends Configured implements Tool {
    @Override
    public int run(String[] args) throws Exception {

        Configuration conf = super.getConf();

        Job job = Job.getInstance(conf, ComonsFriendsStepOne.class.getSimpleName());

        job.setInputFormatClass(TextInputFormat.class);
        TextInputFormat.addInputPath(job,new Path("E:\\cache\\mapReduceTestCache\\20191122"));

        job.setMapperClass(ComonsFriendsStepOneMapper.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(Text.class);

        job.setReducerClass(ComonsFriendsStepOneReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(Text.class);

        job.setOutputFormatClass(TextOutputFormat.class);
        TextOutputFormat.setOutputPath(job,new Path("E:\\cache\\mapReduceResultCache\\20191122\\Test01\\one01"));
        boolean b = job.waitForCompletion(true);
        return b?0:1;
    }

    public  static class ComonsFriendsStepOneMapper  extends Mapper<LongWritable,Text,Text,Text> {
        @Override
        protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
            String[] split = value.toString().split(":");
            //左边是用户 右边是好友列表

            String person = split[0];
            String[] friends = split[1].split(",");

            for (String friend : friends) {
                //输出好友,用户
                context.write(new Text(friend),new Text(person));
            }
        }
    }

    public static class ComonsFriendsStepOneReducer extends Reducer<Text,Text,Text,Text> {
        @Override
        protected void reduce(Text friend, Iterable<Text> persons, Context context) throws IOException, InterruptedException {
            //构建字符串,这个字符串的意思是把一个用户对应的好友都组成A-B-C
            StringBuffer buffer = new StringBuffer();

            for (Text person : persons) {
                buffer.append(person).append("-");
            }

            //然后写出这个好友 的好友列表
            context.write(friend,new Text(buffer.toString()));
        }
    }

    public static void main(String[] args) throws Exception {
        Configuration configuration = new Configuration();
        ToolRunner.run(configuration,new ComonsFriendsStepOne(),args);
    }
}

ComonsFriendsStepTwo

package com.czxy.day20191122.Test01;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

import java.io.IOException;
import java.util.Arrays;

public class ComonsFriendsStepTwo extends Configured implements Tool {
    @Override
    public int run(String[] args) throws Exception {

        Job job = Job.getInstance(super.getConf(), ComonsFriendsStepTwo.class.getSimpleName());

        job.setInputFormatClass(TextInputFormat.class);
        TextInputFormat.addInputPath(job,new Path("E:\\cache\\mapReduceResultCache\\20191122\\Test01\\one01"));

        job.setMapperClass(ComonsFriendStepTwoMapper.class);
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(Text.class);

        job.setReducerClass(ComonsFriendStepTwoReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(Text.class);

        job.setOutputFormatClass(TextOutputFormat.class);
        TextOutputFormat.setOutputPath(job,new Path("E:\\cache\\mapReduceResultCache\\20191122\\Test01\\one02"));

        boolean b = job.waitForCompletion(true);
        return b?0:1;
    }
    public  static  class ComonsFriendStepTwoMapper  extends Mapper<LongWritable,Text,Text,Text> {

        /**
         * A   F-D-O-I-H-B-K-G-C-
         * B   E-A-J-F-
         * C   K-A-B-E-F-G-H-
         */
        @Override
        protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {

            String[] split = value.toString().split("\t");

            String friends = split[0];
            String[] persons = split[1].split("-");

            System.out.println("排序前");
            System.out.println(Arrays.toString(persons));

            //排序,避免c-b  与b-c  这样的情况出现
            Arrays.sort(persons);
            System.out.println("排序后:");
            System.out.println(Arrays.toString(persons));


            for(int i =0;i< persons.length -1 ;i++){

                for(int j = i+1;j<persons.length;j++){

                    context.write(new Text(persons[i]+"-"+persons[j]),new Text(friends));

                }

            }
        }
    }

    public static class ComonsFriendStepTwoReducer extends Reducer<Text,Text,Text,Text> {
        /**
         *
         *  A-B C
         *  A-B D
         *  A-B E
         *
         * @param key
         * @param values
         * @param context
         * @throws IOException
         * @throws InterruptedException
         */
        @Override
        protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
            StringBuffer buffer = new StringBuffer();

            for (Text value : values) {

                buffer.append(value.toString()+"\t");

            }

            context.write(key,new Text(buffer.toString()));
        }
    }
    public static void main(String[] args) throws Exception {
        ToolRunner.run(new Configuration(),new ComonsFriendsStepTwo(),args);
    }
}

过程分析

首先,你得把代码跑一遍,即使你看不懂它到底是什么意思。

首先我们可以看到输入的数据是这样的

www.zeeklog.com  - MapReduce综合练习题(2)社交粉丝数据分析


这个过程分为两个阶段,每个阶段又有map和reduce的过程,所以看起来有些绕。
先说第一个过程。
1、map拿到了每个用户的好友列表。
2、map输出了它的其中一个好友,和这个用户本身。
我们知道,map输出的是 key和value 的list也就是list(key,value)这种形式。
而经过map和reduce中间的一系列过程。
它会变成一个key,list(value)这种形式被reduce接收到。

3、reduce接收到 一个作为别人好友的用户A,拥有这个用户A好友的用户列表

这时,reduce接收到:
key:好友人用户A
values:把用户A作为好友的用户们。

4、reduce接收到之后就把它们格式化了一下直接就保存到结果中了。
整个过程数据是这样变化的:
map接收:
A:B,C,D,E
map输出:
B-A
C-A
D-A
E-A
reduce接收:
B ,list(A,C,D,E)
reduce输出
B A-C-D-E

也就是说,经过第一阶段,拿到了像下面这样的数据。

www.zeeklog.com  - MapReduce综合练习题(2)社交粉丝数据分析

然后是第二阶段。

可以看出,第二阶段接收的正是第一阶段输出的内容。
而第二阶段关键的地方来了,请看如下代码:

String[] split = value.toString().split("\t");

String friends = split[0];
String[] persons = split[1].split("-");

System.out.println("排序前");
System.out.println(Arrays.toString(persons));

//排序,避免c-b  与b-c  这样的情况出现
Arrays.sort(persons);
System.out.println("排序后:");
System.out.println(Arrays.toString(persons));


for(int i =0;i< persons.length -1 ;i++){

    for(int j = i+1;j<persons.length;j++){

        context.write(new Text(persons[i]+"-"+persons[j]),new Text(friends));

    }

}

这里边把右边用-连起来的一串,拆分成一个数组了,这个时候给数组排序。

排序前
[J, I, H, A, F]
排序后:
[A, F, H, I, J]

以上排序结果后,再遍历两次这个数组,这个遍历实现了如下效果:
把persons数组中的第n个和它后面所有的都组成了一个A-B形式的这样的组合,然后就输出给reduce了。
这里的A-B的含义是什么呢?

注意,map接收到的数据是:
一个作为别人好友的用户A,拥有这个用户A好友的用户列表(用户们)

这时候他把有用户A好友的这些人,都配了个对,两两配对,作为key输出了
而这个好友人(工具人)A
这个A作为value。
也就是:

map输出了:
key : 好友列表有A的用户B,好友列表有A的用户C
value: 作为好友人(工具人)A

这个map输出的意思是啥?不就是
B和C有共同的好友A吗?

嗯?你说是不是?答案出来了!
所以第二阶段这个reduce接收到了这个正确答案,只是格式化了一下,就给输出了

   StringBuffer buffer = new StringBuffer();

            for (Text value : values) {

                buffer.append(value.toString()+"\t");

            }

            context.write(key,new Text(buffer.toString()));

reduce 输出的正是 用户B和用户C共同拥有的好友们啊!

然后结果出来了,如下。

www.zeeklog.com  - MapReduce综合练习题(2)社交粉丝数据分析