mapreduce编程实例(2)-求最大值和最小值

在网站的数据统计中,有这样一种情况,即统计某个用户发表的评论数、第一次发表评论的时间和最后一次发表评论的时间。下面代码就是解决comments.xml的这个问题。代码如下:

[java] view plain copy
  1. package mrdp.ch2;  
  2.   
  3. import java.io.DataInput;  
  4. import java.io.DataOutput;  
  5. import java.io.IOException;  
  6. import java.text.ParseException;  
  7. import java.text.SimpleDateFormat;  
  8. import java.util.Date;  
  9. import java.util.Map;  
  10.   
  11. import mrdp.utils.MRDPUtils;  
  12.   
  13. import org.apache.hadoop.conf.Configuration;  
  14. import org.apache.hadoop.fs.Path;  
  15. import org.apache.hadoop.io.Text;  
  16. import org.apache.hadoop.io.Writable;  
  17. import org.apache.hadoop.mapreduce.Job;  
  18. import org.apache.hadoop.mapreduce.Mapper;  
  19. import org.apache.hadoop.mapreduce.Reducer;  
  20. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;  
  21. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;  
  22. import org.apache.hadoop.util.GenericOptionsParser;  
  23.   
  24. public class MinMaxCountDriver {  
  25.   
  26.     public static class SOMinMaxCountMapper extends  
  27.             Mapper<Object, Text, Text, MinMaxCountTuple> {  
  28.         // Our output key and value Writables  
  29.         private Text outUserId = new Text();  
  30.         private MinMaxCountTuple outTuple = new MinMaxCountTuple();  
  31.   
  32.         // This object will format the creation date string into a Date object  
  33.         private final static SimpleDateFormat frmt = new SimpleDateFormat(  
  34.                 "yyyy-MM-dd'T'HH:mm:ss.SSS");  
  35.   
  36.         @Override  
  37.         public void map(Object key, Text value, Context context)  
  38.                 throws IOException, InterruptedException {  
  39.   
  40.             // Parse the input string into a nice map  
  41.             Map<String, String> parsed = MRDPUtils.transformXmlToMap(value.toString());  
  42.   
  43.             // Grab the "CreationDate" field since it is what we are finding  
  44.             // the min and max value of  
  45.             String strDate = parsed.get("CreationDate");  
  46.   
  47.             // Grab the “UserID” since it is what we are grouping by  
  48.             String userId = parsed.get("UserId");  
  49.   
  50.             // .get will return null if the key is not there  
  51.             if (strDate == null || userId == null) {  
  52.                 // skip this record  
  53.                 return;  
  54.             }  
  55.   
  56.             try {  
  57.                 // Parse the string into a Date object  
  58.                 Date creationDate = frmt.parse(strDate);  
  59.   
  60.                 // Set the minimum and maximum date values to the creationDate  
  61.                 outTuple.setMin(creationDate);  
  62.                 outTuple.setMax(creationDate);  
  63.   
  64.                 // Set the comment count to 1  
  65.                 outTuple.setCount(1);  
  66.   
  67.                 // Set our user ID as the output key  
  68.                 outUserId.set(userId);  
  69.   
  70.                 // Write out the user ID with min max dates and count  
  71.                 context.write(outUserId, outTuple);  
  72.             } catch (ParseException e) {  
  73.                 // An error occurred parsing the creation Date string  
  74.                 // skip this record  
  75.             }  
  76.         }  
  77.     }  
  78.   
  79.     public static class SOMinMaxCountReducer extends  
  80.             Reducer<Text, MinMaxCountTuple, Text, MinMaxCountTuple> {  
  81.         private MinMaxCountTuple result = new MinMaxCountTuple();  
  82.   
  83.         @Override  
  84.         public void reduce(Text key, Iterable<MinMaxCountTuple> values,  
  85.                 Context context) throws IOException, InterruptedException {  
  86.   
  87.             // Initialize our result  
  88.             result.setMin(null);  
  89.             result.setMax(null);  
  90.             int sum = 0;  
  91.   
  92.             // Iterate through all input values for this key  
  93.             for (MinMaxCountTuple val : values) {  
  94.   
  95.                 // If the value's min is less than the result's min  
  96.                 // Set the result's min to value's  
  97.                 if (result.getMin() == null  
  98.                         || val.getMin().compareTo(result.getMin()) < 0) {  
  99.                     result.setMin(val.getMin());  
  100.                 }  
  101.   
  102.                 // If the value's max is less than the result's max  
  103.                 // Set the result's max to value's  
  104.                 if (result.getMax() == null  
  105.                         || val.getMax().compareTo(result.getMax()) > 0) {  
  106.                     result.setMax(val.getMax());  
  107.                 }  
  108.   
  109.                 // Add to our sum the count for val  
  110.                 sum += val.getCount();  
  111.             }  
  112.   
  113.             // Set our count to the number of input values  
  114.             result.setCount(sum);  
  115.   
  116.             context.write(key, result);  
  117.         }  
  118.     }  
  119.   
  120.     public static void main(String[] args) throws Exception {  
  121.         Configuration conf = new Configuration();  
  122.         String[] otherArgs = new GenericOptionsParser(conf, args)  
  123.                 .getRemainingArgs();  
  124.         if (otherArgs.length != 2) {  
  125.             System.err.println("Usage: MinMaxCountDriver <in> <out>");  
  126.             System.exit(2);  
  127.         }  
  128.         Job job = new Job(conf, "StackOverflow Comment Date Min Max Count");  
  129.         job.setJarByClass(MinMaxCountDriver.class);  
  130.         job.setMapperClass(SOMinMaxCountMapper.class);  
  131.         job.setCombinerClass(SOMinMaxCountReducer.class);  
  132.         job.setReducerClass(SOMinMaxCountReducer.class);  
  133.         job.setOutputKeyClass(Text.class);  
  134.         job.setOutputValueClass(MinMaxCountTuple.class);  
  135.         FileInputFormat.addInputPath(job, new Path(otherArgs[0]));  
  136.         FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));  
  137.         System.exit(job.waitForCompletion(true) ? 0 : 1);  
  138.     }  
  139.   
  140.     public static class MinMaxCountTuple implements Writable {  
  141.         private Date min = new Date();  
  142.         private Date max = new Date();  
  143.         private long count = 0;  
  144.   
  145.         private final static SimpleDateFormat frmt = new SimpleDateFormat(  
  146.                 "yyyy-MM-dd'T'HH:mm:ss.SSS");  
  147.   
  148.         public Date getMin() {  
  149.             return min;  
  150.         }  
  151.   
  152.         public void setMin(Date min) {  
  153.             this.min = min;  
  154.         }  
  155.   
  156.         public Date getMax() {  
  157.             return max;  
  158.         }  
  159.   
  160.         public void setMax(Date max) {  
  161.             this.max = max;  
  162.         }  
  163.   
  164.         public long getCount() {  
  165.             return count;  
  166.         }  
  167.   
  168.         public void setCount(long count) {  
  169.             this.count = count;  
  170.         }  
  171.   
  172.         @Override  
  173.         public void readFields(DataInput in) throws IOException {  
  174.             min = new Date(in.readLong());  
  175.             max = new Date(in.readLong());  
  176.             count = in.readLong();  
  177.         }  
  178.   
  179.         @Override  
  180.         public void write(DataOutput out) throws IOException {  
  181.             out.writeLong(min.getTime());  
  182.             out.writeLong(max.getTime());  
  183.             out.writeLong(count);  
  184.         }  
  185.   
  186.         @Override  
  187.         public String toString() {  
  188.             return frmt.format(min) + "\t" + frmt.format(max) + "\t" + count;  
  189.         }  
  190.     }  
  191. }  
