數據集的解決方案會是什麼樣子這樣的:
case class orig(city: String, product: String, Jan: Int, Feb: Int, Mar: Int)
case class newOne(city: String, product: String, metric_type: String, metric_value: Int)
val df = Seq(("c1", "p1", 123, 22, 34), ("c2", "p2", 234, 432, 43)).toDF("city", "product", "Jan", "Feb", "Mar")
val newDf = df.as[orig].flatMap(v => Seq(newOne(v.city, v.product, "Jan", v.Jan), newOne(v.city, v.product, "Feb", v.Feb), newOne(v.city, v.product, "Mar", v.Mar)))
newDf.show()
>>+----+-------+-----------+-----------+
>>|city|product|metric_type|metric_value|
>>+----+-------+-----------+-----------+
>>| c1| p1| Jan| 123|
>>| c1| p1| Feb| 22|
>>| c1| p1| Mar| 34|
>>| c2| p2| Jan| 234|
>>| c2| p2| Feb| 432|
>>| c2| p2| Mar| 43|
>>+----+-------+-----------+-----------+
使用數據幀API
儘管OP專門詢問沒有spark sql的數據集,但對於查看此問題的其他人,我認爲應該使用數據幀解決方案。
首先,瞭解數據集API是Spark API的一部分非常重要。數據集和數據框是可互換的,實際上數據框只是一個DataSet [Row]。雖然數據集既有「鍵入」也有「無類型」的API,但忽略某些API似乎對我來說是錯誤的。
二,純「打字」選項有侷限性。例如,如果我們有100個月而不是3個月,那麼以上述方式來做就不切實際了。
最後,Spark在使用類型化API時(因爲類型化API對Spark不透明)提供了很多對數據框的優化,因此在很多情況下性能會變差。
我會建議使用下面的數據幀的解決方案:
val df = Seq(("c1", "p1", 123, 22, 34), ("c2", "p2", 234, 432, 43)).toDF("city", "product", "Jan", "Feb", "Mar")
val months = Seq("Jan", "Feb", "Mar")
val arrayedDF = df.withColumn("combined", array(months.head, months.tail: _*))_*)).select("city", "product", "combined")
val explodedDF = arrayedDF.selectExpr("city", "product", "posexplode(combined) as (pos, metricValue)")
val u = udf((p: Int) => months(p))
val targetDF = explodedDF.withColumn("metric_type", u($"pos")).drop("pos")
targetDF.show()
>>+----+-------+-----------+-----------+
>>|city|product|metricValue|metric_type|
>>+----+-------+-----------+-----------+
>>| c1| p1| 123| Jan|
>>| c1| p1| 22| Feb|
>>| c1| p1| 34| Mar|
>>| c2| p2| 234| Jan|
>>| c2| p2| 432| Feb|
>>| c2| p2| 43| Mar|
>>+----+-------+-----------+-----------+
雖然這是長一點,它處理更一般的情況下。
問題是什麼? –
嗨Assaf,將第一個數據框轉換爲第二個數據框,而不使用spark sql – prakash
你是什麼意思,而不使用spark sql? –