我的目标是爆炸(即从结构内部获取它们,并将它们公开为数据集的其余列)一个Spark结构列(已完成),但通过在任意字符串前添加内部字段名称来进行更改。动机之一是我的结构可以包含与其外部名称相同的列-因此,我需要一种轻松区分它们的方法。当然,我事先不知道结构中的列是什么。
这是我到目前为止所拥有的:
implicit class Implicit(df: DataFrame) {
def explodeStruct(column: String) = df.select("*", column + ".*").drop(column)
}
这可以正常工作-我使用这段文字:
df.explodeStruct("myColumn")
它返回原始数据帧中的所有列,最后返回结构的内部列。
至于前缀,我的想法是获取该列并找出其内部列。我浏览了文档,但在Column类上找不到任何方法可以做到这一点。然后,我改变了方法以采用DataFrame的架构,然后按列名过滤结果,并从结果数组中提取找到的列。问题是我发现该元素的类型为StructField-再次不提供提取其内部字段的选项-而我真正想要的是处理StructType元素-该元素具有.getFields
方法,这正是我想要的(即,向我显示内部列的名称,以便我可以对其进行迭代并在我的选择上使用它们,并在它们之前添加所需的前缀)。我不知道将StructField转换为StructType的方法。
我的最后一次尝试是解析StructField.toString的输出-该输出包含内部列的所有名称和类型,尽管这听起来确实很脏,但我宁愿避免这种卑鄙的方法。
对这个问题有什么优雅的解决方案吗?
答案 0 :(得分:1)
好吧,在再次阅读我自己的问题之后,我找到了解决该问题的优雅方法-我只需要按照自己的方式选择所有列,然后将其与原始数据框进行比较以找出问题所在什么是新列。这是最终结果-我也这样做是为了使分解的列显示在与原始结构相同的位置,以免破坏信息流:
implicit class Implicit(df: DataFrame) {
def explodeStruct(column: String) = {
val prefix = column + "_"
val originalPosition = df.columns.indexOf(column)
val dfWithAllColumns = df.select("*", column + ".*")
val explodedColumns = dfWithAllColumns.columns diff df.columns
val prefixedExplodedColumns = explodedColumns.map(c => col(column + "." + c) as prefix + c)
val finalColumnsList = df.columns.map(col).patch(originalPosition, prefixedExplodedColumns, 1)
df.select(finalColumnsList: _*)
}
}
当然,您可以自定义前缀,分隔符等,但这很简单,任何人都可以调整参数等。用法保持不变。