Apache spark 在spark中访问嵌套数据

Apache spark 在spark中访问嵌套数据,apache-spark,dataframe,apache-spark-sql,Apache Spark,Dataframe,Apache Spark Sql,我有一个嵌套案例类的集合。我有一个任务,它使用这些case类生成一个数据集,并将输出写入parquet 我非常恼火地发现,我必须手动进行大量的修改,才能加载这些数据并将其转换回案例类,以便在后续作业中使用。不管怎样,这就是我现在要做的 我的案例类如下: case class Person(userId: String, tech: Option[Tech]) case class Tech(browsers: Seq[Browser], platforms: Seq[Platform]) cas

我有一个嵌套案例类的集合。我有一个任务,它使用这些case类生成一个数据集,并将输出写入parquet

我非常恼火地发现,我必须手动进行大量的修改,才能加载这些数据并将其转换回案例类,以便在后续作业中使用。不管怎样,这就是我现在要做的

我的案例类如下:

case class Person(userId: String, tech: Option[Tech])
case class Tech(browsers: Seq[Browser], platforms: Seq[Platform])
case class Browser(family: String, version: Int)
所以我正在加载我的拼花数据。我可以通过以下方式获得
tech
数据作为

val df = sqlContext.load("part-r-00716.gz.parquet")
val x = df.head
val tech = x.getStruct(x.fieldIndex("tech"))
但现在我找不到如何在浏览器上进行实际迭代。如果我尝试
val browsers=tech.getStruct(tech.fieldIndex(“浏览器”))
我会得到一个异常:

java.lang.ClassCastException: scala.collection.mutable.WrappedArray$ofRef cannot be cast to org.apache.spark.sql.Row
如何使用spark 1.5.2迭代嵌套的浏览器数据

更新 事实上,我的案例类包含可选值,因此
Browser
实际上是:

case class Browser(family: String,
               major: Option[String] = None, 
               minor: Option[String] = None,
               patch: Option[String] = None, 
               language: String,
               timesSeen: Long = 1,
               firstSeenAt: Long,
               lastSeenAt: Long)
对于
操作系统
,我也有类似的功能:

case class Os(family: String,
          major: Option[String] = None,
          minor: Option[String] = None,
          patch: Option[String] = None,
          patchMinor: Option[String],
          override val timesSeen: Long = 1,
          override val firstSeenAt: Long,
          override val lastSeenAt: Long)
所以
Tech
实际上是:

case class Technographic(browsers: Seq[Browser], 
                     devices: Seq[Device],
                     oss: Seq[Os])
现在,考虑到某些值是可选的,我需要一个解决方案,使我能够正确地重构案例类。当前解决方案不支持
None
值,因此,例如,给定输入数据:

Tech(browsers=Seq(
    Browser(family=Some("IE"), major=Some(7), language=Some("en"), timesSeen=3),
    Browser(family=None, major=None, language=Some("en-us"), timesSeen=1),
    Browser(family=Some("Firefox), major=None, language=None, timesSeen=1)
  )
)
我需要它加载数据,如下所示:

family=IE, major=7, language=en, timesSeen=3,
family=None, major=None, language=en-us, timesSeen=1,
family=Firefox, major=None, language=None, timesSeen=1
由于当前解决方案不支持
None
值,因此实际上每个列表项都有任意数量的值,即:

browsers.family = ["IE", "Firefox"]
browsers.major = [7]
browsers.language = ["en", "en-us"]
timesSeen = [3, 1, 1]
如您所见,无法将最终数据(由spark返回)转换为生成它的case类

我怎样才能克服这种疯狂呢?

一些例子

//选择两列
df.select(“userId”、“tech.browsers”).show()
//仅选择嵌套值
df.select(“tech.browsers”).show(truncate=false)
+-------------------------+
|浏览器|
+-------------------------+
|[[Firefox,4],[Chrome,2]]|
|[[Firefox,4],[Chrome,2]]|
|[IE,25]]|
|[]                       |
|空的|
+-------------------------+
//提取族(嵌套值)
//通过这种方式,您可以迭代这些人,并获取他们的浏览器
//族值是嵌套的
df.select(“tech.browsers.family”).show()
+-----------------+
|家族|
+-----------------+
|[Firefox,Chrome]|
|[Firefox,Chrome]|
|[即]|
|               []|
|空的|
+-----------------+
//规范化族:每个族一行
//然后可以迭代所有族
//族值未嵌套,空值/null/None由explode()处理
df.select(分解(col(“tech.browsers.family”)).alias(“family”)).show()
+-------+
|家族|
+-------+
|火狐|
|铬|
|火狐|
|铬|
|即|
+-------+
根据最后一个示例:

val families=df.select(分解(col(“tech.browsers.family”))
.map(r=>r.getString(0)).distinct().collect().toList
println(家庭)
提供“普通”本地Scala列表中浏览器的唯一列表:

列表(IE、Firefox、Chrome)


如何使用别名列?我有多个其他技术案例类,它们的属性称为
family
,如果我随后调用
.getList(x.fieldIndex(“family”)
,它只会给出最后一个。此外,浏览器实际上为
系列和
版本提供了
选项。如果一个值是
数据,如果我将其压缩在一起,是否可以保证数据会对齐?
爆炸
还有什么作用?我读了API,但是没有帮助。我已经更新了答案。最后一个示例使用别名。所有示例都会在整个数据帧上迭代,因此这些示例不会只返回最后一个。至于zip,请提供输入和预期结果。explode()通过将嵌套值转换为新行来取消测试/规范化嵌套值。好的,explode可以让它工作,但我看不到如何选择整个列列表-我似乎只能使用
explode
选择单个列。如何选择所有列,分解数组元素?explode()不能处理多个列:它只取消对一列的检测。问题的第一个版本是关于“在浏览器上迭代”(=跳过person和tech),现在改为“将所有内容转换为case类”。请将问题回滚,因为此答案与问题不再匹配。然后打开另一个关于“转换回案例类”的问题。当然,因为我期待一个不同的解决方案。不管怎样,我创造了