mapreduce分组统计_MapReduce分组

本文详细介绍了如何使用MapReduce实现分组统计,通过自定义Mapper和Reducer类来找出每组的最大值。首先,通过MyMapper将数据按照第一列进行分组,然后在MyReducer中对每个分组内的第二列取最大值。此外,还讨论了另一种方法,通过自定义分组规则(MyGroupingComparator),确保相同的第一列数值被分在同一组内,从而正确地进行分组统计。
摘要由CSDN通过智能技术生成

分组:相同key的value进行分组

例子:如下输入输出,右边的第一列没有重复值,第二列取得是当第一列相同时第二例取最大值

分析:首先确定,k3的选择两种方式,

方法1.前两列都作为k3

方法2.两列分别是k3和v3,此种情况的k2和v2分别是那些,第一列为k2,第二列为v2,但是最后如何无法转化为k3,v3呢,思路是从v2s中取值最大的,此种情况不能取值。

第一部分:方法二达到任务目的

(1)自定义Mapper

1 private static class MyMapper extends Mapper{2 IntWritable k2= newIntWritable();3 IntWritable v2= newIntWritable();4 @Override5 protected voidmap(LongWritable key, Text value,6 Mapper.Context context)7 throwsIOException, InterruptedException {8 String[] splited = value.toString().split("\t");9 k2.set(Integer.parseInt(splited[0]));10 v2.set(Integer.parseInt(splited[1]));11 context.write(k2, v2);12 }13 }

(2)自定义Reduce

//按照k2進行排序,分組(分为3各组,reduce函数被调用3次,分几组调用几次)

//分组为3-{3,2,1}, 2-{2,1},1-{1}

1 private static class MyReducer extends Reducer{2 IntWritable v3 = newIntWritable();3 @Override4 protected void reduce(IntWritable k2, Iterablev2s,5 Reducer.Context context)6 throwsIOException, InterruptedException {7 int max=Integer.MIN_VALUE;8 for(IntWritable v2 : v2s) {9 if (v2.get()>max) {10 max=v2.get();11 }12 }13 //每个组求得一个最大值可得到结果的序列

14 v3.set(max);15 context.write(k2, v3);16 }17 }

(3)组合MapReduce

1 public static void main(String[] args) throwsException {2 Job job = Job.getInstance(new Configuration(), GroupTest.class.getSimpleName());3 job.setJarByClass(GroupTest.class);4 //1.自定义输入路径

5 FileInputFormat.setInputPaths(job, new Path(args[0]));6 //2.自定义mapper7 //job.setInputFormatClass(TextInputFormat.class);

8 job.setMapperClass(MyMapper.class);9 //job.setMapOutputKeyClass(Text.class);10 //job.setMapOutputValueClass(TrafficWritable.class);11

12 //3.自定义reduce

13 job.setReducerClass(MyReducer.class);14 job.setOutputKeyClass(IntWritable.class);15 job.setOutputValueClass(IntWritable.class);16 //4.自定义输出路径

17 FileOutputFormat.setOutputPath(job, new Path(args[1]));18 //job.setOutputFormatClass(TextOutputFormat.class);//对输出的数据格式化并写入磁盘

19

20 job.waitForCompletion(true);21 }

由此,完整的代码如下:

1 packageMapreduce;2

3 importjava.io.IOException;4

5 importorg.apache.hadoop.conf.Configuration;6 importorg.apache.hadoop.fs.Path;7 importorg.apache.hadoop.io.IntWritable;8 importorg.apache.hadoop.io.LongWritable;9 importorg.apache.hadoop.io.Text;10 importorg.apache.hadoop.mapreduce.Job;11 importorg.apache.hadoop.mapreduce.Mapper;12 importorg.apache.hadoop.mapreduce.Reducer;13 importorg.apache.hadoop.mapreduce.lib.input.FileInputFormat;14 importorg.apache.hadoop.mapreduce.lib.output.FileOutputFormat;15

