Scala:如何将任何通用序列用作此方法的输入

时间:2019-01-01 09:10:27

标签: scala apache-spark dataframe apache-spark-sql

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调用识别为有效符号。

我不知道如何进行这项工作。有任何想法吗?谢谢!

2 个答案:

答案 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重新发明已经定义的功能。