विंडो फ़ंक्शन :
कुछ इस तरह करना चाहिए ट्रिक:
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|
// +----+--------+----------+
महत्वपूर्ण डेटा तिरछा होने की स्थिति में यह विधि अक्षम होगी।
सादे SQL एकत्रीकरण के बादjoin
:
वैकल्पिक रूप से आप कुल डेटा फ्रेम के साथ जुड़ सकते हैं:
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|
// +----+--------+----------+
यह डुप्लिकेट मान रखेगा (यदि समान मान के साथ प्रति घंटे एक से अधिक श्रेणी है)। आप इन्हें निम्नानुसार हटा सकते हैं:
dfTopByJoin
.groupBy($"hour")
.agg(
first("category").alias("category"),
first("TotalValue").alias("TotalValue"))
आदेश का उपयोग करनाstructs
:
नीट, हालांकि बहुत अच्छी तरह से परीक्षण नहीं किया गया है, ट्रिक जिसमें जुड़ने या खिड़की के कार्यों की आवश्यकता नहीं है:
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|
// +----+--------+----------+
डेटासेट API (स्पार्क 1.6+, 2.0+) के साथ:
स्पार्क 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]|
// +---+--------------+
स्पार्क 2.0 या बाद में :
df.as[Record]
.groupByKey(_.Hour)
.reduceGroups((x, y) => if (x.TotalValue > y.TotalValue) x else y)
अंतिम दो विधियाँ मैप साइड संयोजन का लाभ उठा सकती हैं और पूर्ण फेरबदल की आवश्यकता नहीं होती है, इसलिए अधिकांश समय विंडो फ़ंक्शन और जॉइन की तुलना में बेहतर प्रदर्शन का प्रदर्शन करना चाहिए। इन बेंत का उपयोग completed
आउटपुट मोड में स्ट्रक्चर्ड स्ट्रीमिंग के साथ भी किया जा सकता है।
उपयोग न करें :
df.orderBy(...).groupBy(...).agg(first(...), ...)
यह काम (में विशेष रूप से लग सकता है local
मोड), लेकिन यह अविश्वसनीय है (देखें चिंगारी से 16,207 करने के लिए, क्रेडिट Tzach ज़ोहर के लिए प्रासंगिक JIRA मुद्दे को जोड़ने , और चिंगारी से 30,335 )।
एक ही नोट पर लागू होता है
df.orderBy(...).dropDuplicates(...)
जो आंतरिक रूप से समतुल्य निष्पादन योजना का उपयोग करता है।