基于map-reduce的TopK词频统计

本文介绍了一种使用MapReduce框架统计大规模数据集中关键词频率的方法,通过Mapper和Reducer两阶段处理,实现了高效查找出现频率最高的30个关键词。Mapper阶段利用HashMap存储关键词及其频次,Reducer阶段则采用小顶堆策略,确保了最终输出的是访问频次最大的Top30关键词。

查询所有记录中搜索频次最高的30个关键词。

主要分两个步骤,首先多个mapper分别处理所有数据中的一部分关键词数据,然后汇总到reducer做词频统计。

CountWordMapper

在Mapper中处理每一小块数据,使用HashMap存储关键字及其频次,可以节省时间,key为查询的关键字。Mapper返回一个<Text , LongWritable>的列表,存储当前文件块中的关键字及其频次,传给reducer作统计。

CountWordReducer

Reducer将所有mapper得到的关键字及频次汇总,不同mapper下的相同关键字在此合并,可得到当前关键字的总频次。为了得到TopK数据,在reducer维护一个大小为K的小顶堆,每次得到一个关键词的搜索总频次即向堆中插入一个Pair<String, Long>,堆中元素排序自定义为关键词频次Long。当堆元素大于K时,将堆顶元素(即当前最小元素)删去。最后reducer可得到访问频次最大的TopK关键词。输出前将堆中元素按频次排序即可。

词频统计完整代码:

Main.java

public class Main {
    public static void main(String[] args) throws Exception {
        countWords();    //统计词频前30的搜索关键词
        countUrls();   //被访问次数前10的网址及其次数占比
    }

    private static void countWords() throws Exception {
        String input_dir = "./data/sogou.full.utf8";//input
        String outputDir = "./result/words";//output

        Configuration conf = new Configuration();
        FileSystem fs = FileSystem.get(conf);
        fs.deleteOnExit(new Path(outputDir));
        fs.close();

        Job job = new Job(conf, "CountWords");
        job.setMapperClass(CountWordMapper.class);
        job.setReducerClass(CountWordReducer.class);
       
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(LongWritable.class);

        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(LongWritable.class);

        job.setInputFormatClass(TextInputFormat.class);
        TextInputFormat.setInputPaths(job, new Path(input_dir));

        job.setOutputFormatClass(TextOutputFormat.class);
        TextOutputFormat.setOutputPath(job, new Path(outputDir));
        job.waitForCompletion(true);

    }

}

CountWordMapper.java

public class CountWordMapper extends Mapper<LongWritable, Text, Text, LongWritable> {
    //使用hash表存储关键词和该词的频次
    HashMap<String, Long> map = new HashMap<>();

    @Override
    public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {

        //分离各项数据,以‘\t’为间隔标志
        String fields[] = value.toString().split("\t");

        if (fields.length != 6) {
            return;
        }
        String keyWord = fields[2];

        long count=map.getOrDefault(keyWord,-1L);
        if (count==-1L)//判断该词是否已存在于hash表中
            map.put(keyWord,1L);//不存在,加入新词
        else
            map.replace(keyWord,count+1);//存在,词频加一
    }

    @Override
    protected void cleanup(Mapper<LongWritable, Text,  Text , LongWritable>.Context context) throws IOException, InterruptedException {
        //将当前文件块内关键词的频度输出给reducer
        for (String keyWord : map.keySet()) {
            context.write(new Text(keyWord), new LongWritable(map.get(keyWord)));
        }
    }
}

CountWordReducer.java

public class CountWordReducer extends Reducer<Text, LongWritable, Text, LongWritable> {
    public static int K = 30;//选出频次最大的K条关键词

    //小顶堆,容量K,用于快速删除词频最小的元素
    PriorityQueue<Pair<String, Long>> minHeap = new PriorityQueue<>((p1, p2) -> (int) (p1.getValue() - p2.getValue()));

