如何在不转换DataFrame并访问数据集的情况下向数据集添加列?

时间:2016-11-15 11:08:23

标签: scala apache-spark

我知道使用from input select time:timestampInMilliseconds(time:dateAdd("2016-11-14 19:40:00", 5, 'minute',"yyyy-MM-dd HH:mm:ss"),'yyyy-MM-dd HH:mm:ss') as tm insert into tmp; from tmp#window.externalTime(tm,10 min) select ..... .withColumn()向Spark DataSet添加新列的方法,它返回一个DataFrame。我也知道,我们可以将生成的DataFrame转换为DataSet。

我的问题是:

  1. 如果我们仍然遵循传统的DF方法(即将列名称作为UDF输入的字符串传递),DataSet的类型安全性如何发挥作用
  2. 是否存在以“面向对象的方式”访问列(不将列名称作为字符串传递),就像我们以前使用RDD一样,用于追加新列。
  3. 如何在正常操作中访问新列,如地图,过滤器等?
  4. 例如:

    UDF

    我使用 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中(它只包含{{} 1}}& Temp)在使用a转换为DS的瞬间。

    如何访问列b

2 个答案:

答案 0 :(得分: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]

答案 1 :(得分:3)

只是为了添加@ maasg的优秀答案......

  

如果我们仍然遵循传统的DF方法(即将列名称作为UDF输入的字符串传递),DataSet的类型安全如何在这里发挥作用

让我用另一个问题回答这个问题"我们是谁?我们仍在关注......'"?如果您认为我,我不同意并仅使用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自定义代码无关,因此无法对基于DataFrame的代码进行优化,因此无法对某些数据集"好东西"进行优化

  

是否有"面向对象的方式"访问列(不将列名称作为字符串传递),就像我们以前用RDD一样,用于追加新列。

是。当然。用例类定义数据集的模式并使用字段。两者都可以访问和添加(@maasg很好地响应了所以我不会在这里重复他的话。)

  

如何在正常操作中访问新列,例如map,filter等?

易......一次。使用描述数据集(模式)的案例类。你如何添加一个新的"东西"到现有的对象?除非以某种方式接受了新列,否则你不能这样做吗?

In""面向对象的方式"访问列或附加新列。"如果您的列是案例类的属性,则不能说"这是一个描述数据的类,同时说这是一个可能具有新属性的类"。它在OOP / FP中是不可能的,是吗?

这就是为什么添加新列可以归结为使用其他案例类或使用withColumn。那有什么不对?我认为......简直......没有错。