2016-11-15 19 views
4

我知道使用.withColumn()UDF向Spark DataSet添加新列的方法,它返回一個DataFrame。我也知道,我們可以將生成的DataFrame轉換爲DataSet。如何將數據列添加到數據集而不從DataFrame轉換並訪問它?

我的問題是:

  1. 如何DataSet的類型安全進場這裏,如果我們依然遵循着傳統的DF方法(即通過列名作爲UDF的輸入字符串)
  2. 是否有「像面向對象的方式」訪問列(不需要像字符串那樣傳遞列名),就像我們以前用RDD做的那樣,用於追加一個新列。
  3. 如何訪問像地圖,過濾器等正常操作中的新列?

例如:

scala> case class Temp(a : Int, b : String) //creating case class 
    scala> val df = Seq((1,"1str"),(2,"2str),(3,"3str")).toDS // creating DS 
    scala> val appendUDF = udf((b : String) => b + "ing")  // sample UDF 

    scala> df.withColumn("c",df("b")) // adding a new column 
    res5: org.apache.spark.sql.DataFrame = [a: int, b: string ... 1 more field] 

    scala> res5.as[Temp] // converting to DS 
    res6: org.apache.spark.sql.Dataset[Temp] = [a: int, b: string ... 1 more field] 

    scala> res6.map(x =>x. 
    // list of autosuggestion : 
    a canEqual equals  productArity  productIterator toString 
    b copy  hashCode productElement productPrefix 

新列c,即我已經使用.withColumn()是無法訪問的添加,由於柱c不在的情況下類Temp(它僅包含a & b)在使用res5.as[Temp]將其轉換爲DS時。

如何訪問列c

回答

6

在類型安全的世界Dataset s中,您可以將結構映射到另一個結構中。

也就是說,對於每次轉換,我們都需要數據的模式表示(因爲RDD需要它)。要訪問上面的'c',我們需要創建一個新的模式來提供對它的訪問。

case class A(a:String) 
case class BC(b:String, c:String) 
val f:A => BC = a=> BC(a.a,"c") // Transforms an A into a BC 

val data = (1 to 10).map(i => A(i.toString)) 
val dsa = spark.createDataset(data) 
// dsa: org.apache.spark.sql.Dataset[A] = [a: string] 

val dsb = dsa.map(f) 
//dsb: org.apache.spark.sql.Dataset[BC] = [b: string, c: string] 
+0

有沒有其他的方式來添加一列而不傳遞字符串? – vdep

+0

@vdep''字符串'只是一個遵循問題脈絡的例子。 – maasg

+0

不,我的意思是,我們可以做到這一點,而不需要在這裏傳遞列名'b'作爲字符串:'df.withColumn(「c」,df(「b」))' – vdep

3

只需添加到@ maasg的出色答卷......

如何DataSet的類型安全進場這裏,如果我們依然遵循着傳統的DF方法(即通過列名作爲字符串對於UDF的輸入)

讓我用另一個問題回答這個問題:「我們誰在'我們還在...'」?如果你認爲我,我不同意並且只是在我懶得創建一個案例類來描述要使用的數據集時才使用DataFrames。

我對UDF的回答是遠離UDF,除非它們非常簡單,並且Spark Optimizer無法優化。是的,我確實相信UDF很容易定義和使用,我自己被太多的時間用來(超過)使用它們。 Spark SQL 2.0中提供了大約239個函數,您可以認真思考一個沒有UDF但標準函數的解決方案。

scala> spark.version 
res0: String = 2.1.0-SNAPSHOT 

scala> spark.catalog.listFunctions.count 
res1: Long = 240 

(240以上是因爲我註冊了一個UDF)。

您應該始終使用標準功能,因爲它們可以進行優化。 Spark可以控制你在做什麼,從而優化你的查詢。

您還應該使用數據集(而不是Dataset[Row]DataFrame),因爲它們使您可以訪問字段的類型安全訪問。因爲數據集編程都是關於Scala自定義代碼,Spark無法像基於DataFrame的代碼那樣優化,所以還是無法優化一些數據集「好東西」。

是否存在像面向RDD那樣訪問列的「面向對象的方式」(不像列名稱那樣傳遞字符串),用於追加新列。

是的。當然。用例類定義數據集的模式並使用該字段。無論是訪問還是添加(這就是@maasg很好的迴應,所以我不會在這裏重複他的話)。

如何訪問正常操作中的新列如地圖,過濾器等?

簡單...再次。使用描述數據集(的模式)的案例類。你如何添加一個新的「東西」到現有的對象?你不能不知道已經接受了一個新的專欄,不是嗎?

在訪問列或附加新列的「」面向對象的方式「。如果您的列是案例類的屬性,則不能說「這是描述數據的類,同時說這是一個可能具有新屬性的類」。這在OOP/FP中是不可能的,是嗎?

這就是爲什麼添加一個新列可歸結爲使用另一個案例類或使用withColumn。那有什麼問題?我認爲......簡單地......沒有任何問題。

相關問題