簡體   English   中英

如何編寫數據集編碼器以支持將函數映射到 Scala Spark 中的 org.apache.spark.sql.Dataset[String]

[英]How do I write a Dataset encoder to support mapping a function to a org.apache.spark.sql.Dataset[String] in Scala Spark

從 Spark 1.6 遷移到 Spark 2.2* 帶來了錯誤“錯誤:無法找到存儲在“數據集”中的類型的編碼器。 原始類型(Int、String 等)”,當嘗試將方法應用於查詢 Parquet 表返回的數據集時。 我已經過度簡化了我的代碼來演示相同的錯誤。 代碼查詢鑲木地板文件以返回以下數據類型: 'org.apache.spark.sql.Dataset[org.apache.spark.sql.Row]' 我應用一個函數來提取字符串和整數,返回一個字符串。 返回以下數據類型: Array[String] 接下來,我需要執行需要單獨函數的大量操作。 在這個測試函數中,我嘗試附加一個產生與我的詳細示例相同的錯誤的字符串。 我嘗試了一些編碼器示例並使用了“案例”,但還沒有提出可行的解決方案。 任何建議/示例將不勝感激

scala> var d1 = hive.executeQuery(st)
d1: org.apache.spark.sql.Dataset[org.apache.spark.sql.Row] = [cvdt35_message_id_d: string, 
cvdt35_input_timestamp_s: decimal(16,5) ... 2 more fields]

val parseCVDP_parquet = (s:org.apache.spark.sql.Row) => s.getString(2).split("0x" 
(1)+","+s.getDecimal(1);

scala> var d2 =  d1.map(parseCVDP_parquet)
d2: org.apache.spark.sql.Dataset[String] = [value: string]

scala> d2.take(1)
20/03/25 19:01:08 WARN TaskSetManager: Stage 3 contains a task of very large size (131 KB). The 
maximum recommended task size is 100 KB.
res10: Array[String] = Array(ab04006000504304,1522194407.95162)

scala> def dd(s:String){
 | s + "some string"
 | }
dd: (s: String)Unit

scala> var d3 = d2.map{s=> dd(s) }
<console>:47: error: Unable to find encoder for type stored in a Dataset.  Primitive types (Int, 
String, etc) and Product types (case classes) are supported by importing spark.implicits._  Support 
for serializing other types will be added in future releases.

為了進一步提煉問題,我相信這種情況(盡管我還沒有嘗試過所有可能的解決方案)可以進一步簡化為以下代碼:

scala> var test = ( 1 to 3).map( _ => "just some words").toDS()
test: org.apache.spark.sql.Dataset[String] = [value: string]

scala> def f(s: String){
 | s + "hi"
 | }
f: (s: String)Unit

scala> var test2 = test.map{ s => f(s) }
<console>:42: error: Unable to find encoder for type stored in a Dataset.  
Primitive types (Int, String, etc) and Product types (case classes) are 
supported by importing spark.implicits._  Support for serializing other types 
will be added in future releases.
   var test2 = test.map{ s => f(s) }

我至少對我的簡化問題有一個解決方案(如下)。 我會測試更多......

scala> var test = ( 1 to 3).map( _ => "just some words").toDS()
test: org.apache.spark.sql.Dataset[String] = [value: string]

scala> def f(s: String): String = {
 | val r = s + "hi"
 | return r
 | }
f: (s: String)String

scala> var test2 = test.rdd.map{ s => f(s) }
test2: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[17] at map at <console>:43

scala> test2.take(1)
res9: Array[String] = Array(just some wordshi)

第一個解決方案不適用於我的初始(生產)數據集,而是產生錯誤“org.apache.spark.SparkException:Task not serializable”(有趣的是,盡管兩者都存儲為相同的數據類型(org.apache.spark.sql) .Dataset[String] = [value: string]) 我認為是相關的。我在我的測試數據集中包含了另一個解決方案,該解決方案消除了初始編碼器錯誤,如圖所示實際上適用於我的玩具問題,不會上升到生產數據集。對於為什么我的應用程序在從 1.6 到 2.3 版本的移動中被擱置有點困惑,因為多年來我不必對我的應用程序進行任何特殊調整,並且已成功運行它以進行最有可能的計算數以萬億計。其他探索包括將我的方法包裝為 Serializable,探索 @transient 關鍵字,利用“org.apache.spark.serializer.KryoSerializer”,將我的方法編寫為函數並將所有變量更改為“vals”(以下重新 “堆棧”上的相關帖子)。

scala>  import spark.implicits._
import spark.implicits._

scala> var test = ( 1 to 3).map( _ => "just some words").toDS()
test: org.apache.spark.sql.Dataset[String] = [value: string]

scala> def f(s: String): String = {
 |   val r = s + "hi"
 |   return r
 |   }
 f: (s: String)String

 scala> var d2 =  test.map{s => f(s)}(Encoders.STRING)
 d2: org.apache.spark.sql.Dataset[String] = [value: string]

 scala> d2.take(1)
 res0: Array[String] = Array(just some wordshi)

標度>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM