Scala 如何在不从数据帧转换并访问列的情况下将列添加到数据集?
我知道使用Scala 如何在不从数据帧转换并访问列的情况下将列添加到数据集?,scala,apache-spark,Scala,Apache Spark,我知道使用.withColumn()和返回数据帧的UDF向Spark数据集添加新列的方法。我还知道,我们可以将结果数据帧转换为数据集 我的问题是: 如果我们仍然遵循传统的DF方法(即将列名作为字符串传递给UDF的输入),那么DataSet的类型安全性在这里是如何发挥作用的 是否有一种“面向对象的方式”来访问列(不以字符串形式传递列名),就像我们使用RDD那样,用于附加一个新列 如何在映射、筛选等正常操作中访问新列 例如: scala> case class Temp(a : Int
.withColumn()
和返回数据帧的UDF向Spark数据集添加新列的方法。我还知道,我们可以将结果数据帧转换为数据集
我的问题是:
如果我们仍然遵循传统的DF方法(即将列名作为字符串传递给UDF的输入),那么DataSet的类型安全性在这里是如何发挥作用的
是否有一种“面向对象的方式”来访问列(不以字符串形式传递列名),就像我们使用RDD那样,用于附加一个新列
如何在映射、筛选等正常操作中访问新列
例如:
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
我使用.withColumn()
添加的新列c
,不可访问,因为列c
不在case类Temp
中(它只包含a
和b
),当它使用res5.as[Temp]
转换为DS时
如何访问列c
?在数据集的类型安全世界中,您可以将一个结构映射到另一个结构
也就是说,对于每个转换,我们都需要数据的模式表示(正如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]
只是为了补充@maasg的优秀答案
如果我们仍然遵循传统的DF方法(即将列名作为字符串传递给UDF的输入),那么DataSet的类型安全性在这里是如何发挥作用的
让我用另一个问题来回答这个问题:“我们在‘我们仍在追随……’中是谁?”?如果你认为我是对的,我不同意,只有在我懒得创建case类来描述要使用的数据集时才使用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以上是因为我注册了一个自定义项)
您应该始终使用标准函数,因为它们可以优化。Spark可以控制您的操作,从而优化您的查询
您还应该使用数据集(而不是Dataset[Row]
,即DataFrame
),因为它们允许您访问字段的类型安全访问
(然而,一些数据集“好东西”也无法优化,因为数据集编程都是关于Scala自定义代码的,Spark无法像基于数据帧的代码那样优化)
是否有一种“面向对象的方式”来访问列(不以字符串形式传递列名),就像我们使用RDD那样,用于附加一个新列
对。当然用例类定义数据集的模式并使用字段。访问和添加(这是@maasg很好的回应,所以我不想在这里重复他的话)
如何在映射、筛选等正常操作中访问新列
再简单一次。使用描述数据集(架构)的case类。如何向现有对象添加新的“某物”?除非它以某种方式接受了一个新的专栏,否则你不能,不是吗
在访问列或追加新列的“面向对象方式”中,“如果列是case类的属性,则不能说“这是一个描述数据的类,同时说这是一个可能具有新属性的类”。在OOP/FP中是不可能的,是吗
这就是为什么添加一个新列最终要使用另一个case类或使用with column
。怎么了?我认为这是……简单地说……没什么错。有没有其他方法可以添加一个列而不传递字符串?@vdepString
只是一个遵循问题脉络的示例。不,我的意思是我们可以在这里不传递列名b
作为字符串:df.withColumn(“c”,df(“b”))
@vdep您能想象一个用于此的API吗?如果你负责数据集的API,你会怎么做?我很好奇为什么你这么需要让它与众不同。@JacekLaskowski举个例子,如果我能像这样使用它:df.withColumn(“c”,df.b)
而不是df.withColumn(“c”,df(“b”)
,我将能够在编译阶段捕捉到错误,比如拼写错误的列名或访问不存在的列等,而不是在编译阶段运行时的“column not found exception”(列未找到异常)。这只是我的拙见,不是主要的需求或要求。