hadoop 中文词频排序 top-k 问题

    本人最近一直在hadoop领域,摸爬滚打,由于最近老是布置了一项作业:让统计一个文件中出现次数最高的单词。一看到题目我就想用hadoop来实现这个问题,由于有现成的wordcount框架,所以就在这之上进行程序的修改添加即可。

    准备过程:

    1、我去下载了金庸的小说全集,顺便分析分析,看他老人家笔下,谁的戏份更重。

    2、由于是中文分词,所以必须要有一个中文分词器,找到了一个java版的apache开源分词器。性能还行,但是在一些词语拆分上还是有些不够智能。它的算法实现是用到了RMM算法,在次不多介绍。

    编码设计:我用的是mapreduce老框架,并且程序实现需要连接多个mapreduce任务,所以就在此介绍mapreduce的设计思路吧

    1、在wordcount的基础上,第一个map实现分词,并筛选出key/value键值对(如:张无忌  ----  1)并输出。map的分词过程,会按照行进行划分。

    2、第一个个reduce接收第一个map的键值对,然后进行累加整合。reduce接收的是key/<value--list>同一个key的所有值会被整合成一个列表,作为reduce的输入如(张无忌:1,1,1,1,1)。最终reduce加工后的输出是key/value,如(张无忌  5)。

    3、第二个map,此处是调用了hadoop的api,Inversemaper.class 其实现功能是将key和value位置进行互换。这样做的原因是,hadoop默认对key进行升序排序,由于我们要统计的是最多的单词,所以需要的数量进行排序,数量在第一个reduce的输出中,为value,所以该步需要对key和value进行互换。   

    4、然后对key进行降序排序

    5、最后一个map,作为计数器,显示出词频出现最高的k个词语。

    这个程序差不多就介绍完了,这是我做的统计我们
4380 张无忌
4623 只见
4643 两人
4873 黄蓉
4994 不知
5055 杨过
5285 心想
5352 令狐冲
5462 听得
5501 师父
5569 他们
5905 心中
6173 郭靖
6309 武功
6487 一声
6697 咱们
7336 甚么
7359 笑道
7779 一个
9828 韦小宝
9872 自己
13157 说道

    哈哈,发现,有9个老婆的韦小宝,果然是金庸老先生,浓墨重彩的

   参考链接:http://blog.csdn.net/xw13106209/article/details/6122719

                       http://my.oschina.net/s01u/blog/39535



package chineseword;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.ByteArrayInputStream;
import java.io.StringReader;
import java.util.Random;
import java.util.StringTokenizer;

import org.wltea.analyzer.core.IKSegmenter;
import org.wltea.analyzer.core.Lexeme;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.WritableComparable;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.Mapper.Context;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.SequenceFileInputFormat;
import org.apache.hadoop.mapreduce.lib.map.InverseMapper;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.SequenceFileOutputFormat;
import org.apache.hadoop.util.GenericOptionsParser;


public class cword {
    
      public static class TokenizerMapper 
           extends Mapper<Object, Text, Text, IntWritable>{
        
        private final static IntWritable one = new IntWritable(1);
        private Text word = new Text();
        public void map(Object key, Text value, Context context
                        ) throws IOException, InterruptedException {
            	byte[] bt = value.toString().getBytes();//**************************8
                InputStream ip = new ByteArrayInputStream(bt);
                Reader read = new InputStreamReader(ip);
//        	StringReader read=new StringReader(value.toString());
                IKSegmenter iks = new IKSegmenter(read,true);
                Lexeme t=null;
                while ((t = iks.next()) != null)
                {
                    word.set(t.getLexemeText());
                   // System.out.print(word+" ");
                    context.write(word, one);
                }
                
                //System.out.println();
        }
      }
      
      public static class TokenizerMapper2 
      extends Mapper<Object, Text,IntWritable,Text>{
  	  int c=0;
        public void map(Object key, Text value, Context context
                        ) throws IOException, InterruptedException {
          StringTokenizer itr = new StringTokenizer(value.toString());
          IntWritable a=new IntWritable(Integer.parseInt(itr.nextToken()));
          Text b=new Text(itr.nextToken());
          
          if((c<100)&b.getLength()>5){
   	       //System.out.println("sss");
            context.write(a, b);
            //System.out.println(" "+b.getLength());
            c++;
          }
        }
       }
      
      private static class IntWritableDecreasingComparator extends IntWritable.Comparator {  
           public int compare(WritableComparable a, WritableComparable b) { 
         	  //System.out.println("ss");
             return -super.compare(a, b);  
           }  
             
