mapreduce编程实例(4)-求中位数和标准差

来源:互联网 发布:sqlyog怎么导入sql文件 编辑:程序博客网 时间:2024/05/16 19:46

这个实例解决问题是:计算一天的每个小时中,网站新增评论长度的中位数和这些长度之间的标准差。代码如下:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  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.ArrayList;  
  9. import java.util.Collections;  
  10. import java.util.Date;  
  11. import java.util.Map;  
  12.   
  13. import mrdp.utils.MRDPUtils;  
  14.   
  15. import org.apache.hadoop.conf.Configuration;  
  16. import org.apache.hadoop.fs.Path;  
  17. import org.apache.hadoop.io.IntWritable;  
  18. import org.apache.hadoop.io.Text;  
  19. import org.apache.hadoop.io.Writable;  
  20. import org.apache.hadoop.mapreduce.Job;  
  21. import org.apache.hadoop.mapreduce.Mapper;  
  22. import org.apache.hadoop.mapreduce.Reducer;  
  23. import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;  
  24. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;  
  25. import org.apache.hadoop.util.GenericOptionsParser;  
  26.   
  27. public class MedianStdDevDriver {  
  28.   
  29.     public static class SOMedianStdDevMapper extends  
  30.             Mapper<Object, Text, IntWritable, IntWritable> {  
  31.   
  32.         private IntWritable outHour = new IntWritable();  
  33.         private IntWritable outCommentLength = new IntWritable();  
  34.   
  35.         private final static SimpleDateFormat frmt = new SimpleDateFormat(  
  36.                 "yyyy-MM-dd'T'HH:mm:ss.SSS");  
  37.   
  38.         @SuppressWarnings("deprecation")  
  39.         @Override  
  40.         public void map(Object key, Text value, Context context)  
  41.                 throws IOException, InterruptedException {  
  42.   
  43.             // Parse the input string into a nice map  
  44.             Map<String, String> parsed = MRDPUtils.transformXmlToMap(value.toString());  
  45.   
  46.             // Grab the "CreationDate" field,  
  47.             // since it is what we are grouping by  
  48.             String strDate = parsed.get("CreationDate");  
  49.   
  50.             // Grab the comment to find the length  
  51.             String text = parsed.get("Text");  
  52.   
  53.             // .get will return null if the key is not there  
  54.             if (strDate == null || text == null) {  
  55.                 // skip this record  
  56.                 return;  
  57.             }  
  58.   
  59.             try {  
  60.                 // get the hour this comment was posted in  
  61.                 Date creationDate = frmt.parse(strDate);  
  62.                 outHour.set(creationDate.getHours());  
  63.   
  64.                 // get the comment length  
  65.                 outCommentLength.set(text.length());  
  66.   
  67.                 // write out the user ID with min max dates and count  
  68.                 context.write(outHour, outCommentLength);  
  69.   
  70.             } catch (ParseException e) {  
  71.                 System.err.println(e.getMessage());  
  72.                 return;  
  73.             }  
  74.         }  
  75.     }  
  76.   
  77.     public static class SOMedianStdDevReducer extends  
  78.             Reducer<IntWritable, IntWritable, IntWritable, MedianStdDevTuple> {  
  79.         private MedianStdDevTuple result = new MedianStdDevTuple();  
  80.         private ArrayList<Float> commentLengths = new ArrayList<Float>();  
  81.   
  82.         @Override  
  83.         public void reduce(IntWritable key, Iterable<IntWritable> values,  
  84.                 Context context) throws IOException, InterruptedException {  
  85.   
  86.             float sum = 0;  
  87.             float count = 0;  
  88.             commentLengths.clear();  
  89.             result.setStdDev(0);  
  90.               
  91.             // Iterate through all input values for this key  
  92.             for (IntWritable val : values) {  
  93.                 commentLengths.add((float) val.get());  
  94.                 sum += val.get();  
  95.                 ++count;  
  96.             }  
  97.   
  98.             // sort commentLengths to calculate median  
  99.             Collections.sort(commentLengths);  
  100.   
  101.             // if commentLengths is an even value, average middle two elements  
  102.             if (count % 2 == 0) {  
  103.                 result.setMedian((commentLengths.get((int) count / 2 - 1) + commentLengths  
  104.                         .get((int) count / 2)) / 2.0f);  
  105.             } else {  
  106.                 // else, set median to middle value  
  107.                 result.setMedian(commentLengths.get((int) count / 2));  
  108.             }  
  109.   
  110.             // calculate standard deviation  
  111.             float mean = sum / count;  
  112.   
  113.             float sumOfSquares = 0.0f;  
  114.             for (Float f : commentLengths) {  
  115.                 sumOfSquares += (f - mean) * (f - mean);  
  116.             }  
  117.   
  118.             result.setStdDev((float) Math.sqrt(sumOfSquares / (count - 1)));  
  119.   
  120.             context.write(key, result);  
  121.         }  
  122.     }  
  123.   
  124.     public static void main(String[] args) throws Exception {  
  125.         Configuration conf = new Configuration();  
  126.         String[] otherArgs = new GenericOptionsParser(conf, args)  
  127.                 .getRemainingArgs();  
  128.         if (otherArgs.length != 2) {  
  129.             System.err.println("Usage: MedianStdDevDriver <in> <out>");  
  130.             System.exit(2);  
  131.         }  
  132.         Job job = new Job(conf,  
  133.                 "StackOverflow Comment Length Median StdDev By Hour");  
  134.         job.setJarByClass(MedianStdDevDriver.class);  
  135.         job.setMapperClass(SOMedianStdDevMapper.class);  
  136.         job.setReducerClass(SOMedianStdDevReducer.class);  
  137.         job.setMapOutputKeyClass(IntWritable.class);  
  138.         job.setMapOutputValueClass(IntWritable.class);  
  139.         job.setOutputKeyClass(IntWritable.class);  
  140.         job.setOutputValueClass(MedianStdDevTuple.class);  
  141.         FileInputFormat.addInputPath(job, new Path(otherArgs[0]));  
  142.         FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));  
  143.         System.exit(job.waitForCompletion(true) ? 0 : 1);  
  144.     }  
  145.   
  146.     public static class MedianStdDevTuple implements Writable {  
  147.         private float median = 0;  
  148.         private float stddev = 0f;  
  149.   
  150.         public float getMedian() {  
  151.             return median;  
  152.         }  
  153.   
  154.         public void setMedian(float median) {  
  155.             this.median = median;  
  156.         }  
  157.   
  158.         public float getStdDev() {  
  159.             return stddev;  
  160.         }  
  161.   
  162.         public void setStdDev(float stddev) {  
  163.             this.stddev = stddev;  
  164.         }  
  165.   
  166.         @Override  
  167.         public void readFields(DataInput in) throws IOException {  
  168.             median = in.readFloat();  
  169.             stddev = in.readFloat();  
  170.         }  
  171.   
  172.         @Override  
  173.         public void write(DataOutput out) throws IOException {  
  174.             out.writeFloat(median);  
  175.             out.writeFloat(stddev);  
  176.         }  
  177.   
  178.         @Override  
  179.         public String toString() {  
  180.             return median + "\t" + stddev;  
  181.         }  
  182.     }  
  183. }  
这里在计算中位数时稍微有点技巧,先把所有的commments 长度存入一个数组中,然后对这个数据进行排序,排序完后取下标为中间那个即可。求中间下标那个对应的长度时,分两种情况,即数组长度为偶数和奇数时,做了分别计算。

求标准差就是简单的根据数学定义求的。

计算结果如下:

[plain] view plaincopy在CODE上查看代码片派生到我的代码片
  1. jpan@jpan-Beijing:~/Mywork/mapreducepatterns/testdata$ hadoop fs -cat output3/part-r-00000  
  2. 0   145.5   158.66512  
  3. 1   218.0   150.04599  
  4. 2   139.0   148.84734  
  5. 3   200.0   158.28148  
  6. 4   139.5   158.62466  
  7. 5   122.5   167.31377  
  8. 6   199.5   160.57263  
  9. 7   238.0   175.86475  
  10. 8   253.5   164.08226  
  11. 9   232.0   167.5952  
  12. 10  200.0   157.11778  
  13. 11  179.0   144.3936  
  14. 12  172.0   148.96738  
  15. 13  229.0   134.17366  
  16. 14  207.0   147.26193  
  17. 15  224.0   147.52689  
  18. 16  143.0   130.6711  
  19. 17  177.0   158.20508  
  20. 18  199.0   159.31636  
  21. 19  175.5   147.4742  
  22. 20  169.0   138.74756  
  23. 21  164.0   141.22824  
  24. 22  152.5   122.51671  
  25. 23  145.0   160.20476 
0 0
原创粉丝点击