    //每次传入的参数为key相同的values的集合
    public void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {
        long total = 0;
        for (LongWritable count : values) {
            //依次取出每个mapper统计的关键词key的频次,加起来
            total += count.get();
        }

        Pair<String, Long> tmp = new Pair<>(key.toString(), total);
        minHeap.add(tmp);//向小顶堆插入新的关键词词频
        if (minHeap.size() > K)//若小顶堆容量达到要求的上限
            minHeap.poll();//删除堆顶最小的元素,保持TopK
    }

    @Override
    protected void cleanup(Context context) throws IOException, InterruptedException {
        List<Pair<String, Long>> list = new ArrayList<>();
        //从小顶堆中取出数据,便于排序
        for (Pair<String, Long> p : minHeap)
            list.add(p);

        //对搜索词频前K个元素排序
        Collections.sort(list, ((p1, p2) -> (int) (p2.getValue() - p1.getValue())));

        //reducer的输出,按搜索词频排好序的TopK关键词
        for (Pair<String, Long> t : list)
            context.write(new Text(t.getKey()), new LongWritable(t.getValue()));
    }
}

 

MapReduce 框架下实现 FP-Growth 算法,主要是通过分布式计算将传统的 FP-Growth 算法进行并行化,以适应大规模数据集的处理需求。该方法利用 Hadoop 平台的分布式存储与计算能力,结合分而治之的思想,对数据进行分组、局部挖掘和全局合并,从而提升算法的效率和可扩展性。 ### 1. 数据预处理与支持度统计(第一阶段) 在第一阶段,使用第一个 MapReduce Job 对原始事务数据进行处理,统计每个项在整个数据集中的支持度。这个过程类似于词频统计,其中 Mapper 对每条事务中的项进行计数,而 Reducer 负责汇总所有 Mapper 的输出,计算每个项的全局支持度。低于最小支持度阈值的项将被过滤掉,从而减少后续处理的数据量。 ### 2. 数据重排与压缩(第二阶段) 在第二阶段,第二个 MapReduce Job 对事务数据进行重新排序。Mapper 根据上一阶段统计得到的项频率,将每条事务中的项按照频率降序排列,并去除非频繁项。这一过程可以显著压缩 FP 树的规模,提高挖掘效率。Reducer 通常只负责将数据传递到下一个阶段,也可以用于进一步优化数据格式。 ### 3. 构建本地 FP 树与局部挖掘(第三阶段) 第三个 MapReduce Job 是整个流程中最关键的部分。在 Mapper 阶段,每个 Mapper 节点会根据分组策略构建本地的 FP 树(Local FP-Tree),并对该分组的数据进行局部频繁模式挖掘。这些本地 FP 树是原始 FP 树的子集,它们在各个节点上独立构建和挖掘,从而实现并行处理。每个 Mapper 输出的局部频繁模式将作为 Reducer 的输入。 ### 4. 汇总与全局频繁模式生成(第四阶段) 在 Reducer 阶段,将来自各个 Mapper 的局部频繁模式进行汇总和合并。为了保证结果的完整性,Reducer 需要对所有局部结果进行整合,并根据全局频率判断哪些模式是真正的频繁模式。在此过程中,还可以使用堆结构来维护每个项目的 top-K 频繁模式,从而优化输出结果的排序和筛选。 ### 5. 优化与负载均衡策略 为了提升算法的性能,可以采用多种优化策略。例如,在数据分组时引入负载均衡机制,使得各个节点的数据量相对均衡,避免某些节点成为瓶颈;在构建 FP 树时引入压缩技术,减少内存占用;在 Reducer 中使用堆结构来高效维护 top-K 频繁模式等。 ### 示例代码片段(Java 实现核心逻辑): ```java // Mapper 阶段示例(统计项频率) public class FrequencyCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> { private final static IntWritable one = new IntWritable(1); private Text item = new Text(); public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String[] items = value.toString().split(" "); for (String itemStr : items) { item.set(itemStr); context.write(item, one); } } } ``` ```java // Reducer 阶段示例(汇总项频率) public class FrequencyCountReducer 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); } } ``` 上述代码仅为统计项频率的 MapReduce 实现示例,实际的 FP-Growth 并行化过程还包括事务重排、FP 树构建与挖掘等步骤,需结合 FPTree 类和 TreeNode 类等自定义结构来实现
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值