这里的mrdp.utils.MRDPUtils包的代码在第一篇中已经给出。

这里最重要的是自己重写了writable函数,自己定义了value类型。有时间我另开一篇博客介绍下writable函数。

map阶段不做任何比较和计算,只是简单的对comments.xml进行解析,然后把每次评论的时间解析出来,并把count赋值为1.如解析下一列

[html] view plain copy
  1. <row Id="1784" PostId="883" Text="Perfect distinction. I've made a note and agree entirely." CreationDate="2012-02-08T21:51:05.223" UserId="46" />  

mapper会把UserID做为key,另外一个outTuple作为value,格式为(min,max,count)即(2012-02-08T21:51:05.223,2012-02-08T21:51:05.223,1)

compiler阶段直接调用的reduce函数,做中间处理。

reducer阶段计算我们需要的数据,即求最大值,最小值,总数。reducer的时间较简单,就是把每个uid对应的value循环取出,然后一一做比较,并计算count.

整个流程如下图:

mapreduce编程实例(2)-求最大值和最小值_第1张图片

得到的部分结果如下:

[plain] view plain copy
  1. jpan@jpan-Beijing:~/Mywork/mapreducepatterns/testdata$ hadoop fs -cat output2/part-r-00000  
  2. 10  2011-02-14T18:04:38.763 2012-07-10T22:57:00.757 8  
  3. 101 2011-04-01T03:02:45.083 2011-04-01T06:02:33.307 2  
  4. 10119   2012-02-08T13:54:38.623 2012-04-12T23:43:14.810 8  
  5. 1057    2011-06-17T19:59:33.013 2011-06-17T19:59:33.013 1  
  6. 10691   2012-04-19T01:15:44.573 2012-05-11T05:47:36.517 2  
  7. 10872   2012-06-14T15:36:26.527 2012-06-14T15:45:43.347 4  
  8. 10921   2011-12-07T18:08:04.583 2011-12-07T18:08:04.583 1  
  9. 11  2011-05-06T02:51:50.370 2011-05-06T14:46:31.483 3  
  10. 110 2010-08-12T14:52:09.830 2010-08-12T14:52:09.830 1  
  11. 1118    2011-02-17T10:27:48.623 2011-02-25T09:25:09.597 2  
  12. 11498   2011-12-30T11:09:58.057 2011-12-30T11:09:58.057 1  
  13. 11682   2012-01-04T21:48:39.267 2012-01-04T21:48:39.267 1 

你可能感兴趣的:(mapreduce编程实例(2)-求最大值和最小值)