【发布时间】:2018-02-10 15:26:18
【问题描述】:
我正在尝试使用以下代码将包含 List[Annotation] 的列添加到 Spark DataFrame(我已重新格式化所有内容,因此可以通过直接复制和粘贴来重现)。
import org.apache.spark.sql.{Row, SparkSession}
import org.apache.spark.sql.types._
case class Annotation(
field1: String,
field2: String,
field3: Int,
field4: Float,
field5: Int,
field6: List[Mapping]
)
case class Mapping(
fieldA: String,
fieldB: String,
fieldC: String,
fieldD: String,
fieldE: String
)
object StructTest {
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder().master("local[*]").getOrCreate()
import spark.implicits._
val annotationStruct =
StructType(
Array(
StructField("field1", StringType, nullable = true),
StructField("field2", StringType, nullable = true),
StructField("field3", IntegerType, nullable = false),
StructField("field4", FloatType, nullable = false),
StructField("field5", IntegerType, nullable = false),
StructField(
"field6",
ArrayType(
StructType(Array(
StructField("fieldA", StringType, nullable = true),
StructField("fieldB", StringType, nullable = true),
StructField("fieldC", StringType, nullable = true),
StructField("fieldD", StringType, nullable = true),
StructField("fieldE", StringType, nullable = true)
))),
nullable = true
)
)
)
val df = List(1).toDF
val annotation = Annotation("1", "2", 1, .5f, 1, List(Mapping("a", "b", "c", "d", "e")))
val schema = df.schema
val newSchema = schema.add("annotations", ArrayType(annotationStruct), false)
val rdd = df.rdd.map(x => Row.fromSeq(x.toSeq :+ List(annotation)))
val newDF = spark.createDataFrame(rdd, newSchema)
newDF.printSchema
newDF.show
}
}
但是,运行此代码时出现错误。
Caused by: java.lang.RuntimeException: Annotation is not a valid external type for schema of struct<field1:string,field2:string,field3:int,field4:float,field5:int,field6:array<struct<fieldA:string,fieldB:string,fieldC:string,fieldD:string,fieldE:string>>>
使用 createDataFrame 创建数据帧时,我传入的架构 (ArrayType(annotationStruct)) 的格式似乎不正确,但它似乎与仅包含 List[Annotation] 的数据帧的架构匹配。
编辑:使用简单类型而不是案例类以这种方式修改 DF 模式的示例。
val df = List(1).toDF
spark.createDataFrame(df.rdd.map(x => Row.fromSeq(x.toSeq :+ "moose")), df.schema.add("moose", StringType, false)).show
+-----+-----+
|value|moose|
+-----+-----+
| 1|moose|
+-----+-----+
编辑 2:我对此进行了更多分析。遗憾的是,我没有直接从案例类创建 DataFrame 的选项,这就是我尝试使用 ScalaReflection 将其镜像为 Struct 的原因。在这种情况下,我不会更改以前的模式,只是尝试从包含我的案例类列表的 RDD 中创建一个 DataFrame。 Spark 在 1.6 中存在一个问题,该问题会影响解析可能为空或 null 的结构数组 - 我想知道这些是否是链接的。
val spark = SparkSession.builder().master("local[*]").getOrCreate()
val annotationSchema = ScalaReflection.schemaFor[Annotation].dataType.asInstanceOf[StructType]
val annotation = Annotation("1", "2", 1, .5, 1, List(Mapping("a", "b", "c", "d", "e")))
val testRDD = spark.sparkContext.parallelize(List(List(annotation))).map(x => Row(x))
val testSchema = StructType(
Array(StructField("annotations", ArrayType(annotationSchema), false)
))
spark.createDataFrame(testRDD, testSchema).show
【问题讨论】:
-
很可能与架构无关。
seq(notesInd).toString显然是罪魁祸首。不幸的是缺少minimal reproducible example / reproducible example ⇒ 投票结束。 -
嗯,在没有生成任何注释的情况下尝试了它,而只是创建了一个
Annotation类的实例并以类似的方式将其附加到 DataFrame 中。收到同样的错误,表明它可能不是地图/地图分区内的任何东西,而是架构本身。 -
重新格式化所有内容,以便无需任何额外代码即可复制。
-
这是一个完全不同的问题,与上一个问题无关(也没有解决上一个问题)。您不能混合外部和内部表示。如果要使用
Row,所有struct值都必须是Row。否则 - 所有值都必须用外部类型表示(struct的Product类型)。 -
不幸的是,这正是我之前遇到的问题。在此之前,我有一个
generateAnnotations函数,它产生了List[Annotation]。然后我尝试通过获取它的模式并使用annotationStruct修改它来将它附加到原始DataFrame。我已经单独验证seq(notesInd).toString没有导致问题,所以我删除了所有分散注意力的代码,只留下了我遇到的问题核心的示例。你能通过混合表示来扩展你的意思吗?当然可以从List[Annotation]类型的 RDD 创建 DataFrame。
标签: scala apache-spark apache-spark-sql