如何从Spark DataFrame按校班分组提取各班前3高分?
解决方案:用Spark窗口函数实现每个班级前3高分筛选
嘿,这个场景用Spark的窗口函数就完美解决了——直接groupBy确实搞不定取Top N的需求,因为groupBy是做聚合计算的,而我们需要保留每个学生的明细行,同时筛选出每个学校+班级组内的前几名。给你一步步拆解实现:
步骤1:导入必要的包
首先得导入Spark窗口函数和内置函数的包,不然没法用相关API:
import org.apache.spark.sql.expressions.Window import org.apache.spark.sql.functions._
步骤2:定义窗口规范
我们需要按school_name和class_name做分区(相当于逻辑上的分组),然后在每个分区内按score降序排序,这样分数高的排在前面:
val windowSpec = Window.partitionBy("school_name", "class_name").orderBy(desc("score"))
步骤3:添加排名列并筛选前3
接下来给每行数据添加上在所属组内的排名,然后过滤出排名≤3的行即可。这里有几个排名函数可选,你可以根据需求选:
row_number():即使分数相同,也会给不同行分配唯一的排名(比如两个100分,一个排第1,一个排第2)rank():同分同排名,后续排名会跳过(比如两个100分都是第1,下一个分数排第3)dense_rank():同分同排名,后续排名不会跳过(比如两个100分都是第1,下一个分数排第2)
这里用最常用的row_number()举例,完整代码结合你已有的读取逻辑:
val df = spark.read.format("csv") .option("sep", ",") .option("inferSchema", "true") .option("header", "true") .load("students.csv") // 定义窗口规范 val windowSpec = Window.partitionBy("school_name", "class_name").orderBy(desc("score")) // 添加排名列 val rankedDF = df.withColumn("rank", row_number().over(windowSpec)) // 筛选前3名,可选去掉排名列 val top3DF = rankedDF.filter(col("rank") <= 3).drop("rank") // 查看结果 top3DF.show()
补充说明
- 如果需要保留排名信息,去掉
.drop("rank")即可 - 如果你的数据里有大量重复分数,且希望同分的学生都能进入前3(比如某班级有4个100分,都想保留),可以换成
dense_rank()或rank(),根据你对排名的定义调整
内容的提问来源于stack exchange,提问作者Bomin
相关产品推荐
相关产品推荐

