Scala noob在这里。仍在尝试学习语法。
我正在尝试减少将测试数据转换为DataFrame所必须编写的代码。这是我现在所拥有的:
def makeDf[T](seq: Seq[(Int, Int)], colNames: String*): Dataset[Row] = {
val context = session.sqlContext
import context.implicits._
seq.toDF(colNames: _*)
}
问题在于上述方法仅采用形状为Seq[(Int, Int)]
的序列作为输入。如何使它以任何顺序作为输入?我可以将输入形状更改为Seq[AnyRef]
,但是代码无法将toDF
调用识别为有效符号。
我不知道如何进行这项工作。有任何想法吗?谢谢!
答案 0 :(得分:1)
简短回答:
import scala.reflect.runtime.universe.TypeTag
def makeDf[T <: Product: TypeTag](seq: Seq[T], colNames: String*): DataFrame = ...
说明:
在调用seq.toDF时,您实际上使用的是SQLImplicits中定义的隐式:
implicit def localSeqToDatasetHolder[T : Encoder](s: Seq[T]): DatasetHolder[T] = {
DatasetHolder(_sqlContext.createDataset(s))
}
则需要生成编码器。问题是编码器仅在某些类型上定义。特别是乘积(即元组,案例类等),还需要隐式添加TypeTag,以便Scala可以克服类型擦除(在运行时,所有Sequence都具有类型序列,而与泛型类型无关。TypeTag提供了有关此信息)
作为副节点,您无需从会话中提取sqlcontext,只需使用:
import sparkSession.implicits._
答案 1 :(得分:0)
@AssafMendelson已经解释了为什么无法创建Dataset
的{{1}}的真正原因是因为 Spark 需要Any
来转换对象 从它们 JVM 表示到其内部表示-和 Spark 不能保证生成此类对象Encoder
用于 Encoder
类型。
Assaf的答案是正确的,并且可以使用。
但是,恕我直言,它有太多的限制,因为它仅适用于Any
(元组和案例类)-即使包括大多数用例,仍然排除了一些用例
由于您真正需要的是Products
,因此您可以将这一责任留给客户。在大多数情况下,只需调用Encoder
即可使它们进入范围。
因此,我认为这将是最通用的解决方案。
import spark.implicits._
注意:这基本上是从Spark重新发明已经定义的功能。