我有一个包含列col1,col2,col3的嵌套框架。col1和col2是字符串。col3是下面定义的Map[String,String]
|-- col3: map (nullable = true)
| |-- key: string
| |-- value: string (valueContainsNull = true)
我已经按col1,col2分组,并使用collect_list聚合以获得Map数组并存储在col4中。
df.groupBy($"col1", $"col2").agg(collect_list($"col3").as("col4"))
|-- col4: array (nullable = true)
| |-- element: map (containsNull = true)
| | |-- key: string
| | |-- value: string (valueContainsNull = true)
然而,我想得到col4作为一个单一的Map与所有的Map相结合。目前我有:
[[a->a1,b->b1],[c->c1]]
预期输出
[a->a1,b->b1,c->c1]
使用udf是理想的吗?
任何帮助都是感激不尽的。谢谢.
3条答案
按热度按时间yrwegjxp1#
你可以使用aggregate和map_concat:
使用
map_concat
,我们通过aggregate
内置函数连接 data 列的所有Map
项,该函数允许我们将聚合应用于列表的对。如果你想避免这种情况,你也可以选择一个UDF:
1.在Spark 3.3.0中,上面的代码不起作用,并抛出以下异常:
似乎map()被初始化为map<null,null>,而map<string,string>是预期的。
要解决这个问题,只需使用
cast(map() as map<string, string>)
显式地将map()
转换为map<string, string>
。下面是更新后的代码:
1.关于相同的密钥错误,这似乎在最新版本中得到了修复。如果你尝试添加相同的键,会抛出异常:
5lhxktic2#
你可以在没有UDF的情况下实现它。让我们创建你的框架:
产出:
如果你的数组包含2个元素,就使用
map_concat
:或者这个(我不知道如何动态地从0循环到'值数组类型列大小,这可能是最短的解决方案)
否则,如果您的数组包含多个map,并且大小未知,您可以尝试以下方法:
产出:
bttbmeg03#
如果记录的数量较少,那么您可以将它们分解并收集为struct(),然后再次使用map_from_entries
注意,当我们在group-by中使用“value”时,spark抛出
org.apache.spark.sql.AnalysisException: expression t4.value cannot be used as a grouping expression because its data type array<map<string,string>> is not an orderable data type.
。让我们采取abiratsis样本数据。这里我们必须在group-by中使用id列,否则所有的map元素将合并在一起。