map根据key排序_Spark实现二次排序

何为二次排序?

二次排序就是对于<key,value>类型的数据,不但按key排序,而且每个Key对应的value也是有序的。比如说,我们有这样的销售数据(年,月,销售额),我们希望在显示的时候,不但按年月升序或降序排列,而且还希望对于相同年月的销售额,也排序显示。

例如,假设我们有以下输入文件data.txt(逗号分割的分别是“年,月,总数”):

    2018,5,22
    2019,1,24
    2018,2,128
    2019,3,56
    2019,1,3
    2019,2,-43
    2019,4,5
    2019,3,46
    2018,2,64
    2019,1,4
    2019,1,21
    2019,2,35
    2019,2,0

我们期望的输出如下的结果:

    2018-2  64,128
    2018-5  22
    2019-1  3,4,21,24
    2019-2  -43,0,35
    2019-3  46,56
    2019-4  5

Spark 二次排序解决思路

我们知道,Spark(包括Hadoop、Flink等)的算子都提供有默认按key进行排序的功能。所以我们只需要将年和月组合起来构成一个Key,将第三列作为value,并使用 groupByKey 函数将同一个Key的所有Value全部分组到一起,然后对同一个Key的所有Value再进行排序即可。

使用Spark RDD实现二次排序

// 加载数据集
val inputPath = "file:///home/hduser/data/spark/data.txt"
val inputRDD = sc.textFile(inputPath)

// 实现二次排序
val sortedRDD = inputRDD
                  .map(line => {
                    val arr = line.split(",")
                    val key = arr(0) + "-" + arr(1)
                    val value = arr(2)
                    (key,value)
                  })
                  .groupByKey()
                  .map(t => (t._1, t._2.toList.sortWith(_.toInt < _.toInt).mkString(",")))
                  .sortByKey(true)          // true:升序,false:降序

// 结果输出
sortedRDD.collect.foreach(t => println(t._1 + "t" + t._2))

执行上述代码。输出结果如下:

2018-2	64,128
2018-5	22
2019-1	3,4,21,24
2019-2	-43,0,35
2019-3	46,56
2019-4	5

执行过程截图:

1888fb9a6232dd5ac05deb9048dc8254.png

最后的显示,当然也可以转换为DataFrame。如下:

// 加载数据集
val inputPath = "file:///home/hduser/data/spark_demo/data.txt"
val inputRDD = sc.textFile(inputPath)

// 实现二次排序
val sortedRDD = inputRDD
                  .map(line => {
                    val arr = line.split(",")
                    val key = arr(0) + "-" + arr(1)
                    val value = arr(2)
                    (key,value)
                  })
                  .groupByKey()
                  .map(t => (t._1, t._2.toList.sortWith(_.toInt < _.toInt).mkString(",")))
                  .sortByKey(true)          // true:升序,false:降序

// 转换为DataFrame
val sortedDF = sortedRDD.toDF("key", "value")

// 显示
sortedDF.show

执行上述代码。输出结果如下:

+------+---------+
|   key|    value|
+------+---------+
|2018-2|   64,128|
|2018-5|       22|
|2019-1|3,4,21,24|
|2019-2| -43,0,35|
|2019-3|    46,56|
|2019-4|        5|
+------+---------+

执行过程截图:

10cd0ddeca4197a8a013a32289a23fd2.png

使用Spark DataFrame实现二次排序

1、加载数据集。

// 加载数据集
val inputPath = "file:///home/hduser/data/spark_demo/data.txt"
val inputDF = spark.read 
                   .option("inferSchema","true")
                   .option("header","false")
                   .csv(inputPath)
                   .toDF("year","month","cnt")

inputDF.show

查看结果:

+----+-----+---+
|year|month|cnt|
+----+-----+---+
|2018|    5| 22|
|2019|    1| 24|
|2018|    2|128|
|2019|    3| 56|
|2019|    1|  3|
|2019|    2|-43|
|2019|    4|  5|
|2019|    3| 46|
|2018|    2| 64|
|2019|    1|  4|
|2019|    1| 21|
|2019|    2| 35|
|2019|    2|  0|
+----+-----+---+

2、 查看schema。注意,因为使用了类型推断,所以 cnt 列的数据类型推断为整数类型。

df2.printSchema

查看结果:

root
 |-- ym: string (nullable = false)
 |-- cnt: integer (nullable = true)

3、组合year和month为一列,并取别名"ym"。

import org.apache.spark.sql.functions._

val df2 = inputDF.select(concat_ws("-",$"year",$"month").as("ym"),$"cnt")
df2.show

查看结果:

+------+---+
|    ym|cnt|
+------+---+
|2018-5| 22|
|2019-1| 24|
|2018-2|128|
|2019-3| 56|
|2019-1|  3|
|2019-2|-43|
|2019-4|  5|
|2019-3| 46|
|2018-2| 64|
|2019-1|  4|
|2019-1| 21|
|2019-2| 35|
|2019-2|  0|
+------+---+

4、对每一组的cnt列进行排序,然后对ym排序,并输出

df2.groupBy("ym")
   .agg(sort_array(collect_list("cnt")).as("cnt"))
   .orderBy("ym")
   .show

查看结果:

+------+--------------+
|    ym|           cnt|
+------+--------------+
|2018-2|     [64, 128]|
|2018-5|          [22]|
|2019-1|[3, 4, 21, 24]|
|2019-2|  [-43, 0, 35]|
|2019-3|      [46, 56]|
|2019-4|           [5]|
+------+--------------+

5、最后,我们可以把上面的代码写到一步当中。

b251ce2f1975a172db433375f81fd735.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值