在hadoop的map-reduce框架下实现倒排索引InvertedIndex算法

编程环境:

Ubuntu16.4 uklin
Hadoop3.2.0
openjdk version "1.8.0_191"
完整代码已经更新至GitHub,欢迎fork~GitHub链接


声明:创作不易,未经授权不得复制转载
statement:No reprinting without authorization


二、在本地编写程序和调试

1、mapper设计:

输入:
<string line> ---------读入文档的每行字符串

处理过程1:
<进行token,正则化去除英文数字外的字符,转为小写,利用空格符分词> ----------得到一个个独立的英文单词

处理过程2:
<得到文档的文件名,加在每隔单词的后面结合成输出的key值(中间用特殊字符分隔),这样设计方便统计每个单词在每篇文档中的词频信息>

处理过程3:
<将每个key对应的value值设为“1”>
输出:
<<key1,1>,<key2,1>,<key3,1>...>

示例:


image.png
//倒排索引mapper类
    public static class InvertedIndexMapper extends Mapper<LongWritable, Text, Text, Text>{

        private static Text keyInfo = new Text();// 存储单词和文档名组合 eg: hello 
        private static final Text valueInfo = new Text("1");// 存储词频,初始化为1  

        @Override  
        protected void map(LongWritable key, Text value, Context context)  
                throws IOException, InterruptedException {  
                        
            String line = value.toString(); 
            
            //去除标点token操作
            line = line.replaceAll("[^a-zA-Z0-9]", " ");
            line = line.replaceAll("\\s{2,}", " ").trim();
            line = line.toLowerCase();
            String[] fields = line.split(" ");// 得到字段数组  

            FileSplit fileSplit = (FileSplit) context.getInputSplit();// 得到这行数据所在的文件切片  
            String fileName = fileSplit.getPath().getName();// 根据文件切片得到文件名  

            for (String field : fields) {
                if(field!=null){
                    // key值由单词和URL组成,如“MapReduce:file1”  
                    keyInfo.set(field + "," + fileName);  
                    context.write(keyInfo, valueInfo);  
                }               
            }  
        }  

    }

2、Combine设计

通过一个Reduce过程无法同时完成词频统计和生成文档列表,所以必须增加一个Combine过程完成词频统计
输入:
<key,valuelist<1...>> -----eg:<word+’,’+filename, <1,1,1>>

处理过程:
<根据特殊的字符将key进行拆分,将key设置为单词,并统计词频信息,将value list中的每个1相加,将文档名和词频信息组合成新的value输出,同样用特殊的字符分隔>

输出:
<newKey,newValue> --------eg:<word, filename+’,’+countNumber>

//倒排索引combiner类
    public static class InvertedIndexCombiner extends Reducer<Text, Text, Text, Text>{

        private static Text info = new Text();  

        // 输入: <MapReduce:file3 {1,1,...}>  
        // 输出:<MapReduce file3:2>  
        @Override  //伪代码,表示重写 系统可以帮你检查正确性
        protected void reduce(Text key, Iterable<Text> values, Context context)  
                throws IOException, InterruptedException {  
            int sum = 0;// 统计词频  
            for (Text value : values) {  
                sum += Integer.parseInt(value.toString());  
            }  
            
            int splitIndex = key.toString().indexOf(",");  
            // 重新设置 value 值由 URL 和词频组成  
            info.set(key.toString().substring(splitIndex + 1) + "," + sum);  
            // 重新设置 key 值为单词  
            key.set(key.toString().substring(0, splitIndex));  

            context.write(key, info);  
        }  
    }

key-value经过map和combine后的变化示例:

image.png

3、停用词处理设计

设计字符串使用config.set()进行传递,在程序map-reduce工作前设计方法,得到停用词列表存储入string sword中,而后在reducer中重载setup函数,config.get()函数取出停用词:


image.png
public static String catStopWords(Configuration conf, String remoteFilePath) {
        Path remotePath = new Path(remoteFilePath);
        //String Swords[] = new String[100];
        //ArrayList<String> strArray = new ArrayList<String> ();
        String sword = "";
        try (FileSystem fs = FileSystem.get(conf);
            FSDataInputStream in = fs.open(remotePath);
            BufferedReader d = new BufferedReader(new InputStreamReader(in));) {
            String line;
            while ((line = d.readLine()) != null) {
                
                line = line.replaceAll("[^a-zA-Z0-9]", "");
                if(line!=null)
                    sword+=line+",";
                    //strArray.add(line);                
            }
        } catch (IOException e) {
            e.printStackTrace();
        }            
        return sword;
    }

4、reducer设计:

经过combiner之后,Reduce过程只需将相同key值的value值组合成倒排索引文件所需的格式即可,利用value中的词频信息,分割相加得到total的词频数,注意对于传入的key值,如果在停用词列表中出现,则不将其输出写入context中,剩下的事情就可以直接交给MapReduce框架进行处理了。

示例输出:


image.png
//倒排索引reducer类
    public static class InvertedIndexReducer extends Reducer<Text, Text, Text, Text>{
        private static Text result = new Text();  
        private static String[] fields;
        @Override
        protected void setup(Context context)
                throws IOException, InterruptedException {
            try {
                //从全局配置获取配置参数
                Configuration conf = context.getConfiguration();
                String Str = conf.get("swords"); //这样就拿到了
                fields = Str.split(",");// 得到字段数组
                            
            } catch (Exception e) {                
                e.printStackTrace();
            }
            
        }
        // 输入:<MapReduce file3,2>  
        // 输出:<MapReduce file1,1;file2,1;file3,2;>  
        
        @Override  
        protected void reduce(Text key, Iterable<Text> values, Context context)  
                throws IOException, InterruptedException {  
            
            // 生成文档列表  
            String fileList = new String();  
            int totalNum = 0;
            for (Text value : values) { 
                String va = value.toString();
                int index = va.indexOf(',');
                String subva = va.substring(index+1);
                int num = Integer.valueOf(subva);
                totalNum += num;
                fileList += "<" + va + ">;";  
            }  
            fileList += "<total," + String.valueOf(totalNum)+">.";
            result.set(fileList);  
            //去除停用词
            String k = key.toString();
            k = k.replaceAll("[^a-z0-9]", "");                      
            if(k!=null){
                boolean tag = true;
                for(String tmp:fields){
                    //System.out.println(tmp);
                    if(tmp.equals(k)){  
                        tag = false;
                        break;
                    }
                }               
                if(tag){
                    context.write(key, result);
                }
            }             
        }  
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容