16 public classGroupTest {17 public static void main(String[] args) throwsException {18 Job job = Job.getInstance(new Configuration(), GroupTest.class.getSimpleName());19 job.setJarByClass(GroupTest.class);20 //1.自定义输入路径

21 FileInputFormat.setInputPaths(job, new Path(args[0]));22 //2.自定义mapper23 //job.setInputFormatClass(TextInputFormat.class);

24 job.setMapperClass(MyMapper.class);25 //job.setMapOutputKeyClass(Text.class);26 //job.setMapOutputValueClass(TrafficWritable.class);27

28 //3.自定义reduce

29 job.setReducerClass(MyReducer.class);30 job.setOutputKeyClass(IntWritable.class);31 job.setOutputValueClass(IntWritable.class);32 //4.自定义输出路径

33 FileOutputFormat.setOutputPath(job, new Path(args[1]));34 //job.setOutputFormatClass(TextOutputFormat.class);//对输出的数据格式化并写入磁盘

35

36 job.waitForCompletion(true);37 }38 private static class MyMapper extends Mapper{39 IntWritable k2= newIntWritable();40 IntWritable v2= newIntWritable();41 @Override42 protected voidmap(LongWritable key, Text value,43 Mapper.Context context)44 throwsIOException, InterruptedException {45 String[] splited = value.toString().split("\t");46 k2.set(Integer.parseInt(splited[0]));47 v2.set(Integer.parseInt(splited[1]));48 context.write(k2, v2);49 }50 }51 //按照k2進行排序,分組(分为3各组,reduce函数被调用3次,分几组调用几次)52 //分组为3-{3,2,1}, 2-{2,1},1-{1}

53 private static class MyReducer extends Reducer{54 IntWritable v3 = newIntWritable();55 @Override56 protected void reduce(IntWritable k2, Iterablev2s,57 Reducer.Context context)58 throwsIOException, InterruptedException {59 int max=Integer.MIN_VALUE;60 for(IntWritable v2 : v2s) {61 if (v2.get()>max) {62 max=v2.get();63 }64 }65 //每个组求得一个最大值可得到结果的序列

66 v3.set(max);67 context.write(k2, v3);68 }69 }70 }

最值得MapReduce代码

(4)测试代码运行结果

[root@neusoft-master filecontent]# hadoop jar GroupTest.jar /neusoft/twoint  /out9

[root@neusoft-master filecontent]# hadoop jar -text  /out9/part-r-00000

[root@neusoft-master filecontent]# hadoop dfs -text  /out9/part-r-00000

第二部分:方法一达到任务目的

前两列都作为k3,无v3,由此类推,k2也是前两列

但是如果采用默认分组的话,上述数据集分为6组,无法达到同样的数值取得最大值的目的。

由此,利用Mapreduce的自定义分组规则,使得第一列相同的数值可以在一个组里面,从而正确的分组。

MapReduce提供了job.setGroupingComparatorClass(cls);其中cls是自定义分组的类

(1) 从源代码可知,该类需要继承RawComparator类,自定义分组代码如下:

1 //分组比较--自定义分组

2 private static class MyGroupingComparator implementsRawComparator {3 public intcompare(Object o1, Object o2) {4 return 0;//默认的比较方法

5 }6 //byte[] b1 表示第一个参数的输入字节表示,byte[] b2表示第二个参数的输入字节表示7 //b1 The first byte array. 第一个字节数组,8 //b1表示前8个字节,b2表示后8个字节,字节是按次序依次比较的9 //s1 The position index in b1. The object under comparison's starting index.第一列开始位置10 //l1 The length of the object in b1.第一列长度 ,在这里表示长度811 //提供的数据集中的k2一共48个字节,k2的每一行的TwoInt类型表示8字节(t1和t2分别为4字节)

12 public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, intl2) {13 //compareBytes是按字节比较的方法,其中k2表示的是两列,第一列比较,第二例不比较14 //第一个字节数组的前四个字节和第二个字节数组的前四个字节比较15 //{3,3},{3,2},{3,1},{2,2},{2,1},{1,1}16 //比较上述六组的每组的第一个数字,也就是比较twoint中得t1数值17 //现在就根据t1可分成3个组了{3,(3,2,1)}{2,(2,1)}{1,1}18 //之后再从v2中取出最大值

19 return WritableComparator.compareBytes(b1, s1, l1-4, b2, s2, l2-4);20 }21

