簡體   English   中英

如何最有效地將Scala DataFrame的Row轉換為case類?

[英]How to convert Row of a Scala DataFrame into case class most efficiently?

一旦我在Spark中獲得了一些Row類,無論是Dataframe還是Catalyst,我想將它轉換為我的代碼中的case類。 這可以通過匹配來完成

someRow match {case Row(a:Long,b:String,c:Double) => myCaseClass(a,b,c)}

但是當行有大量的列,比如十幾個雙打,一些布爾,甚至偶爾的空位時,它變得丑陋。

我想能夠將-all-cast轉換為myCaseClass。 是否可能,或者我已經獲得了最經濟的語法?

DataFrame只是Dataset [Row]的類型別名。 與強類型Scala / Java數據集一起提供的“類型轉換”相比,這些操作也稱為“無類型轉換”。

從數據集[Row]到Dataset [Person]的轉換在spark中非常簡單

val DFtoProcess = SQLContext.sql("SELECT * FROM peoples WHERE name='test'")

此時,Spark將您的數據轉換為DataFrame = Dataset [Row],這是一個通用Row對象的集合,因為它不知道確切的類型。

// Create an Encoders for Java class (In my eg. Person is a JAVA class)
// For scala case class you can pass Person without .class reference
val personEncoder = Encoders.bean(Person.class) 

val DStoProcess = DFtoProcess.as[Person](personEncoder)

現在,Spark轉換Dataset[Row] -> Dataset[Person]類型特定的Scala / Java JVM對象,由Person類指定。

有關詳細信息,請參閱databricks提供的以下鏈接

https://databricks.com/blog/2016/07/14/a-tale-of-three-apache-spark-apis-rdds-dataframes-and-datasets.html

據我所知,你不能將一行寫入一個案例類,但我有時會選擇直接訪問行字段,比如

map(row => myCaseClass(row.getLong(0), row.getString(1), row.getDouble(2))

我發現這更容易,特別是如果case類構造函數只需要行中的某些字段。

scala> import spark.implicits._    
scala> val df = Seq((1, "james"), (2, "tony")).toDF("id", "name")
df: org.apache.spark.sql.DataFrame = [id: int, name: string]

scala> case class Student(id: Int, name: String)
defined class Student

scala> df.as[Student].collectAsList
res6: java.util.List[Student] = [Student(1,james), Student(2,tony)]

spark.implicits._spark是你的SparkSession 如果您在REPL中,則會話已定義為spark否則您需要相應地調整名稱以對應SparkSession

當然,您可以將Row對象匹配到案例類中。 假設您的SchemaType有很多字段,並且您希望將它們中的一些匹配到您的案例類中。 如果您沒有空字段,則可以執行以下操作:

case class MyClass(a: Long, b: String, c: Int, d: String, e: String)

dataframe.map {
  case Row(a: java.math.BigDecimal, 
    b: String, 
    c: Int, 
    _: String,
    _: java.sql.Date, 
    e: java.sql.Date,
    _: java.sql.Timestamp, 
    _: java.sql.Timestamp, 
    _: java.math.BigDecimal, 
    _: String) => MyClass(a = a.longValue(), b = b, c = c, d = d.toString, e = e.toString)
}

在空值的情況下,此方法將失敗,並且還要求您明確定義每個單個字段的類型。 如果必須處理空值,則需要通過執行操作丟棄包含空值的所有行

dataframe.na.drop()

即使空字段不是您的案例類的模式匹配中使用的空字段,也會丟棄記錄。 或者,如果要處理它,可以將Row對象轉換為List,然后使用選項模式:

case class MyClass(a: Long, b: String, c: Option[Int], d: String, e: String)

dataframe.map(_.toSeq.toList match {
  case List(a: java.math.BigDecimal, 
    b: String, 
    c: Int, 
    _: String,
    _: java.sql.Date, 
    e: java.sql.Date,
    _: java.sql.Timestamp, 
    _: java.sql.Timestamp, 
    _: java.math.BigDecimal, 
    _: String) => MyClass(
      a = a.longValue(), b = b, c = Option(c), d = d.toString, e = e.toString)
}

檢查這個github項目Sparkz(),它將很快引入很多庫來簡化Spark和DataFrame API,並使它們更具功能性。

暫無
暫無

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

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