           public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {  
         	  //System.out.println("ss1");
               return -super.compare(b1, s1, l1, b2, s2, l2);  
           }  
       }  
  public static class IntSumReducer 
       extends Reducer<Text,IntWritable,Text,IntWritable> {
    private IntWritable result = new IntWritable();

    public void reduce(Text key, Iterable<IntWritable> values, 
                       Context context
                       ) throws IOException, InterruptedException {
      int sum = 0;
      for (IntWritable val : values) {
        sum += val.get();
      }
      result.set(sum);
      context.write(key, result);
    }
  }

  public static void main(String[] args) throws Exception {  
      Configuration conf = new Configuration();  
      String[] otherArgs = new GenericOptionsParser(conf, args)  
              .getRemainingArgs();  
      if (otherArgs.length != 2) {  
          System.err.println("Usage: wordcount <in> <out>");  
          System.exit(2);  
      }  
       Path tempDir = new Path("wordcount-temp-" + Integer.toString(  
                  new Random().nextInt(Integer.MAX_VALUE))); //定义一个临时目录  
       Path tempDir2 = new Path("wordcount-temp2-" + Integer.toString(  
               new Random().nextInt(Integer.MAX_VALUE))); //定义一个临时目录  
        
      Job job = new Job(conf, "word count");  
      job.setJarByClass(cword.class);  
      try{  
          job.setMapperClass(TokenizerMapper.class);  
          job.setCombinerClass(IntSumReducer.class);  
          job.setReducerClass(IntSumReducer.class);  
            
          job.setOutputKeyClass(Text.class);  
          job.setOutputValueClass(IntWritable.class);  
            
          FileInputFormat.addInputPath(job, new Path(otherArgs[0]));  
          FileOutputFormat.setOutputPath(job, tempDir);//先将词频统计任务的输出结果写到临时目  
                                                       //录中, 下一个排序任务以临时目录为输入目录。  
          job.setOutputFormatClass(SequenceFileOutputFormat.class);  
          if(job.waitForCompletion(true))  
          {  
              Job sortJob = new Job(conf, "sort");  
              sortJob.setJarByClass(cword.class);  
                
              FileInputFormat.addInputPath(sortJob, tempDir);  
              sortJob.setInputFormatClass(SequenceFileInputFormat.class);  
                
              /*InverseMapper由hadoop库提供,作用是实现map()之后的数据对的key和value交换*/  
              sortJob.setMapperClass(InverseMapper.class);  
              /*将 Reducer 的个数限定为1, 最终输出的结果文件就是一个。*/  
              sortJob.setNumReduceTasks(1);   
              FileOutputFormat.setOutputPath(sortJob, tempDir2);  
                
              sortJob.setOutputKeyClass(IntWritable.class);  
              sortJob.setOutputValueClass(Text.class);  
              /*Hadoop 默认对 IntWritable 按升序排序,而我们需要的是按降序排列。 
               * 因此我们实现了一个 IntWritableDecreasingComparator 类,  
               * 并指定使用这个自定义的 Comparator 类对输出结果中的 key (词频)进行排序*/  
              sortJob.setSortComparatorClass(IntWritableDecreasingComparator.class);  
              if(sortJob.waitForCompletion(true))
              {
              	Job topJob = new Job(conf, "sort");  
              	topJob.setJarByClass(cword.class);  
                    
                  FileInputFormat.addInputPath(topJob, tempDir2);  
                  //topJob.setInputFormatClass(SequenceFileInputFormat.class);  
                    
                  /*InverseMapper由hadoop库提供,作用是实现map()之后的数据对的key和value交换*/  
                  topJob.setMapperClass(TokenizerMapper2.class);  
                  /*将 Reducer 的个数限定为1, 最终输出的结果文件就是一个。*/  
                  topJob.setNumReduceTasks(1);   
                  FileOutputFormat.setOutputPath(topJob, new Path(otherArgs[1]));  
                    
                  topJob.setOutputKeyClass(IntWritable.class);  
                  topJob.setOutputValueClass(Text.class);  
                  System.exit(topJob.waitForCompletion(true) ? 0 : 1);  
              }
              
     
             // System.exit(sortJob.waitForCompletion(true) ? 0 : 1);  
          }  
      }finally{  
          FileSystem.get(conf).deleteOnExit(tempDir);  
      }  
  }  
}



你可能感兴趣的:(java,mapreduce,hadoop)