任务需求:已知RDD[(query:String, item_id:String, imp:Int, clk:Int)],要求找到每个query对应的点击最多的前2个item_id,即:按照query分组,并按照clk降序排序,每组取前两个。
例如:
(连衣裙,1234, 22, 13)
(牛仔裤,2768, 34, 7)
(连衣裙,1673,45, 9)
(衬衣,3468, 67, 12)
(牛仔裤,2754, 68, 20)
(连衣裙,1976,93, 29)
希望得到:
(连衣裙,1976,93, 29)
(连衣裙,1234, 22, 13)
(牛仔裤,2754, 68, 20)
(牛仔裤,2768, 34, 7)
(衬衣,3468, 67, 12)
先看一个错误的版本:
val topItem_set= data_set.map(ele => (ele._1, (ele._2, ele._3, ele._4))).groupByKey()
.map(line => {
val topItem = line._2.toArray.sortBy(_._3)(Ordering[Int].reverse).
take(2)
(line._1, topItem(0))
})
我们把query作为key,其余放到一起,groupByKey后(map之前),类型为:RDD[(String, Iterable[(String, Int, Int)])],根据query分组再map,line._2.toArray把Iterable转为Array,sortBy(_._3)是按最后一个Int即clk排序,(Ordering[Int].reverse)表示从大到小(sortBy默认从小到大,注意这里的sortBy是Array的成员函数而不是rdd的sortBy,用法比较不同),take(2)是取前2个,然后返回(query, item_id)。跑一下上面的过程。
你会发现,返回结果只有3项:
(连衣裙,1976,93, 29)
(牛仔裤,2754, 68, 20)
(衬衣,3468, 67, 12)
为什么呢?因为这里要用flatMap而不是Map:
val topItem_set= data_set.map(ele => (ele._1, (ele._2, ele._3, ele._4))).groupByKey()
.flatMap(line => {
val topItem = line._2.toArray.sortBy(_._3)(Ordering[Int].reverse).
take(2)
topItem.map(line._1,_.1)
})
为什么呢?GroupByKey后,类型为RDD[(String, Iterable[(String, Int, Int)])],如果用map,那每一个key对应的一个Iterable变量,相当于一条数据,map后的结果自然还是一条。但flatMap,相当于map+flat操作,这才是我们真正的需要的形式。