Spark SQL查找组中最常见的项目

时间:2016-12-19 22:34:08

标签: apache-spark

如果我有一组不同的项目,如下所示

case class Order(orderId: String, orderDetails: OrderDetail, destination: String)
case class OrderDetail(date: Timestamp, recipient: String, item: String)

grouped = ordersDF.groupby($"destination")

我正在寻找的是一种找到每个目的地最常见项目的方法查看可用于聚合的sql函数,我没有在数据上看到子groupBys的内容。将数据转换为RDD可以起作用,但我的理解是它不是最好的实践。

我希望看到像

这样的内容
|Destination | mostCommon |
----------------------------   
|XYZ         |item x      |

1 个答案:

答案 0 :(得分:3)

您可以使用groupBy / aggregate函数和窗口函数的组合来实现此目的。

我们认为这是ordersDf:

+-------+--------------+-----------+
|orderId|  orderDetails|destination|
+-------+--------------+-----------+
|      1|[11,abc,item1]|       loc1|
|      2|[12,abc,item2]|       loc1|
|      3|[13,abc,item1]|       loc1|
|      4|[14,abc,item1]|       loc2|
|      5|[15,abc,item2]|       loc2|
|      6|[11,abc,item2]|       loc2|
|      7|[11,abc,item2]|       loc2|
+-------+--------------+-----------+

首先,按目的地和项目对数据进行分组,并计算每个项目的频率。

val dfWithCount = ordersDf
.groupBy("destination","orderDetails.item")
.agg(count("orderDetails.item").alias("itemCount"))

汇总的数据框看起来像这样

+-----------+-----+---------+
|destination| item|itemCount|
+-----------+-----+---------+
|       loc1|item2|        1|
|       loc2|item1|        1|
|       loc2|item2|        3|
|       loc1|item1|        2|
+-----------+-----+---------+

由于我们希望找出每个位置最常见的项目,让我们按目的地进行分区,并在itemCount列上应用最大聚合。

val maxWindowSpec = Window.partitionBy("destination")
val maxColumn = max($"itemCount").over(maxWindowSpec)
val dfWithMax = dfWithCount.withColumn("maxItemCount",maxColumn)

结果数据框包含每个目的地的itemCounts和maxCount项目

+-----------+-----+---------+------------+
|destination| item|itemCount|maxItemCount|
+-----------+-----+---------+------------+
|       loc1|item2|        1|           2|
|       loc1|item1|        2|           2|
|       loc2|item1|        1|           3|
|       loc2|item2|        3|           3|
+-----------+-----+---------+------------+

最后,我们过滤出给定(目的地,项目)组合的itemCount不是该目的地的最大项目数的行。

val result = dfWithMax
.filter("maxItemCount - itemCount == 0")
.drop("maxItemCount","itemCount")

result.show()

+-----------+-----+
|destination| item|
+-----------+-----+
|       loc1|item1|
|       loc2|item2|
+-----------+-----+