Pencere fonksiyonları :
Böyle bir şey hile yapmalıdır:
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|
// +----+--------+----------+
Önemli veri eğriliği durumunda bu yöntem verimsiz olacaktır.
Sade SQL toplama ve ardındanjoin
:
Alternatif olarak, birleştirilmiş veri çerçevesine katılabilirsiniz:
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|
// +----+--------+----------+
Yinelenen değerleri korur (aynı toplam değere sahip saatte birden fazla kategori varsa). Bunları aşağıdaki gibi kaldırabilirsiniz:
dfTopByJoin
.groupBy($"hour")
.agg(
first("category").alias("category"),
first("TotalValue").alias("TotalValue"))
Üzerinde sipariş kullanmastructs
:
Çok iyi test edilmemesine rağmen, birleştirme veya pencere fonksiyonları gerektirmeyen düzgün numaralar:
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|
// +----+--------+----------+
DataSet API'sı ile (Spark 1.6+, 2.0+):
Kıvılcım 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 veya üstü :
df.as[Record]
.groupByKey(_.Hour)
.reduceGroups((x, y) => if (x.TotalValue > y.TotalValue) x else y)
Son iki yöntem, harita tarafı birleştirmeden yararlanabilir ve tam karıştırma gerektirmez, bu nedenle çoğu zaman pencere işlevleri ve birleşimlerine kıyasla daha iyi bir performans göstermelidir. Bunlar, completed
çıkış modunda Yapısal Akış ile de kullanılabilir .
Kullanmayın :
df.orderBy(...).groupBy(...).agg(first(...), ...)
O (özellikle iş gibi görünebilir local
modda) ama güvenilmez (bkz SPARK-16207 için, kredi Tzach Zohar için ilgili JIRA sorunu bağlayan ve SPARK-30335 ).
Aynı not aşağıdakiler için de geçerlidir
df.orderBy(...).dropDuplicates(...)
dahili olarak eşdeğer yürütme planı kullanır.