Spark GroupBy同时保持架构无效

时间:2018-03-27 06:57:12

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

我有一个包含多个JSON对象的文件,其中包含以下架构:

{A: struct, B: struct, C: struct, D: struct}

使用A的值永远不为空的属性;但是,B, C,D中只有一个也可以是非空的。例如,我们会在数据框中看到类似的内容:

+----+----+----+----+
| A  | B  | C  | D  |
+----+----+----+----+
|[..]|[..]|null|null|
|[..]|null|[..]|null|
|[..]|null|null|[..]|
+----+----+----+----+

我尝试按A对数据框进行分组,同时保持(A,B,C,D)的相同模式/列结构,使得给定行中的所有值都为非空。

A和任何B,C,D之间可能存在多对一关系,在这种情况下,我会将架构更改为

{A: struct, B: list, C: list, D: list},但仍保留列名称。

我对Spark和Scala相当新,并且只能以程序的方式构建我的想法,我遍历每一行并在A上散列,然后以这种方式构建一个完整的行,但是我&#39 ;我不相信它是一个干净的解决方案,我也无法使用spark API有效地表达它。

2 个答案:

答案 0 :(得分:0)

评论部分有点笨拙,所以这里有一个如何做到这一点的例子

scala> case class Foo(a:String, b:String, c:String)
defined class Foo

scala> val ds = spark.createDataset(List(Foo("1","1",null), Foo("1",null,null), Foo("1","3",null), Foo("1", null, null)))
ds: org.apache.spark.sql.Dataset[Foo] = [a: string, b: string ... 1 more field]

scala> val collected = ds.groupBy(ds("a")).agg(collect_list(ds("b")).alias("b"), collect_list(ds("c")).alias("c"))
collected: org.apache.spark.sql.DataFrame = [a: string, b: array<string> ... 1 more field]

scala> val filtered = collected.where(size(collected("b")) > 0 and size(collected("c")) > 0)
filtered: org.apache.spark.sql.Dataset[org.apache.spark.sql.Row] = [a: string, b: array<string> ... 1 more field]

scala> collected.show
+---+------+---+
|  a|     b|  c|
+---+------+---+
|  1|[1, 3]| []|
+---+------+---+


scala> filtered.show
+---+---+---+
|  a|  b|  c|
+---+---+---+
+---+---+---+

答案 1 :(得分:0)

val df = spark.createDataFrame(
     sc.parallelize(Seq(Row(1, 2, 3, 4), Row(1, 3, 4, null), Row(2, null, 4, null), Row(2, 2, 2, null))),
     StructType(Seq("A","B","C","D").map(StructField(_, IntegerType, true )))
)

df.show()
+---+----+---+----+
|  A|   B|  C|   D|
+---+----+---+----+
|  1|   2|  3|   4|
|  1|   3|  4|null|
|  2|null|  4|null|
|  2|   2|  2|null|
+---+----+---+----+

df
    .groupBy("A")
    .agg(collect_list('B) as "B", collect_list('C) as "C",collect_list('D) as "D")
    .show

+---+------+------+---+
|  A|     B|     C|  D|
+---+------+------+---+
|  1|[2, 3]|[3, 4]|[4]|
|  2|   [2]|[4, 2]| []|
+---+------+------+---+

默认情况下,collect_list不会收集空值,这正是您想要的(如果所有值都为null,您将获得一个空列表)。使用collect_set可以避免重复。