Các chức năng của cửa sổ :
Một cái gì đó như thế này nên thực hiện các mẹo:
import org.apache.spark.sql.functions.{row_number, max, broadcast}
import org.apache.spark.sql.expressions.Window
val df = sc.parallelize(Seq(
(0,"cat26",30.9), (0,"cat13",22.1), (0,"cat95",19.6), (0,"cat105",1.3),
(1,"cat67",28.5), (1,"cat4",26.8), (1,"cat13",12.6), (1,"cat23",5.3),
(2,"cat56",39.6), (2,"cat40",29.7), (2,"cat187",27.9), (2,"cat68",9.8),
(3,"cat8",35.6))).toDF("Hour", "Category", "TotalValue")
val w = Window.partitionBy($"hour").orderBy($"TotalValue".desc)
val dfTop = df.withColumn("rn", row_number.over(w)).where($"rn" === 1).drop("rn")
dfTop.show
// +----+--------+----------+
// |Hour|Category|TotalValue|
// +----+--------+----------+
// | 0| cat26| 30.9|
// | 1| cat67| 28.5|
// | 2| cat56| 39.6|
// | 3| cat8| 35.6|
// +----+--------+----------+
Phương pháp này sẽ không hiệu quả trong trường hợp sai lệch dữ liệu quan trọng.
Tập hợp SQL đơn giản theo saujoin
:
Ngoài ra, bạn có thể tham gia với khung dữ liệu tổng hợp:
val dfMax = df.groupBy($"hour".as("max_hour")).agg(max($"TotalValue").as("max_value"))
val dfTopByJoin = df.join(broadcast(dfMax),
($"hour" === $"max_hour") && ($"TotalValue" === $"max_value"))
.drop("max_hour")
.drop("max_value")
dfTopByJoin.show
// +----+--------+----------+
// |Hour|Category|TotalValue|
// +----+--------+----------+
// | 0| cat26| 30.9|
// | 1| cat67| 28.5|
// | 2| cat56| 39.6|
// | 3| cat8| 35.6|
// +----+--------+----------+
Nó sẽ giữ các giá trị trùng lặp (nếu có nhiều hơn một danh mục mỗi giờ với cùng một giá trị). Bạn có thể loại bỏ chúng như sau:
dfTopByJoin
.groupBy($"hour")
.agg(
first("category").alias("category"),
first("TotalValue").alias("TotalValue"))
Sử dụng đặt hàng quastructs
:
Mặc dù gọn gàng, nhưng không được kiểm tra kỹ, thủ thuật không yêu cầu tham gia hoặc chức năng cửa sổ:
val dfTop = df.select($"Hour", struct($"TotalValue", $"Category").alias("vs"))
.groupBy($"hour")
.agg(max("vs").alias("vs"))
.select($"Hour", $"vs.Category", $"vs.TotalValue")
dfTop.show
// +----+--------+----------+
// |Hour|Category|TotalValue|
// +----+--------+----------+
// | 0| cat26| 30.9|
// | 1| cat67| 28.5|
// | 2| cat56| 39.6|
// | 3| cat8| 35.6|
// +----+--------+----------+
Với API dữ liệu (Spark 1.6+, 2.0+):
Tia lửa 1.6 :
case class Record(Hour: Integer, Category: String, TotalValue: Double)
df.as[Record]
.groupBy($"hour")
.reduce((x, y) => if (x.TotalValue > y.TotalValue) x else y)
.show
// +---+--------------+
// | _1| _2|
// +---+--------------+
// |[0]|[0,cat26,30.9]|
// |[1]|[1,cat67,28.5]|
// |[2]|[2,cat56,39.6]|
// |[3]| [3,cat8,35.6]|
// +---+--------------+
Spark 2.0 trở lên :
df.as[Record]
.groupByKey(_.Hour)
.reduceGroups((x, y) => if (x.TotalValue > y.TotalValue) x else y)
Hai phương pháp cuối cùng có thể tận dụng kết hợp phía bản đồ và không yêu cầu xáo trộn hoàn toàn, do đó, phần lớn thời gian sẽ thể hiện hiệu suất tốt hơn so với các chức năng cửa sổ và tham gia. Những gậy này cũng được sử dụng với Structured Streaming ở completed
chế độ đầu ra.
Đừng sử dụng :
df.orderBy(...).groupBy(...).agg(first(...), ...)
Nó có vẻ hoạt động (đặc biệt là trong local
chế độ) nhưng không đáng tin cậy (xem SPARK-16207 , tín dụng cho Tzach Zohar để liên kết vấn đề JIRA có liên quan và SPARK-30335 ).
Lưu ý tương tự áp dụng cho
df.orderBy(...).dropDuplicates(...)
trong đó sử dụng kế hoạch thực hiện tương đương.