22 }

(2)主函数中调用

//当没有下面的自定义分组的话,会调用k2的compareto方法执行k2的比较,如果自定义了分组类则使用自定义分组类

job.setGroupingComparatorClass(MyGroupingComparator.class);

(3)根据比较函数个字段的含义,可以得到v2的类型为intwritable,而不是nullwritable,v2是由第二列的数组成的集合

Mapper函数如下:

1 private static class MyMapper extends

2 Mapper{3 //这里的v2需要改为IntWritable而不是nullwritable

4 TwoInt K2 = newTwoInt();5 IntWritable v2= newIntWritable();6 @Override7 protected voidmap(LongWritable key, Text value,8 Mapper.Context context)9 throwsIOException, InterruptedException {10 String[] splited = value.toString().split("\t");11 K2.set(Integer.parseInt(splited[0]), Integer.parseInt(splited[1]));12 v2.set(Integer.parseInt(splited[1])); //要比较第二列,需要将第二列的值赋值为v213 context.write(K2, v2);14 }15 }

(4)k3和v3的类型为reduce输出的类型,均为intwritable类型,但是如何根据得到的v2去统计其中相同key的value中得最大值呢?

1 private static class MyReducer extends

2 Reducer {//k2,v2s,k3,v3

3 IntWritable k3 = newIntWritable();4 IntWritable v3 = newIntWritable();5 @Override6 protected voidreduce(7 TwoInt k2,8 Iterablev2s,9 Reducer.Context context)10 throwsIOException, InterruptedException {11 int max=Integer.MIN_VALUE;12 for(IntWritable v2 : v2s) {13 if (v2.get()>max) {14 max=v2.get();15 }16 }17 //每个组求得一个最大值可得到结果的序列

18 v3.set(max);19 k3.set(k2.t1);//k2的第一列作为k3,因为k2为Twoint类型

20 context.write(k3,v3);21 }22 }

最终的代码如下:

1 packageMapreduce;2

3 importjava.io.DataInput;4 importjava.io.DataOutput;5 importjava.io.IOException;6

7 importorg.apache.hadoop.conf.Configuration;8 importorg.apache.hadoop.fs.Path;9 importorg.apache.hadoop.io.IntWritable;10 importorg.apache.hadoop.io.LongWritable;11 importorg.apache.hadoop.io.NullWritable;12 importorg.apache.hadoop.io.RawComparator;13 importorg.apache.hadoop.io.Text;14 importorg.apache.hadoop.io.WritableComparable;15 importorg.apache.hadoop.io.WritableComparator;16 importorg.apache.hadoop.mapreduce.Job;17 importorg.apache.hadoop.mapreduce.Mapper;18 importorg.apache.hadoop.mapreduce.Reducer;19 importorg.apache.hadoop.mapreduce.lib.input.FileInputFormat;20 importorg.apache.hadoop.mapreduce.lib.output.FileOutputFormat;21

22

23 public classGroup2Test {24 public static void main(String[] args) throwsException {25 Job job = Job.getInstance(newConfiguration(),26 Group2Test.class.getSimpleName());27 job.setJarByClass(Group2Test.class);28 //1.自定义输入路径

29 FileInputFormat.setInputPaths(job, new Path(args[0]));30 //2.自定义mapper

31 job.setMapperClass(MyMapper.class);32 //这里的k2,v2和k3,v3不同,需要显式定义k2和v2类型

33 job.setMapOutputKeyClass(TwoInt.class);34 job.setMapOutputValueClass(IntWritable.class);35

36 //当没有下面的自定义分组的话,会调用k2的compareto方法执行k2的比较,如果自定义了分组类则使用自定义分组类

37 job.setGroupingComparatorClass(MyGroupingComparator.class);38

39 //3.自定义reduce

40 job.setReducerClass(MyReducer.class);41 job.setOutputKeyClass(IntWritable.class);42 job.setOutputValueClass(IntWritable.class);43 //4.自定义输出路径

44 FileOutputFormat.setOutputPath(job, new Path(args[1]));45

46 job.waitForCompletion(true);47 }48 //分组比较--自定义分组

49 private static class MyGroupingComparator implementsRawComparator {50 public intcompare(Object o1, Object o2) {51 return 0;//默认的比较方法

52 }53 //byte[] b1 表示第一个参数的输入字节表示,byte[] b2表示第二个参数的输入字节表示54 //b1 The first byte array. 第一个字节数组,55 //b1表示前8个字节,b2表示后8个字节,字节是按次序依次比较的56 //s1 The position index in b1. The object under comparison's starting index.第一列开始位置57 //l1 The length of the object in b1.第一列长度 ,在这里表示长度858 //提供的数据集中的k2一共48个字节,k2的每一行的TwoInt类型表示8字节(t1和t2分别为4字节)

59 public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, intl2) {60 //compareBytes是按字节比较的方法,其中k2表示的是两列,第一列比较,第二例不比较61 //第一个字节数组的前四个字节和第二个字节数组的前四个字节比较62 //{3,3},{3,2},{3,1},{2,2},{2,1},{1,1}63 //比较上述六组的每组的第一个数字,也就是比较twoint中得t1数值64 //现在就根据t1可分成3个组了{3,(3,2,1)}{2,(2,1)}{1,1}65 //之后再从v2中取出最大值

66 return WritableComparator.compareBytes(b1, s1, l1-4, b2, s2, l2-4);67 }68

69 }70

71 private static class MyMapper extends

72 Mapper{73 //这里的v2需要改为IntWritable而不是nullwritable

74 TwoInt K2 = newTwoInt();75 IntWritable v2= newIntWritable();76 @Override77 protected voidmap(LongWritable key, Text value,78 Mapper.Context context)79 throwsIOException, InterruptedException {80 String[] splited = value.toString().split("\t");81 K2.set(Integer.parseInt(splited[0]), Integer.parseInt(splited[1]));82 v2.set(Integer.parseInt(splited[1]));83 context.write(K2, v2);84 }85 }86

87 private static class MyReducer extends

88 Reducer {//k2,v2s,k3,v3

89 IntWritable k3 = newIntWritable();90 IntWritable v3 = newIntWritable();91 @Override92 protected voidreduce(93 TwoInt k2,94 Iterablev2s,95 Reducer.Context context)96 throwsIOException, InterruptedException {97 int max=Integer.MIN_VALUE;98 for(IntWritable v2 : v2s) {99 if (v2.get()>max) {100 max=v2.get();101 }102 }103 //每个组求得一个最大值可得到结果的序列

104 v3.set(max);105 k3.set(k2.t1);//k2的第一列作为k3,因为k2为Twoint类型

106 context.write(k3,v3);107 }108 }109

110 private static class TwoInt implements WritableComparable{111 public intt1;112 public intt2;113

114 public void write(DataOutput out) throwsIOException {115 out.writeInt(t1);116 out.writeInt(t2);117 }118

119 public void set(int t1, intt2) {120 this.t1 =t1;121 this.t2 =t2;122 }123

124 public void readFields(DataInput in) throwsIOException {125 this.t1 =in.readInt();126 this.t2 =in.readInt();127 }128

129 public intcompareTo(TwoInt o) {130 if (this.t1 == o.t1) { //當第一列相等的時候,第二列升序排列

131 return this.t2 -o.t2;132 }133 return this.t1 - o.t1;//當第一列不相等的時候,按第一列升序排列

134 }135 @Override136 publicString toString() {137 return t1 + "\t" +t2;138 }139 }140 }

方法1求最值

测试并运行结果如下:

[root@neusoft-master filecontent]# hadoop dfs -text  /out9/part-r-00000

[root@neusoft-master filecontent]# hadoop dfs -text  /out10/part-r-00000

结果是正确无误的。

END~

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值