问题:

有如下数据文件 city.txt (id, city, value)

cat city.txt 
1 wh 500
2 bj 600
3 wh 100
4 sh 400
5 wh 200
6 bj 100
7 sh 200
8 bj 300
9 sh 900
需要按 city 分组聚合,然后从每组数据中取出前两条value最大的记录。

1、这是实际业务中经常会遇到的 group TopK 问题,下面来看看 pig 如何解决:

1 a = load '/data/city.txt'  using PigStorage(' 'as (id:chararray, city:chararray, value:int);
2 b = group by city;
3 c = foreach b {c1=order by value desc; c2=limit c1 2; generate group,c2.value;};
4 d = stream c through `sed 's/[(){}]//g'`;
5 dump d;

结果:

1 (bj,600,300)
2 (sh,900,400)
3 (wh,500,200)

这几行代码其实也实现了mysql中的 group_concat 函数的功能:

1 a = load '/data/city.txt'  using PigStorage(' 'as (id:chararray, city:chararray, value:int);
2 b = group by city;
3 c = foreach b {c1=order by value desc;  generate group,c1.value;};
4 d = stream c through `sed 's/[(){}]//g'`;
5 dump d;

结果:

1 (bj,600,300,100)
2 (sh,900,400,200)
3 (wh,500,200,100)

2、下面我们再来看看hive如何处理group topk的问题:

本质上HSQL和sql有很多相同的地方,但HSQL目前功能还有很多缺失,至少不如原生态的SQL功能强大,

比起PIG也有些差距,如果SQL中这类分组topk的问题如何解决呢?

1 select from city a where
2 2>(select count(1) from city where cname=a.cname and value>a.value)
3 distribute by a.cname sort by a.cname,a.value desc;

http://my.oschina.net/leejun2005/blog/78904

但是这种写法在HQL中直接报语法错误了,下面我们只能用hive udf的思路来解决了:

排序city和value,然后对city计数,最后where过滤掉city列计数器大于k的行即可。

好了,上代码:

(1)定义UDF:

01 package com.example.hive.udf;
02 import org.apache.hadoop.hive.ql.exec.UDF;
03       
04 public final class Rank extends UDF{
05     private int  counter;
06     private String last_key;
07     public int evaluate(final String key){
08       if ( !key.equalsIgnoreCase(this.last_key) ) {
09          this.counter = 0;
10          this.last_key = key;
11       }
12       return this.counter++;
13     }
14 }

(2)注册jar、建表、导数据,查询:

1 add jar Rank.jar;
2 create temporary function rank as 'com.example.hive.udf.Rank';
3 create table city(id int,cname string,value int) row format delimited fields terminated by ' ';
4 LOAD DATA LOCAL INPATH 'city.txt' OVERWRITE INTO TABLE city;
5 select cname, value from (
6     select cname,rank(cname) csum,value from (
7         select id, cname, value from city distribute by cname sort by cname,value desc
8     )a
9 )b where csum < 2;

(3)结果:

1 bj  600
2 bj  300
3 sh  900
4 sh  400
5 wh  500
6 wh  200
可以看到,hive相比pig来说,处理起来稍微复杂了点,但随着hive的日渐完善,以后比pig更简洁也说不定。

REF:hive中分组取前N个值的实现

http://baiyunl.iteye.com/blog/1466343

3、最后我们来看一下原生态的MR:

01 import java.io.IOException;
02 import java.util.TreeSet;
03  
04 import org.apache.hadoop.conf.Configuration;
05 import org.apache.hadoop.fs.Path;
06 import org.apache.hadoop.io.IntWritable;
07 import org.apache.hadoop.io.LongWritable;
08 import org.apache.hadoop.io.Text;
09 import org.apache.hadoop.mapreduce.Job;
10 import org.apache.hadoop.mapreduce.Mapper;
11 import org.apache.hadoop.mapreduce.Reducer;
12 import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
13 import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
14 import org.apache.hadoop.util.GenericOptionsParser;
15  
16 public class GroupTopK {
17     // 这个 MR 将会取得每组年龄中 id 最大的前 3 个
18     // 测试数据由脚本生成:http://my.oschina.net/leejun2005/blog/76631
19     public static class GroupTopKMapper extends
20             Mapper<LongWritable, Text, IntWritable, LongWritable> {
21         IntWritable outKey = new IntWritable();
22         LongWritable outValue = new LongWritable();
23         String[] valArr = null;
24  
25         public void map(LongWritable key, Text value, Context context)
26                 throws IOException, InterruptedException {
27             valArr = value.toString().split("\t");
28             outKey.set(Integer.parseInt(valArr[2]));// age int
29             outValue.set(Long.parseLong(valArr[0]));// id long
30             context.write(outKey, outValue);
31         }
32     }
33  
34     public static class GroupTopKReducer extends
35             Reducer<IntWritable, LongWritable, IntWritable, LongWritable> {
36  
37         LongWritable outValue = new LongWritable();
38  
39         public void reduce(IntWritable key, Iterable<LongWritable> values,
40                 Context context) throws IOException, InterruptedException {
41             TreeSet<Long> idTreeSet = new TreeSet<Long>();
42             for (LongWritable val : values) {
43                 idTreeSet.add(val.get());
44                 if (idTreeSet.size() > 3) {
45                     idTreeSet.remove(idTreeSet.first());
46                 }
47             }
48             for (Long id : idTreeSet) {
49                 outValue.set(id);
50                 context.write(key, outValue);
51             }
52         }
53     }
54  
55     public static void main(String[] args) throws Exception {
56         Configuration conf = new Configuration();
57         String[] otherArgs = new GenericOptionsParser(conf, args)
58                 .getRemainingArgs();
59  
60         System.out.println(otherArgs.length);
61         System.out.println(otherArgs[0]);
62         System.out.println(otherArgs[1]);
63  
64         if (otherArgs.length != 3) {
65             System.err.println("Usage: GroupTopK <in> <out>");
66             System.exit(2);
67         }
68         Job job = new Job(conf, "GroupTopK");
69         job.setJarByClass(GroupTopK.class);
70         job.setMapperClass(GroupTopKMapper.class);
71         job.setReducerClass(GroupTopKReducer.class);
72         job.setNumReduceTasks(1);
73         job.setOutputKeyClass(IntWritable.class);
74         job.setOutputValueClass(LongWritable.class);
75         FileInputFormat.addInputPath(job, new Path(otherArgs[1]));
76         FileOutputFormat.setOutputPath(job, new Path(otherArgs[2]));
77         System.exit(job.waitForCompletion(true) ? 0 1);
78     }
79 }

hadoop jar GroupTopK.jar GroupTopK /tmp/decli/record_new.txt /tmp/1

结果:

hadoop fs -cat /tmp/1/part-r-00000
0       12869695
0       12869971
0       12869976
1       12869813
1       12869870
1       12869951

......

数据验证:

awk '$3==0{print $1}' record_new.txt|sort -nr|head -3
12869976
12869971
12869695

可以看到结果没有问题。

注:测试数据由以下脚本生成:

http://my.oschina.net/leejun2005/blog/76631

PS:

如果说hive类似sql的话,那pig就类似plsql存储过程了:程序编写更自由,逻辑能处理的更强大了。

pig中还能直接通过反射调用java的静态类中的方法,这块内容请参考之前的相关pig博文。

附几个HIVE UDAF链接,有兴趣的同学自己看下:

Hive UDAF和UDTF实现group by后获取top值 http://blog.csdn.net/liuzhoulong/article/details/7789183
hive中自定义函数(UDAF)实现多行字符串拼接为一行 http://blog.sina.com.cn/s/blog_6ff05a2c0100tjw4.html
编写Hive UDAF http://www.fuzhijie.me/?p=118
Hive UDAF开发 http://richiehu.blog.51cto.com/2093113/386113

Pig、Hive、MapReduce 解决分组 Top K 问题(转)的更多相关文章

  1. 如何解决海量数据的Top K问题

    1. 问题描述 在大规模数据处理中,常遇到的一类问题是,在海量数据中找出出现频率最高的前K个数,或者从海量数据中找出最大的前K个数,这类问题通常称为“top K”问题,如:在搜索引擎中,统计搜索最热门 ...

  2. 优先队列实现 大小根堆 解决top k 问题

      摘于:http://my.oschina.net/leejun2005/blog/135085 目录:[ - ] 1.认识 PriorityQueue 2.应用:求 Top K 大/小 的元素 3 ...

  3. get top k elements of the same key in hive

    key points: 1. group by key and sort by using distribute by and sort by. 2. get top k elements by a ...

  4. pig询问top k,每个返回hour和ad_network_id最大的两个记录(SUBSTRING,order,COUNT_STAR,limit)

    pig里面有一个TOP功能.我不知道为什么用不了.有时间去看看pig源代码. SET job.name 'top_k'; SET job.priority HIGH; --REGISTER piggy ...

  5. Top K问题的两种解决思路

    Top K问题在数据分析中非常普遍的一个问题(在面试中也经常被问到),比如: 从20亿个数字的文本中,找出最大的前100个. 解决Top K问题有两种思路, 最直观:小顶堆(大顶堆 -> 最小1 ...

  6. pig中查询top k,返回每个hour和ad_network_id下最大两个记录(SUBSTRING,order,COUNT_STAR,limit)

    pig里面是有TOP函数,不知道为什么用不了.有时间要去看看pig源码了. SET job.name 'top_k'; SET job.priority HIGH; --REGISTER piggyb ...

  7. 优先队列PriorityQueue实现 大小根堆 解决top k 问题

    转载:https://www.cnblogs.com/lifegoesonitself/p/3391741.html PriorityQueue是从JDK1.5开始提供的新的数据结构接口,它是一种基于 ...

  8. 海量数据处理 - 10亿个数中找出最大的10000个数(top K问题)

    前两天面试3面学长问我的这个问题(想说TEG的3个面试学长都是好和蔼,希望能完成最后一面,各方面原因造成我无比想去鹅场的心已经按捺不住了),这个问题还是建立最小堆比较好一些. 先拿10000个数建堆, ...

  9. pig hive 区别

    Pig是一种编程语言,它简化了Hadoop常见的工作任务.Pig可加载数据.表达转换数据以及存储最终结果.Pig内置的操作使得半结构化数据变得有意义(如日志文件).同时Pig可扩展使用Java中添加的 ...

随机推荐

  1. The Definitive Guide To Django 2 学习笔记(一) Views and UrL confsRL

    1.如何找到django在Ubuntu下的安装路径: 进入python命令行,import django,print(django.__path__) 2.使用django-admin.py 创建项目 ...

  2. nginx php-fpm安装手记

    首先下载nginx,nginx下载地址:http://www.nginx.org/download/nginx-0.8.53.tar.gz[root@winsyk ~]# mkdir -p /usr/ ...

  3. Enable Authentication on MongoDB

    1.Connect to the server using the mongo shell mongo mongodb://localhost:27017 2.Create the user admi ...

  4. <!>字体效果

    <h1>...</h1>标题字(最大) <h6>...</h6>标题字(最小) <b>...</b>粗体字 <strong ...

  5. mfc小工具开发之定时闹钟之---多线程急线程同步

    一.MFC对多线程编程的支持 MFC中有两类线程,分别称之为工作者线程和用户界面线程.二者的主要区别在于工作者线程没有消息循环,而用户界面线程有自己的消息队列和消息循环. 工作者线程没有消息机制,通常 ...

  6. 【BZOJ】3391: [Usaco2004 Dec]Tree Cutting网络破坏(dfs)

    http://www.lydsy.com/JudgeOnline/problem.php?id=3391 显然判断每个点只需要判断子树是否小于等于n/2即可 那么我们虚拟一个根,然后计算每个子树的si ...

  7. .net泛型通用函数的特殊问题的解决方法

    自从2.0版本的net framework推出之后泛型(Generic)得到了广泛好评.它不必像object类型一样性能上因为“拆箱”或者“装箱”得到损失,同时在编译语法检测阶段就可以实时检测出传入或 ...

  8. 维纳滤波和编码曝光PSF去除运动模糊【matlab】

    编码曝光知识 - ostartech - 博客园 https://www.cnblogs.com/wxl845235800/p/8276362.html %%%%%%%%%%%%%%%%%%%%%%% ...

  9. [转帖收集] Java注解

    1.Annotation 它的作用是修饰编程元素.什么是编程元素呢?例如:包.类.构造方法.方法.成员变量等.Annotation(注解)就是Java提供了一种元程序中的元素关联任何信息和任何元数据( ...

  10. JS-校验表单后提交表单的三种方法总结

    第一种: <script type="text/javascript"> function check(form) { if(form.userId.value=='' ...