无法在运行时成功从RDD转换为数据集

时间:2018-04-29 11:08:44

标签: apache-spark apache-spark-sql

我正在尝试运行这个小型Spark程序。 Spark Version 2.1.1

val rdd = sc.parallelize(List((2012, "Tesla", "S"), (1997, "Ford", "E350"), (2015, "Chevy", "Volt")))
    import spark.implicits._
    val carDetails: Dataset[CarDetails] = spark.createDataset(rdd).as[CarDetails] // Error Line
    carDetails.map(car => {
      val name = if (car.name == "Tesla") "S" else car.name
      CarDetails(car.year, name, car.model)
    }).collect().foreach(print)

在这一行上抛出错误:

val carDetails: Dataset[CarDetails] = spark.createDataset(rdd).as[CarDetails] 

Exception in thread "main" org.apache.spark.sql.AnalysisException: cannot resolve '`year`' given input columns: [_1, _2, _3];

没有编译错误!

我试过做很多改动,比如使用List而不是RDD。此外,尝试先转换为DS,然后转换为as[CarDetails],但无法正常工作。现在我一无所知。

为什么在我已经给出案例类

时将列作为_1_2_3
case class CarDetails(year: Int, name: String, model: String)

我尝试在案例课程中从Int更改为Long。它仍然无效。

修改

我在提到可能的重复问题之后改变了这一行,并且它有效。

val carDetails: Dataset[CarDetails] = spark.createDataset(rdd)
    .withColumnRenamed("_1","year")
    .withColumnRenamed("_2","name")
    .withColumnRenamed("_3","model")
        .as[CarDetails]

但是,即使在显式映射到案例类之后,我仍然不清楚为什么我需要重命名列。

1 个答案:

答案 0 :(得分:1)

as

中详细说明了Tuple*转换的规则
  

用于映射列的方法取决于U的类型:

     
      
  • 当U是一个类时,该类的字段将映射到同名的列(区分大小写由spark.sql.caseSensitive决定)。
  •   
  • 当U是元组时,列将按顺序映射(即第一列将分配给_1)。
  •   
  • 当U是基本类型(即String,Int等)时,将使用DataFrame的第一列。
  •   
     

如果数据集的架构与所需的U类型不匹配,您可以使用select with alias或者根据需要重新排列或重命名。

用代码解释这个。从案例类到Seq(CarDetails(2012, "Tesla", "S")).toDF.as[(Int, String, String)] 的转换有效(字段在结构上匹配):

Tuple*

但是从Seq((2012, "Tesla", "S")).toDF("year", "name", "model").as[CarDetails] 到任意案例类的转换不是(字段按名称匹配)。您必须先重命名字段(同上):

Tuple

它具有非常有趣的实际意义:

  • case class CarDetailsWithColor( year: Int, name: String, model: String, color: String) Seq( CarDetailsWithColor(2012, "Tesla", "S", "red") ).toDF.as[(Int, String, String)] // org.apache.spark.sql.AnalysisException: Try to map struct<year:int,name:string,model:string,color:string> to Tuple3, but failed as the number of fields does not line up.; 类型对象不能包含无关字段:

    Dataset
  • 虽然案例类型Seq( (2012, "Tesla", "S", "red") ).toDF("year", "name", "model", "color").as[CarDetails] 可以:

    sc.parallelize(Seq(CarDetails(2012, "Tesla", "S"))).toDS
    

当然,从case class typed variant开始可以省去所有麻烦:

{{1}}